Repository: nolanlawson/socket-pouch Branch: master Commit: 6911f9a47b41 Files: 86 Total size: 3.7 MB Directory structure: gitextract_5blrnppl/ ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin/ │ ├── dev-server.js │ ├── es3ify.js │ ├── run-test.sh │ ├── test-browser.js │ └── test-node.sh ├── client/ │ └── index.js ├── dist/ │ └── socket-pouch.client.js ├── lib/ │ ├── client/ │ │ ├── arrayBufferToBinaryString.js │ │ ├── base64.js │ │ ├── base64StringToBlobOrBuffer-browser.js │ │ ├── base64StringToBlobOrBuffer.js │ │ ├── binaryStringToArrayBuffer.js │ │ ├── binaryStringToBlobOrBuffer-browser.js │ │ ├── binaryStringToBlobOrBuffer.js │ │ ├── blob.js │ │ ├── index.js │ │ ├── readAsArrayBuffer.js │ │ ├── readAsBinaryString-browser.js │ │ ├── readAsBinaryString.js │ │ ├── typedBuffer.js │ │ └── utils.js │ ├── server/ │ │ ├── index.js │ │ ├── make-pouch-creator.js │ │ ├── safe-eval.js │ │ └── utils.js │ └── shared/ │ ├── buffer-browser.js │ ├── buffer.js │ ├── cloneBinaryObject-browser.js │ ├── cloneBinaryObject.js │ ├── errors.js │ ├── isBinaryObject-browser.js │ ├── isBinaryObject.js │ ├── parse-message.js │ ├── pouchdb-clone.js │ ├── utils.js │ └── uuid.js ├── package.json ├── server/ │ └── index.js └── test/ ├── bind-polyfill.js ├── deps/ │ └── bigimage.js ├── index.html ├── node.setup.js ├── pouchdb/ │ ├── integration/ │ │ ├── deps/ │ │ │ └── bigimage.js │ │ ├── pouchdb-for-coverage.js │ │ ├── test.aa.setup.js │ │ ├── test.all_docs.js │ │ ├── test.attachments.js │ │ ├── test.basics.js │ │ ├── test.bulk_docs.js │ │ ├── test.bulk_get.js │ │ ├── test.changes.js │ │ ├── test.compaction.js │ │ ├── test.conflicts.js │ │ ├── test.constructor.js │ │ ├── test.design_docs.js │ │ ├── test.events.js │ │ ├── test.get.js │ │ ├── test.http.js │ │ ├── test.issue1175.js │ │ ├── test.issue221.js │ │ ├── test.issue3179.js │ │ ├── test.issue3646.js │ │ ├── test.local_docs.js │ │ ├── test.replication.js │ │ ├── test.replication_events.js │ │ ├── test.reserved.js │ │ ├── test.retry.js │ │ ├── test.revs_diff.js │ │ ├── test.slash_id.js │ │ ├── test.sync.js │ │ ├── test.sync_events.js │ │ ├── test.taskqueue.js │ │ ├── test.uuids.js │ │ ├── utils-bundle.js │ │ └── utils.js │ └── mapreduce/ │ ├── test.mapreduce.js │ ├── test.persisted.js │ └── test.views.js ├── test.js └── webrunner.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules .DS_Store *~ coverage test/test-bundle.js npm-debug.log testdb* ================================================ FILE: .jshintrc ================================================ { "curly": true, "eqeqeq": true, "immed": true, "newcap": true, "noarg": true, "sub": true, "undef": true, "unused": true, "eqnull": true, "browser": true, "node": true, "strict": true, "globalstrict": true, "globals": { "eio": true}, "white": true, "indent": 2, "maxlen": 100, "predef": [ "chrome", "eio", "process", "global", "require", "console", "describe", "beforeEach", "afterEach", "it", "emit" ] } ================================================ FILE: .npmignore ================================================ .git* node_modules .DS_Store *~ coverage npm-debug.log vendor/ ================================================ FILE: .travis.yml ================================================ language: node_js services: - couchdb node_js: - "5" sudo: false addons: firefox: "41.0.1" script: npm run $COMMAND before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - "export DEBUG=pouchdb:socket" - "npm install add-cors-to-couchdb" - "./node_modules/.bin/add-cors-to-couchdb" env: matrix: - COMMAND=test - CLIENT=selenium:firefox:41.0.1 COMMAND=test branches: only: - master ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ socket-pouch [![Build Status](https://travis-ci.org/nolanlawson/socket-pouch.svg)](https://travis-ci.org/nolanlawson/socket-pouch) ===== ```js // This pouch is powered by web sockets! var db = new PouchDB('mydb', {adapter: 'socket', url: 'ws://localhost:80'}); ``` Adapter plugin that proxies all PouchDB API calls to another PouchDB running on the server in Node.js. The communication mechanism is [Engine.io](https://github.com/Automattic/engine.io), the famous core of [Socket.io](http://socket.io/). This means that instead of syncing over HTTP, socket-pouch syncs over WebSockets. Thanks to Engine.io, it falls back to XHR polling in browsers that don't support WebSockets. The socket-pouch library has two parts: * **A Node.js server**, which can create local PouchDBs or proxy to a remote CouchDB. * **A JavaScript client**, which can run in Node.js or the browser. This adapter passes [the full PouchDB test suite](https://travis-ci.org/nolanlawson/socket-pouch). It requires PouchDB 5.0.0+. Usage --- $ npm install socket-pouch #### Server ```js var socketPouchServer = require('socket-pouch/server'); socketPouchServer.listen(80); ``` #### Client ##### In the browser When you `npm install socket-pouch`, the client JS file is available at `node_modules/socket-pouch/dist/socket-pouch.client.js`. Or you can just download it from Github above. Then include it in your HTML, after PouchDB: ```html ``` Then you can create a socket-powered PouchDB using: ```js var db = new PouchDB('mydb', { adapter: 'socket', url: 'ws://localhost:80' }); ``` ##### In Node.js/Browserify The same rules apply, but you have to notify PouchDB of the new adapter: ```js var PouchDB = require('pouchdb'); PouchDB.adapter('socket', require('socket-pouch/client')); ``` API ---- ### Server ```js var socketPouchServer = require('socket-pouch/server'); socketPouchServer.listen(80, {}, function () { // server started }); ``` #### socketPouchServer.listen(port [, options] [, callback]) ##### Arguments * **port**: the port to listen on. You should probably use 80 or 443 if you plan on running this in production; most browsers are finicky about other ports. 8080 may work in Chrome during debugging. * **options**: (optional) options object * **remoteUrl**: tells socket-pouch to act as a proxy for a remote CouchDB at the given URL (rather than creating local PouchDB databases) * **pouchCreator**: alternatively, you can supply a custom function that takes a string and returns any PouchDB object however you like. (See examples below.) * **socketOptions**: (optional) options passed verbatim to Engine.io. See [their documentation](https://github.com/Automattic/engine.io/#methods) for details. * **callback**: (optional) called when the server has started Create a server which creates local PouchDBs, named by the user and placed in the current directory: ```js socketPouchServer.listen(80, {}, function () { console.log('server started!'); }); ``` Create a server which acts as a proxy to a remote CouchDB (or CouchDB-compliant database): ```js socketPouchServer.listen(80, { remoteUrl: 'http://localhost:5984' }); ``` So e.g. when the user requests a database called 'foo', it will use a remote database at `'http://localhost:5984/foo'`. Note that authentication is not handled, so you may want the `pouchCreator` option instead. Create a MemDOWN-backed PouchDB server: ```js socketPouchServer.listen(80, { pouchCreator: function (dbName) { return new PouchDB(dbName, { db: require('memdown') }); } }); ``` Note that this `dbName` is supplied by the client ver batim, meaning **it could be dangerous**. In the example above, everything is fine because MemDOWN databases can have any string as a name. Alternatively, your `pouchCreator` can return a `Promise` if you want to do something asynchronously, such as authenticating the user. In that case you must wrap the object in `{pouch: yourPouchDB}`: ```js socketPouchServer.listen(80, { pouchCreator: function (dbName) { return doSomethingAsynchronously().then(function () { return { pouch: new PouchDB('dbname') }; }); } }); ``` ### Client ```js var db = new PouchDB({ adapter: 'socket', name: 'mydb', url: 'ws://localhost:80', socketOptions: {} }); ``` The `name` and `url` are required and must point to a valid `socketPouchServer`. The `socketOptions`, if provided, are passed ver batim to Engine.io, so refer to [their documentation](https://github.com/Automattic/engine.io-client/#nodejs-with-certificates) for details. ### Replication The `db` object acts like a PouchDB that communicates remotely with the `socketPouchServer` In other words, it's analogous to a PouchDB created like `new PouchDB('http://localhost:5984/mydb')`. So you can replicate using the normal methods: ```js var localDB = new PouchDB('local'); var remoteDB = new PouchDB({adapter: 'socket', name: 'remote', url: 'ws://localhost:80'}); // replicate from local to remote localDB.replicate.to(remoteDB); // replicate from remote to local localDB.replicate.from(remoteDB); // replicate bidirectionally localDB.sync(remoteDB); ``` For details, see the official [`replicate()`](http://pouchdb.com/api.html#replication) or [`sync()`](http://pouchdb.com/api.html#sync) docs. ### Remote API ```js var remoteDB = new PouchDB({adapter: 'socket', name: 'remote', url: 'ws://localhost:80'}); ``` You can also talk to this `remoteDB` as if it were a normal PouchDB. All the standard methods like `info()`, `get()`, `put()`, and `putAttachment()` will work. The [Travis tests](https://travis-ci.org/nolanlawson/socket-pouch) run the full PouchDB test suite. ### Debugging SocketPouch uses [debug](https://github.com/visionmedia/debug) for logging. So in Node.js, you can enable debugging by setting a flag: ``` DEBUG=pouchdb:socket:* ``` In the browser, you can enable debugging by using PouchDB's logger: ```js PouchDB.debug.enable('pouchdb:socket:*'); ``` Q & A --- #### How does it communicate? SocketPouch communicates using the normal Engine.io APIs like `send()` and `on('message')`. Normally it sends JSON text data, but in the case of attachments, binary data is sent. This means that SocketPouch is actually more efficient than regular PouchDB replication, which (as of this writing) uses base64-string encoding to send attachments between the client and server. #### Does it work in a web worker or service worker? Unfortuantely, not at the moment. #### How is it implemented? This is a custom PouchDB adapter. Other examples of PouchDB adapters include the built-in IndexedDB, WebSQL, LevelDB, and HTTP (Couch) adapters, as well as a partial adapter written for [pouchdb-replication-stream](https://github.com/nolanlawson/pouchdb-replication-stream) and [worker-pouch](https://github.com/nolanlawson/worker-pouch), which is a fork of this repo. Changelog --- - 2.0.0 - Support for PouchDB 6.0.0, drop support for PouchDB <=5 - 1.0.0 - Initial release Building ---- npm install npm run build Testing ---- ### In Node This will run the tests in Node using LevelDB: npm test You can also check for 100% code coverage using: npm run coverage Run certain tests: ``` GREP=foo npm test ``` ### In the browser Run `npm run dev` and then point your favorite browser to [http://127.0.0.1:8000/test/index.html](http://127.0.0.1:8000/test/index.html). The query param `?grep=mysearch` will search for tests matching `mysearch`. ### Automated browser tests You can run e.g. CLIENT=selenium:firefox npm test CLIENT=selenium:phantomjs npm test This will run the tests automatically and the process will exit with a 0 or a 1 when it's done. Firefox uses IndexedDB, and PhantomJS uses WebSQL. ================================================ FILE: bin/dev-server.js ================================================ #!/usr/bin/env node 'use strict'; var COUCH_HOST = process.env.COUCH_HOST || 'http://127.0.0.1:5984'; var HTTP_PORT = 8000; var CORS_PORT = 2020; var SOCKET_PORT = 8080; var cors_proxy = require('corsproxy'); var Promise = require('bluebird'); var http_proxy = require('pouchdb-http-proxy'); var http_server = require("http-server"); var fs = require('fs'); var indexfile = "./test/test.js"; var dotfile = "./test/.test-bundle.js"; var outfile = "./test/test-bundle.js"; var watchify = require("watchify"); var browserify = require('browserify'); var socketPouch = require('../lib/server'); var w = watchify(browserify(indexfile, { cache: {}, packageCache: {}, fullPaths: true, debug: true })); w.on('update', bundle); bundle(); var filesWritten = false; var serverStarted = false; var socketServerStarted = false; var readyCallback; function bundle() { var wb = w.bundle(); wb.on('error', function (err) { console.error(String(err)); }); wb.on("end", end); wb.pipe(fs.createWriteStream(dotfile)); function end() { fs.rename(dotfile, outfile, function (err) { if (err) { return console.error(err); } console.log('Updated:', outfile); filesWritten = true; checkReady(); }); } } function startSocketServer() { socketPouch.listen(SOCKET_PORT, {}, function () { console.log('Socket server started'); socketServerStarted = true; checkReady(); }); } function startServers(callback) { readyCallback = callback; startSocketServer(); return new Promise(function (resolve, reject) { http_server.createServer().listen(HTTP_PORT, function (err) { if (err) { return reject(err); } cors_proxy.options = {target: COUCH_HOST}; http_proxy.createServer(cors_proxy).listen(CORS_PORT, function (err) { if (err) { return reject(err); } resolve(); }); }); }).then(function () { console.log('Tests: http://127.0.0.1:' + HTTP_PORT + '/test/index.html'); serverStarted = true; checkReady(); }).catch(function (err) { if (err) { console.log(err); process.exit(1); } }); } function checkReady() { if (filesWritten && serverStarted && socketServerStarted && readyCallback) { readyCallback(); } } if (require.main === module) { startServers(); } else { module.exports.start = startServers; } ================================================ FILE: bin/es3ify.js ================================================ #!/usr/bin/env node 'use strict'; var es3ify = require('es3ify'); return process.stdin.pipe(es3ify()).pipe(process.stdout); ================================================ FILE: bin/run-test.sh ================================================ #!/bin/bash : ${CLIENT:="node"} if [ "$CLIENT" == "node" ]; then npm run test-node else npm run test-browser fi ================================================ FILE: bin/test-browser.js ================================================ #!/usr/bin/env node 'use strict'; var wd = require('wd'); var sauceConnectLauncher = require('sauce-connect-launcher'); var selenium = require('selenium-standalone'); var querystring = require("querystring"); var devserver = require('./dev-server.js'); var testTimeout = 30 * 60 * 1000; var username = process.env.SAUCE_USERNAME; var accessKey = process.env.SAUCE_ACCESS_KEY; // process.env.CLIENT is a colon seperated list of // (saucelabs|selenium):browserName:browserVerion:platform var tmp = (process.env.CLIENT || 'selenium:firefox').split(':'); var client = { runner: tmp[0] || 'selenium', browser: tmp[1] || 'firefox', version: tmp[2] || null, // Latest platform: tmp[3] || null }; var testUrl = 'http://127.0.0.1:8000/test/index.html'; var qs = {}; var sauceClient; var sauceConnectProcess; var tunnelId = process.env.TRAVIS_JOB_NUMBER || 'tunnel-' + Date.now(); if (client.runner === 'saucelabs') { qs.saucelabs = true; } if (process.env.GREP) { qs.grep = process.env.GREP; } testUrl += '?'; testUrl += querystring.stringify(qs); if (process.env.TRAVIS && client.browser !== 'firefox' && client.browser !== 'phantomjs' && process.env.TRAVIS_SECURE_ENV_VARS === 'false') { console.error('Not running test, cannot connect to saucelabs'); process.exit(1); return; } function testError(e) { console.error(e); console.error('Doh, tests failed'); sauceClient.quit(); process.exit(3); } function postResult(result) { process.exit(!process.env.PERF && result.failed ? 1 : 0); } function testComplete(result) { console.log(result); sauceClient.quit().then(function () { if (sauceConnectProcess) { sauceConnectProcess.close(function () { postResult(result); }); } else { postResult(result); } }); } function startSelenium(callback) { // Start selenium var opts = {version: '2.45.0'}; selenium.install(opts, function(err) { if (err) { console.error('Failed to install selenium'); process.exit(1); } selenium.start(opts, function(err, server) { sauceClient = wd.promiseChainRemote(); callback(); }); }); } function startSauceConnect(callback) { var options = { username: username, accessKey: accessKey, tunnelIdentifier: tunnelId }; sauceConnectLauncher(options, function (err, process) { if (err) { console.error('Failed to connect to saucelabs'); console.error(err); return process.exit(1); } sauceConnectProcess = process; sauceClient = wd.promiseChainRemote("localhost", 4445, username, accessKey); callback(); }); } function startTest() { console.log('Starting', client); var opts = { browserName: client.browser, version: client.version, platform: client.platform, tunnelTimeout: testTimeout, name: client.browser + ' - ' + tunnelId, 'max-duration': 60 * 30, 'command-timeout': 599, 'idle-timeout': 599, 'tunnel-identifier': tunnelId }; sauceClient.init(opts).get(testUrl, function () { /* jshint evil: true */ var interval = setInterval(function () { sauceClient.eval('window.results', function (err, results) { if (err) { clearInterval(interval); testError(err); } else if (results.completed || results.failures.length) { clearInterval(interval); testComplete(results); } else { console.log('=> ', results); } }); }, 10 * 1000); }); } devserver.start(function () { if (client.runner === 'saucelabs') { startSauceConnect(startTest); } else { startSelenium(startTest); } }); ================================================ FILE: bin/test-node.sh ================================================ #!/usr/bin/env bash : ${TIMEOUT:=50000} : ${REPORTER:="spec"} node ./bin/dev-server.js & export DEV_SERVER_PID=$! sleep 10 # TODO: this fixes a weird test in test.views.js ./node_modules/.bin/rimraf tmp ./node_modules/.bin/mkdirp tmp # skip migration and defaults tests if [[ $INVERT == '1' ]]; then INVERT_ARG='--invert' else INVERT_ARG='' fi mocha \ --reporter=$REPORTER \ --timeout $TIMEOUT --bail \ --require=./test/node.setup.js \ --grep=$GREP \ $INVERT_ARG \ test/pouchdb/{integration,mapreduce}/test.*.js EXIT_STATUS=$? if [[ ! -z $DEV_SERVER_PID ]]; then kill $DEV_SERVER_PID fi exit $EXIT_STATUS ================================================ FILE: client/index.js ================================================ 'use strict'; module.exports = require('../lib/client'); ================================================ FILE: dist/socket-pouch.client.js ================================================ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.socketPouch = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0; i--) { if (str.charAt(i) === char) { return i; } } return -1; }; exports.clone = _dereq_('./pouchdb-clone'); exports.parseMessage = _dereq_('./parse-message'); /* istanbul ignore next */ exports.once = function once(fun) { var called = false; return exports.getArguments(function (args) { if (called) { console.trace(); throw new Error('once called more than once'); } else { called = true; fun.apply(this, args); } }); }; /* istanbul ignore next */ exports.getArguments = function getArguments(fun) { return function () { var len = arguments.length; var args = new Array(len); var i = -1; while (++i < len) { args[i] = arguments[i]; } return fun.call(this, args); }; }; /* istanbul ignore next */ exports.toPromise = function toPromise(func) { //create the function we will be returning return exports.getArguments(function (args) { var self = this; var tempCB = (typeof args[args.length - 1] === 'function') ? args.pop() : false; // if the last argument is a function, assume its a callback var usedCB; if (tempCB) { // if it was a callback, create a new callback which calls it, // but do so async so we don't trap any errors usedCB = function (err, resp) { process.nextTick(function () { tempCB(err, resp); }); }; } var promise = new Promise(function (fulfill, reject) { try { var callback = exports.once(function (err, mesg) { if (err) { reject(err); } else { fulfill(mesg); } }); // create a callback for this invocation // apply the function in the orig context args.push(callback); func.apply(self, args); } catch (e) { reject(e); } }); // if there is a callback, call it back if (usedCB) { promise.then(function (result) { usedCB(null, result); }, usedCB); } promise.cancel = function () { return this; }; return promise; }); }; if (typeof atob === 'function') { exports.atob = function atobShim(str) { return atob(str); }; } else { exports.atob = function atobShim(str) { var base64 = new buffer(str, 'base64'); // Node.js will just skip the characters it can't encode instead of // throwing and exception if (base64.toString('base64') !== str) { throw ("Cannot base64 encode full string"); } return base64.toString('binary'); }; } if (typeof btoa === 'function') { exports.btoa = function btoaShim(str) { return btoa(str); }; } else { exports.btoa = function btoaShim(str) { return new buffer(str, 'binary').toString('base64'); }; } exports.inherits = _dereq_('inherits'); exports.Promise = Promise; var binUtil = _dereq_('pouchdb-binary-util'); exports.createBlob = binUtil.createBlob; exports.readAsArrayBuffer = binUtil.readAsArrayBuffer; exports.readAsBinaryString = binUtil.readAsBinaryString; exports.binaryStringToArrayBuffer = binUtil.binaryStringToArrayBuffer; exports.arrayBufferToBinaryString = binUtil.arrayBufferToBinaryString; }).call(this,_dereq_('_process')) },{"./buffer":11,"./parse-message":15,"./pouchdb-clone":16,"_process":71,"inherits":48,"pouchdb-binary-util":68,"pouchdb-promise":69}],18:[function(_dereq_,module,exports){ "use strict"; // BEGIN Math.uuid.js /*! Math.uuid.js (v1.4) http://www.broofa.com mailto:robert@broofa.com Copyright (c) 2010 Robert Kieffer Dual licensed under the MIT and GPL licenses. */ /* * Generate a random uuid. * * USAGE: Math.uuid(length, radix) * length - the desired number of characters * radix - the number of allowable values for each character. * * EXAMPLES: * // No arguments - returns RFC4122, version 4 ID * >>> Math.uuid() * "92329D39-6F5C-4520-ABFC-AAB64544E172" * * // One argument - returns ID of the specified length * >>> Math.uuid(15) // 15 character ID (default base=62) * "VcydxgltxrVZSTV" * * // Two arguments - returns ID of the specified length, and radix. * // (Radix must be <= 62) * >>> Math.uuid(8, 2) // 8 character ID (base=2) * "01001010" * >>> Math.uuid(8, 10) // 8 character ID (base=10) * "47473046" * >>> Math.uuid(8, 16) // 8 character ID (base=16) * "098F4D35" */ var chars = ( '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' ).split(''); function getValue(radix) { return 0 | Math.random() * radix; } function uuid(len, radix) { radix = radix || chars.length; var out = ''; var i = -1; if (len) { // Compact form while (++i < len) { out += chars[getValue(radix)]; } return out; } // rfc4122, version 4 form // Fill in random data. At i==19 set the high bits of clock sequence as // per rfc4122, sec. 4.1.5 while (++i < 36) { switch (i) { case 8: case 13: case 18: case 23: out += '-'; break; case 19: out += chars[(getValue(16) & 0x3) | 0x8]; break; default: out += chars[getValue(16)]; } } return out; } module.exports = uuid; },{}],19:[function(_dereq_,module,exports){ module.exports = after function after(count, callback, err_cb) { var bail = false err_cb = err_cb || noop proxy.count = count return (count === 0) ? callback() : proxy function proxy(err, result) { if (proxy.count <= 0) { throw new Error('after called too many times') } --proxy.count // after first error, rest are passed to err_cb if (err) { bail = true callback(err) // future error callbacks will go to error handler callback = err_cb } else if (proxy.count === 0 && !bail) { callback(null, result) } } } function noop() {} },{}],20:[function(_dereq_,module,exports){ /** * An abstraction for slicing an arraybuffer even when * ArrayBuffer.prototype.slice is not supported * * @api public */ module.exports = function(arraybuffer, start, end) { var bytes = arraybuffer.byteLength; start = start || 0; end = end || bytes; if (arraybuffer.slice) { return arraybuffer.slice(start, end); } if (start < 0) { start += bytes; } if (end < 0) { end += bytes; } if (end > bytes) { end = bytes; } if (start >= bytes || start >= end || bytes === 0) { return new ArrayBuffer(0); } var abv = new Uint8Array(arraybuffer); var result = new Uint8Array(end - start); for (var i = start, ii = 0; i < end; i++, ii++) { result[ii] = abv[i]; } return result.buffer; }; },{}],21:[function(_dereq_,module,exports){ /* * base64-arraybuffer * https://github.com/niklasvh/base64-arraybuffer * * Copyright (c) 2012 Niklas von Hertzen * Licensed under the MIT license. */ (function(){ "use strict"; var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Use a lookup table to find the index. var lookup = new Uint8Array(256); for (var i = 0; i < chars.length; i++) { lookup[chars.charCodeAt(i)] = i; } exports.encode = function(arraybuffer) { var bytes = new Uint8Array(arraybuffer), i, len = bytes.length, base64 = ""; for (i = 0; i < len; i+=3) { base64 += chars[bytes[i] >> 2]; base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; base64 += chars[bytes[i + 2] & 63]; } if ((len % 3) === 2) { base64 = base64.substring(0, base64.length - 1) + "="; } else if (len % 3 === 1) { base64 = base64.substring(0, base64.length - 2) + "=="; } return base64; }; exports.decode = function(base64) { var bufferLength = base64.length * 0.75, len = base64.length, i, p = 0, encoded1, encoded2, encoded3, encoded4; if (base64[base64.length - 1] === "=") { bufferLength--; if (base64[base64.length - 2] === "=") { bufferLength--; } } var arraybuffer = new ArrayBuffer(bufferLength), bytes = new Uint8Array(arraybuffer); for (i = 0; i < len; i+=4) { encoded1 = lookup[base64.charCodeAt(i)]; encoded2 = lookup[base64.charCodeAt(i+1)]; encoded3 = lookup[base64.charCodeAt(i+2)]; encoded4 = lookup[base64.charCodeAt(i+3)]; bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); } return arraybuffer; }; })(); },{}],22:[function(_dereq_,module,exports){ 'use strict'; /* jshint -W079 */ var Blob = _dereq_('blob'); var Promise = _dereq_('native-or-lie'); // // PRIVATE // // From http://stackoverflow.com/questions/14967647/ (continues on next line) // encode-decode-image-with-base64-breaks-image (2013-04-21) function binaryStringToArrayBuffer(binary) { var length = binary.length; var buf = new ArrayBuffer(length); var arr = new Uint8Array(buf); var i = -1; while (++i < length) { arr[i] = binary.charCodeAt(i); } return buf; } // Can't find original post, but this is close // http://stackoverflow.com/questions/6965107/ (continues on next line) // converting-between-strings-and-arraybuffers function arrayBufferToBinaryString(buffer) { var binary = ''; var bytes = new Uint8Array(buffer); var length = bytes.byteLength; var i = -1; while (++i < length) { binary += String.fromCharCode(bytes[i]); } return binary; } // doesn't download the image more than once, because // browsers aren't dumb. uses the cache function loadImage(src, crossOrigin) { return new Promise(function (resolve, reject) { var img = new Image(); if (crossOrigin) { img.crossOrigin = crossOrigin; } img.onload = function () { resolve(img); }; img.onerror = reject; img.src = src; }); } function imgToCanvas(img) { var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; // copy the image contents to the canvas var context = canvas.getContext('2d'); context.drawImage( img, 0, 0, img.width, img.height, 0, 0, img.width, img.height); return canvas; } // // PUBLIC // /** * Shim for * [new Blob()]{@link https://developer.mozilla.org/en-US/docs/Web/API/Blob.Blob} * to support * [older browsers that use the deprecated BlobBuilder API]{@link http://caniuse.com/blob}. * * @param {Array} parts - content of the Blob * @param {Object} options - usually just {type: myContentType} * @returns {Blob} */ function createBlob(parts, options) { options = options || {}; if (typeof options === 'string') { options = {type: options}; // do you a solid here } return new Blob(parts, options); } /** * Shim for * [URL.createObjectURL()]{@link https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL} * to support browsers that only have the prefixed * webkitURL (e.g. Android <4.4). * @param {Blob} blob * @returns {string} url */ function createObjectURL(blob) { return (window.URL || window.webkitURL).createObjectURL(blob); } /** * Shim for * [URL.revokeObjectURL()]{@link https://developer.mozilla.org/en-US/docs/Web/API/URL.revokeObjectURL} * to support browsers that only have the prefixed * webkitURL (e.g. Android <4.4). * @param {string} url */ function revokeObjectURL(url) { return (window.URL || window.webkitURL).revokeObjectURL(url); } /** * Convert a Blob to a binary string. Returns a Promise. * * @param {Blob} blob * @returns {Promise} Promise that resolves with the binary string */ function blobToBinaryString(blob) { return new Promise(function (resolve, reject) { var reader = new FileReader(); var hasBinaryString = typeof reader.readAsBinaryString === 'function'; reader.onloadend = function (e) { var result = e.target.result || ''; if (hasBinaryString) { return resolve(result); } resolve(arrayBufferToBinaryString(result)); }; reader.onerror = reject; if (hasBinaryString) { reader.readAsBinaryString(blob); } else { reader.readAsArrayBuffer(blob); } }); } /** * Convert a base64-encoded string to a Blob. Returns a Promise. * @param {string} base64 * @param {string|undefined} type - the content type (optional) * @returns {Promise} Promise that resolves with the Blob */ function base64StringToBlob(base64, type) { return Promise.resolve().then(function () { var parts = [binaryStringToArrayBuffer(atob(base64))]; return type ? createBlob(parts, {type: type}) : createBlob(parts); }); } /** * Convert a binary string to a Blob. Returns a Promise. * @param {string} binary * @param {string|undefined} type - the content type (optional) * @returns {Promise} Promise that resolves with the Blob */ function binaryStringToBlob(binary, type) { return Promise.resolve().then(function () { return base64StringToBlob(btoa(binary), type); }); } /** * Convert a Blob to a binary string. Returns a Promise. * @param {Blob} blob * @returns {Promise} Promise that resolves with the binary string */ function blobToBase64String(blob) { return blobToBinaryString(blob).then(function (binary) { return btoa(binary); }); } /** * Convert a data URL string * (e.g. 'data:image/png;base64,iVBORw0KG...') * to a Blob. Returns a Promise. * @param {string} dataURL * @returns {Promise} Promise that resolves with the Blob */ function dataURLToBlob(dataURL) { return Promise.resolve().then(function () { var type = dataURL.match(/data:([^;]+)/)[1]; var base64 = dataURL.replace(/^[^,]+,/, ''); var buff = binaryStringToArrayBuffer(atob(base64)); return createBlob([buff], {type: type}); }); } /** * Convert an image's src URL to a data URL by loading the image and painting * it to a canvas. Returns a Promise. * *

Note: this will coerce the image to the desired content type, and it * will only paint the first frame of an animated GIF. * * @param {string} src * @param {string|undefined} type - the content type (optional, defaults to 'image/png') * @param {string|undefined} crossOrigin - for CORS-enabled images, set this to * 'Anonymous' to avoid "tainted canvas" errors * @param {number|undefined} quality - a number between 0 and 1 indicating image quality * if the requested type is 'image/jpeg' or 'image/webp' * @returns {Promise} Promise that resolves with the data URL string */ function imgSrcToDataURL(src, type, crossOrigin, quality) { type = type || 'image/png'; return loadImage(src, crossOrigin).then(function (img) { return imgToCanvas(img); }).then(function (canvas) { return canvas.toDataURL(type, quality); }); } /** * Convert a canvas to a Blob. Returns a Promise. * @param {string} canvas * @param {string|undefined} type - the content type (optional, defaults to 'image/png') * @param {number|undefined} quality - a number between 0 and 1 indicating image quality * if the requested type is 'image/jpeg' or 'image/webp' * @returns {Promise} Promise that resolves with the Blob */ function canvasToBlob(canvas, type, quality) { return Promise.resolve().then(function () { if (typeof canvas.toBlob === 'function') { return new Promise(function (resolve) { canvas.toBlob(resolve, type, quality); }); } return dataURLToBlob(canvas.toDataURL(type, quality)); }); } /** * Convert an image's src URL to a Blob by loading the image and painting * it to a canvas. Returns a Promise. * *

Note: this will coerce the image to the desired content type, and it * will only paint the first frame of an animated GIF. * * @param {string} src * @param {string|undefined} type - the content type (optional, defaults to 'image/png') * @param {string|undefined} crossOrigin - for CORS-enabled images, set this to * 'Anonymous' to avoid "tainted canvas" errors * @param {number|undefined} quality - a number between 0 and 1 indicating image quality * if the requested type is 'image/jpeg' or 'image/webp' * @returns {Promise} Promise that resolves with the Blob */ function imgSrcToBlob(src, type, crossOrigin, quality) { type = type || 'image/png'; return loadImage(src, crossOrigin).then(function (img) { return imgToCanvas(img); }).then(function (canvas) { return canvasToBlob(canvas, type, quality); }); } /** * Convert an ArrayBuffer to a Blob. Returns a Promise. * * @param {ArrayBuffer} buffer * @param {string|undefined} type - the content type (optional) * @returns {Promise} Promise that resolves with the Blob */ function arrayBufferToBlob(buffer, type) { return Promise.resolve().then(function () { return createBlob([buffer], type); }); } /** * Convert a Blob to an ArrayBuffer. Returns a Promise. * @param {Blob} blob * @returns {Promise} Promise that resolves with the ArrayBuffer */ function blobToArrayBuffer(blob) { return new Promise(function (resolve, reject) { var reader = new FileReader(); reader.onloadend = function (e) { var result = e.target.result || new ArrayBuffer(0); resolve(result); }; reader.onerror = reject; reader.readAsArrayBuffer(blob); }); } module.exports = { createBlob : createBlob, createObjectURL : createObjectURL, revokeObjectURL : revokeObjectURL, imgSrcToBlob : imgSrcToBlob, imgSrcToDataURL : imgSrcToDataURL, canvasToBlob : canvasToBlob, dataURLToBlob : dataURLToBlob, blobToBase64String : blobToBase64String, base64StringToBlob : base64StringToBlob, binaryStringToBlob : binaryStringToBlob, blobToBinaryString : blobToBinaryString, arrayBufferToBlob : arrayBufferToBlob, blobToArrayBuffer : blobToArrayBuffer }; },{"blob":23,"native-or-lie":64}],23:[function(_dereq_,module,exports){ (function (global){ /** * Create a blob builder even when vendor prefixes exist */ var BlobBuilder = global.BlobBuilder || global.WebKitBlobBuilder || global.MSBlobBuilder || global.MozBlobBuilder; /** * Check if Blob constructor is supported */ var blobSupported = (function() { try { var a = new Blob(['hi']); return a.size === 2; } catch(e) { return false; } })(); /** * Check if Blob constructor supports ArrayBufferViews * Fails in Safari 6, so we need to map to ArrayBuffers there. */ var blobSupportsArrayBufferView = blobSupported && (function() { try { var b = new Blob([new Uint8Array([1,2])]); return b.size === 2; } catch(e) { return false; } })(); /** * Check if BlobBuilder is supported */ var blobBuilderSupported = BlobBuilder && BlobBuilder.prototype.append && BlobBuilder.prototype.getBlob; /** * Helper function that maps ArrayBufferViews to ArrayBuffers * Used by BlobBuilder constructor and old browsers that didn't * support it in the Blob constructor. */ function mapArrayBufferViews(ary) { for (var i = 0; i < ary.length; i++) { var chunk = ary[i]; if (chunk.buffer instanceof ArrayBuffer) { var buf = chunk.buffer; // if this is a subarray, make a copy so we only // include the subarray region from the underlying buffer if (chunk.byteLength !== buf.byteLength) { var copy = new Uint8Array(chunk.byteLength); copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); buf = copy.buffer; } ary[i] = buf; } } } function BlobBuilderConstructor(ary, options) { options = options || {}; var bb = new BlobBuilder(); mapArrayBufferViews(ary); for (var i = 0; i < ary.length; i++) { bb.append(ary[i]); } return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); }; function BlobConstructor(ary, options) { mapArrayBufferViews(ary); return new Blob(ary, options || {}); }; module.exports = (function() { if (blobSupported) { return blobSupportsArrayBufferView ? global.Blob : BlobConstructor; } else if (blobBuilderSupported) { return BlobBuilderConstructor; } else { return undefined; } })(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],24:[function(_dereq_,module,exports){ },{}],25:[function(_dereq_,module,exports){ /** * Expose `Emitter`. */ module.exports = Emitter; /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); }; /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks[event] = this._callbacks[event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ var self = this; this._callbacks = this._callbacks || {}; function on() { self.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks[event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks[event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = [].slice.call(arguments, 1) , callbacks = this._callbacks[event]; if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; return this._callbacks[event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; },{}],26:[function(_dereq_,module,exports){ module.exports = function(a, b){ var fn = function(){}; fn.prototype = b.prototype; a.prototype = new fn; a.prototype.constructor = a; }; },{}],27:[function(_dereq_,module,exports){ (function (process){ /** * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = _dereq_('./debug'); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage(); /** * Colors. */ exports.colors = [ 'lightseagreen', 'forestgreen', 'goldenrod', 'dodgerblue', 'darkorchid', 'crimson' ]; /** * Currently only WebKit-based Web Inspectors, Firefox >= v31, * and the Firebug extension (any Firefox version) are known * to support "%c" CSS customizations. * * TODO: add a `localStorage` variable to explicitly enable/disable colors */ function useColors() { // is webkit? http://stackoverflow.com/a/16459606/376773 // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 return (typeof document !== 'undefined' && 'WebkitAppearance' in document.documentElement.style) || // is firebug? http://stackoverflow.com/a/398120/376773 (window.console && (console.firebug || (console.exception && console.table))) || // is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); } /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ exports.formatters.j = function(v) { return JSON.stringify(v); }; /** * Colorize log arguments if enabled. * * @api public */ function formatArgs() { var args = arguments; var useColors = this.useColors; args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff); if (!useColors) return args; var c = 'color: ' + this.color; args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); // the final "%c" is somewhat tricky, because there could be other // arguments passed either before or after the %c, so we need to // figure out the correct index to insert the CSS into var index = 0; var lastC = 0; args[0].replace(/%[a-z%]/g, function(match) { if ('%%' === match) return; index++; if ('%c' === match) { // we only are interested in the *last* %c // (the user may have provided their own) lastC = index; } }); args.splice(lastC, 0, c); return args; } /** * Invokes `console.log()` when available. * No-op when `console.log` is not a "function". * * @api public */ function log() { // this hackery is required for IE8/9, where // the `console.log` function doesn't have 'apply' return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments); } /** * Save `namespaces`. * * @param {String} namespaces * @api private */ function save(namespaces) { try { if (null == namespaces) { exports.storage.removeItem('debug'); } else { exports.storage.debug = namespaces; } } catch(e) {} } /** * Load `namespaces`. * * @return {String} returns the previously persisted debug modes * @api private */ function load() { var r; try { r = exports.storage.debug; } catch(e) {} // If debug isn't set in LS, and we're in Electron, try to load $DEBUG if ('env' in (typeof process === 'undefined' ? {} : process)) { r = process.env.DEBUG; } return r; } /** * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); /** * Localstorage attempts to return the localstorage. * * This is necessary because safari throws * when a user disables cookies/localstorage * and you attempt to access it. * * @return {LocalStorage} * @api private */ function localstorage(){ try { return window.localStorage; } catch (e) {} } }).call(this,_dereq_('_process')) },{"./debug":28,"_process":71}],28:[function(_dereq_,module,exports){ /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug.debug = debug; exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; exports.humanize = _dereq_('ms'); /** * The currently active debug mode names, and names to skip. */ exports.names = []; exports.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lowercased letter, i.e. "n". */ exports.formatters = {}; /** * Previously assigned color. */ var prevColor = 0; /** * Previous log timestamp. */ var prevTime; /** * Select a color. * * @return {Number} * @api private */ function selectColor() { return exports.colors[prevColor++ % exports.colors.length]; } /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function debug(namespace) { // define the `disabled` version function disabled() { } disabled.enabled = false; // define the `enabled` version function enabled() { var self = enabled; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // add the `color` if not set if (null == self.useColors) self.useColors = exports.useColors(); if (null == self.color && self.useColors) self.color = selectColor(); var args = new Array(arguments.length); for (var i = 0; i < args.length; i++) { args[i] = arguments[i]; } args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %o args = ['%o'].concat(args); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); // apply env-specific formatting args = exports.formatArgs.apply(self, args); var logFn = enabled.log || exports.log || console.log.bind(console); logFn.apply(self, args); } enabled.enabled = true; var fn = exports.enabled(namespace) ? enabled : disabled; fn.namespace = namespace; return fn; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { exports.save(namespaces); var split = (namespaces || '').split(/[\s,]+/); var len = split.length; for (var i = 0; i < len; i++) { if (!split[i]) continue; // ignore empty strings namespaces = split[i].replace(/[\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '.*?'); if (namespaces[0] === '-') { exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); } else { exports.names.push(new RegExp('^' + namespaces + '$')); } } } /** * Disable debug output. * * @api public */ function disable() { exports.enable(''); } /** * Returns true if the given mode name is enabled, false otherwise. * * @param {String} name * @return {Boolean} * @api public */ function enabled(name) { var i, len; for (i = 0, len = exports.skips.length; i < len; i++) { if (exports.skips[i].test(name)) { return false; } } for (i = 0, len = exports.names.length; i < len; i++) { if (exports.names[i].test(name)) { return true; } } return false; } /** * Coerce `val`. * * @param {Mixed} val * @return {Mixed} * @api private */ function coerce(val) { if (val instanceof Error) return val.stack || val.message; return val; } },{"ms":63}],29:[function(_dereq_,module,exports){ module.exports = _dereq_('./lib/index'); },{"./lib/index":30}],30:[function(_dereq_,module,exports){ module.exports = _dereq_('./socket'); /** * Exports parser * * @api public * */ module.exports.parser = _dereq_('engine.io-parser'); },{"./socket":31,"engine.io-parser":42}],31:[function(_dereq_,module,exports){ (function (global){ /** * Module dependencies. */ var transports = _dereq_('./transports/index'); var Emitter = _dereq_('component-emitter'); var debug = _dereq_('debug')('engine.io-client:socket'); var index = _dereq_('indexof'); var parser = _dereq_('engine.io-parser'); var parseuri = _dereq_('parseuri'); var parsejson = _dereq_('parsejson'); var parseqs = _dereq_('parseqs'); /** * Module exports. */ module.exports = Socket; /** * Socket constructor. * * @param {String|Object} uri or options * @param {Object} options * @api public */ function Socket (uri, opts) { if (!(this instanceof Socket)) return new Socket(uri, opts); opts = opts || {}; if (uri && 'object' === typeof uri) { opts = uri; uri = null; } if (uri) { uri = parseuri(uri); opts.hostname = uri.host; opts.secure = uri.protocol === 'https' || uri.protocol === 'wss'; opts.port = uri.port; if (uri.query) opts.query = uri.query; } else if (opts.host) { opts.hostname = parseuri(opts.host).host; } this.secure = null != opts.secure ? opts.secure : (global.location && 'https:' === location.protocol); if (opts.hostname && !opts.port) { // if no port is specified manually, use the protocol default opts.port = this.secure ? '443' : '80'; } this.agent = opts.agent || false; this.hostname = opts.hostname || (global.location ? location.hostname : 'localhost'); this.port = opts.port || (global.location && location.port ? location.port : (this.secure ? 443 : 80)); this.query = opts.query || {}; if ('string' === typeof this.query) this.query = parseqs.decode(this.query); this.upgrade = false !== opts.upgrade; this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; this.forceJSONP = !!opts.forceJSONP; this.jsonp = false !== opts.jsonp; this.forceBase64 = !!opts.forceBase64; this.enablesXDR = !!opts.enablesXDR; this.timestampParam = opts.timestampParam || 't'; this.timestampRequests = opts.timestampRequests; this.transports = opts.transports || ['polling', 'websocket']; this.readyState = ''; this.writeBuffer = []; this.prevBufferLen = 0; this.policyPort = opts.policyPort || 843; this.rememberUpgrade = opts.rememberUpgrade || false; this.binaryType = null; this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; if (true === this.perMessageDeflate) this.perMessageDeflate = {}; if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { this.perMessageDeflate.threshold = 1024; } // SSL options for Node.js client this.pfx = opts.pfx || null; this.key = opts.key || null; this.passphrase = opts.passphrase || null; this.cert = opts.cert || null; this.ca = opts.ca || null; this.ciphers = opts.ciphers || null; this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? null : opts.rejectUnauthorized; // other options for Node.js client var freeGlobal = typeof global === 'object' && global; if (freeGlobal.global === freeGlobal) { if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { this.extraHeaders = opts.extraHeaders; } } // set on handshake this.id = null; this.upgrades = null; this.pingInterval = null; this.pingTimeout = null; // set on heartbeat this.pingIntervalTimer = null; this.pingTimeoutTimer = null; this.open(); } Socket.priorWebsocketSuccess = false; /** * Mix in `Emitter`. */ Emitter(Socket.prototype); /** * Protocol version. * * @api public */ Socket.protocol = parser.protocol; // this is an int /** * Expose deps for legacy compatibility * and standalone browser access. */ Socket.Socket = Socket; Socket.Transport = _dereq_('./transport'); Socket.transports = _dereq_('./transports/index'); Socket.parser = _dereq_('engine.io-parser'); /** * Creates transport of the given type. * * @param {String} transport name * @return {Transport} * @api private */ Socket.prototype.createTransport = function (name) { debug('creating transport "%s"', name); var query = clone(this.query); // append engine.io protocol identifier query.EIO = parser.protocol; // transport name query.transport = name; // session id if we already have one if (this.id) query.sid = this.id; var transport = new transports[name]({ agent: this.agent, hostname: this.hostname, port: this.port, secure: this.secure, path: this.path, query: query, forceJSONP: this.forceJSONP, jsonp: this.jsonp, forceBase64: this.forceBase64, enablesXDR: this.enablesXDR, timestampRequests: this.timestampRequests, timestampParam: this.timestampParam, policyPort: this.policyPort, socket: this, pfx: this.pfx, key: this.key, passphrase: this.passphrase, cert: this.cert, ca: this.ca, ciphers: this.ciphers, rejectUnauthorized: this.rejectUnauthorized, perMessageDeflate: this.perMessageDeflate, extraHeaders: this.extraHeaders }); return transport; }; function clone (obj) { var o = {}; for (var i in obj) { if (obj.hasOwnProperty(i)) { o[i] = obj[i]; } } return o; } /** * Initializes transport to use and starts probe. * * @api private */ Socket.prototype.open = function () { var transport; if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) { transport = 'websocket'; } else if (0 === this.transports.length) { // Emit error on next tick so it can be listened to var self = this; setTimeout(function () { self.emit('error', 'No transports available'); }, 0); return; } else { transport = this.transports[0]; } this.readyState = 'opening'; // Retry with the next transport if the transport is disabled (jsonp: false) try { transport = this.createTransport(transport); } catch (e) { this.transports.shift(); this.open(); return; } transport.open(); this.setTransport(transport); }; /** * Sets the current transport. Disables the existing one (if any). * * @api private */ Socket.prototype.setTransport = function (transport) { debug('setting transport %s', transport.name); var self = this; if (this.transport) { debug('clearing existing transport %s', this.transport.name); this.transport.removeAllListeners(); } // set up transport this.transport = transport; // set up transport listeners transport .on('drain', function () { self.onDrain(); }) .on('packet', function (packet) { self.onPacket(packet); }) .on('error', function (e) { self.onError(e); }) .on('close', function () { self.onClose('transport close'); }); }; /** * Probes a transport. * * @param {String} transport name * @api private */ Socket.prototype.probe = function (name) { debug('probing transport "%s"', name); var transport = this.createTransport(name, { probe: 1 }); var failed = false; var self = this; Socket.priorWebsocketSuccess = false; function onTransportOpen () { if (self.onlyBinaryUpgrades) { var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; failed = failed || upgradeLosesBinary; } if (failed) return; debug('probe transport "%s" opened', name); transport.send([{ type: 'ping', data: 'probe' }]); transport.once('packet', function (msg) { if (failed) return; if ('pong' === msg.type && 'probe' === msg.data) { debug('probe transport "%s" pong', name); self.upgrading = true; self.emit('upgrading', transport); if (!transport) return; Socket.priorWebsocketSuccess = 'websocket' === transport.name; debug('pausing current transport "%s"', self.transport.name); self.transport.pause(function () { if (failed) return; if ('closed' === self.readyState) return; debug('changing transport and sending upgrade packet'); cleanup(); self.setTransport(transport); transport.send([{ type: 'upgrade' }]); self.emit('upgrade', transport); transport = null; self.upgrading = false; self.flush(); }); } else { debug('probe transport "%s" failed', name); var err = new Error('probe error'); err.transport = transport.name; self.emit('upgradeError', err); } }); } function freezeTransport () { if (failed) return; // Any callback called by transport should be ignored since now failed = true; cleanup(); transport.close(); transport = null; } // Handle any error that happens while probing function onerror (err) { var error = new Error('probe error: ' + err); error.transport = transport.name; freezeTransport(); debug('probe transport "%s" failed because of error: %s', name, err); self.emit('upgradeError', error); } function onTransportClose () { onerror('transport closed'); } // When the socket is closed while we're probing function onclose () { onerror('socket closed'); } // When the socket is upgraded while we're probing function onupgrade (to) { if (transport && to.name !== transport.name) { debug('"%s" works - aborting "%s"', to.name, transport.name); freezeTransport(); } } // Remove all listeners on the transport and on self function cleanup () { transport.removeListener('open', onTransportOpen); transport.removeListener('error', onerror); transport.removeListener('close', onTransportClose); self.removeListener('close', onclose); self.removeListener('upgrading', onupgrade); } transport.once('open', onTransportOpen); transport.once('error', onerror); transport.once('close', onTransportClose); this.once('close', onclose); this.once('upgrading', onupgrade); transport.open(); }; /** * Called when connection is deemed open. * * @api public */ Socket.prototype.onOpen = function () { debug('socket open'); this.readyState = 'open'; Socket.priorWebsocketSuccess = 'websocket' === this.transport.name; this.emit('open'); this.flush(); // we check for `readyState` in case an `open` // listener already closed the socket if ('open' === this.readyState && this.upgrade && this.transport.pause) { debug('starting upgrade probes'); for (var i = 0, l = this.upgrades.length; i < l; i++) { this.probe(this.upgrades[i]); } } }; /** * Handles a packet. * * @api private */ Socket.prototype.onPacket = function (packet) { if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) { debug('socket receive: type "%s", data "%s"', packet.type, packet.data); this.emit('packet', packet); // Socket is live - any packet counts this.emit('heartbeat'); switch (packet.type) { case 'open': this.onHandshake(parsejson(packet.data)); break; case 'pong': this.setPing(); this.emit('pong'); break; case 'error': var err = new Error('server error'); err.code = packet.data; this.onError(err); break; case 'message': this.emit('data', packet.data); this.emit('message', packet.data); break; } } else { debug('packet received with socket readyState "%s"', this.readyState); } }; /** * Called upon handshake completion. * * @param {Object} handshake obj * @api private */ Socket.prototype.onHandshake = function (data) { this.emit('handshake', data); this.id = data.sid; this.transport.query.sid = data.sid; this.upgrades = this.filterUpgrades(data.upgrades); this.pingInterval = data.pingInterval; this.pingTimeout = data.pingTimeout; this.onOpen(); // In case open handler closes socket if ('closed' === this.readyState) return; this.setPing(); // Prolong liveness of socket on heartbeat this.removeListener('heartbeat', this.onHeartbeat); this.on('heartbeat', this.onHeartbeat); }; /** * Resets ping timeout. * * @api private */ Socket.prototype.onHeartbeat = function (timeout) { clearTimeout(this.pingTimeoutTimer); var self = this; self.pingTimeoutTimer = setTimeout(function () { if ('closed' === self.readyState) return; self.onClose('ping timeout'); }, timeout || (self.pingInterval + self.pingTimeout)); }; /** * Pings server every `this.pingInterval` and expects response * within `this.pingTimeout` or closes connection. * * @api private */ Socket.prototype.setPing = function () { var self = this; clearTimeout(self.pingIntervalTimer); self.pingIntervalTimer = setTimeout(function () { debug('writing ping packet - expecting pong within %sms', self.pingTimeout); self.ping(); self.onHeartbeat(self.pingTimeout); }, self.pingInterval); }; /** * Sends a ping packet. * * @api private */ Socket.prototype.ping = function () { var self = this; this.sendPacket('ping', function () { self.emit('ping'); }); }; /** * Called on `drain` event * * @api private */ Socket.prototype.onDrain = function () { this.writeBuffer.splice(0, this.prevBufferLen); // setting prevBufferLen = 0 is very important // for example, when upgrading, upgrade packet is sent over, // and a nonzero prevBufferLen could cause problems on `drain` this.prevBufferLen = 0; if (0 === this.writeBuffer.length) { this.emit('drain'); } else { this.flush(); } }; /** * Flush write buffers. * * @api private */ Socket.prototype.flush = function () { if ('closed' !== this.readyState && this.transport.writable && !this.upgrading && this.writeBuffer.length) { debug('flushing %d packets in socket', this.writeBuffer.length); this.transport.send(this.writeBuffer); // keep track of current length of writeBuffer // splice writeBuffer and callbackBuffer on `drain` this.prevBufferLen = this.writeBuffer.length; this.emit('flush'); } }; /** * Sends a message. * * @param {String} message. * @param {Function} callback function. * @param {Object} options. * @return {Socket} for chaining. * @api public */ Socket.prototype.write = Socket.prototype.send = function (msg, options, fn) { this.sendPacket('message', msg, options, fn); return this; }; /** * Sends a packet. * * @param {String} packet type. * @param {String} data. * @param {Object} options. * @param {Function} callback function. * @api private */ Socket.prototype.sendPacket = function (type, data, options, fn) { if ('function' === typeof data) { fn = data; data = undefined; } if ('function' === typeof options) { fn = options; options = null; } if ('closing' === this.readyState || 'closed' === this.readyState) { return; } options = options || {}; options.compress = false !== options.compress; var packet = { type: type, data: data, options: options }; this.emit('packetCreate', packet); this.writeBuffer.push(packet); if (fn) this.once('flush', fn); this.flush(); }; /** * Closes the connection. * * @api private */ Socket.prototype.close = function () { if ('opening' === this.readyState || 'open' === this.readyState) { this.readyState = 'closing'; var self = this; if (this.writeBuffer.length) { this.once('drain', function () { if (this.upgrading) { waitForUpgrade(); } else { close(); } }); } else if (this.upgrading) { waitForUpgrade(); } else { close(); } } function close () { self.onClose('forced close'); debug('socket closing - telling transport to close'); self.transport.close(); } function cleanupAndClose () { self.removeListener('upgrade', cleanupAndClose); self.removeListener('upgradeError', cleanupAndClose); close(); } function waitForUpgrade () { // wait for upgrade to finish since we can't send packets while pausing a transport self.once('upgrade', cleanupAndClose); self.once('upgradeError', cleanupAndClose); } return this; }; /** * Called upon transport error * * @api private */ Socket.prototype.onError = function (err) { debug('socket error %j', err); Socket.priorWebsocketSuccess = false; this.emit('error', err); this.onClose('transport error', err); }; /** * Called upon transport close. * * @api private */ Socket.prototype.onClose = function (reason, desc) { if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) { debug('socket close with reason: "%s"', reason); var self = this; // clear timers clearTimeout(this.pingIntervalTimer); clearTimeout(this.pingTimeoutTimer); // stop event from firing again for transport this.transport.removeAllListeners('close'); // ensure transport won't stay open this.transport.close(); // ignore further transport communication this.transport.removeAllListeners(); // set ready state this.readyState = 'closed'; // clear session id this.id = null; // emit close event this.emit('close', reason, desc); // clean buffers after, so users can still // grab the buffers on `close` event self.writeBuffer = []; self.prevBufferLen = 0; } }; /** * Filters upgrades, returning only those matching client transports. * * @param {Array} server upgrades * @api private * */ Socket.prototype.filterUpgrades = function (upgrades) { var filteredUpgrades = []; for (var i = 0, j = upgrades.length; i < j; i++) { if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); } return filteredUpgrades; }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./transport":32,"./transports/index":33,"component-emitter":25,"debug":39,"engine.io-parser":42,"indexof":47,"parsejson":65,"parseqs":66,"parseuri":67}],32:[function(_dereq_,module,exports){ /** * Module dependencies. */ var parser = _dereq_('engine.io-parser'); var Emitter = _dereq_('component-emitter'); /** * Module exports. */ module.exports = Transport; /** * Transport abstract constructor. * * @param {Object} options. * @api private */ function Transport (opts) { this.path = opts.path; this.hostname = opts.hostname; this.port = opts.port; this.secure = opts.secure; this.query = opts.query; this.timestampParam = opts.timestampParam; this.timestampRequests = opts.timestampRequests; this.readyState = ''; this.agent = opts.agent || false; this.socket = opts.socket; this.enablesXDR = opts.enablesXDR; // SSL options for Node.js client this.pfx = opts.pfx; this.key = opts.key; this.passphrase = opts.passphrase; this.cert = opts.cert; this.ca = opts.ca; this.ciphers = opts.ciphers; this.rejectUnauthorized = opts.rejectUnauthorized; // other options for Node.js client this.extraHeaders = opts.extraHeaders; } /** * Mix in `Emitter`. */ Emitter(Transport.prototype); /** * Emits an error. * * @param {String} str * @return {Transport} for chaining * @api public */ Transport.prototype.onError = function (msg, desc) { var err = new Error(msg); err.type = 'TransportError'; err.description = desc; this.emit('error', err); return this; }; /** * Opens the transport. * * @api public */ Transport.prototype.open = function () { if ('closed' === this.readyState || '' === this.readyState) { this.readyState = 'opening'; this.doOpen(); } return this; }; /** * Closes the transport. * * @api private */ Transport.prototype.close = function () { if ('opening' === this.readyState || 'open' === this.readyState) { this.doClose(); this.onClose(); } return this; }; /** * Sends multiple packets. * * @param {Array} packets * @api private */ Transport.prototype.send = function (packets) { if ('open' === this.readyState) { this.write(packets); } else { throw new Error('Transport not open'); } }; /** * Called upon open * * @api private */ Transport.prototype.onOpen = function () { this.readyState = 'open'; this.writable = true; this.emit('open'); }; /** * Called with data. * * @param {String} data * @api private */ Transport.prototype.onData = function (data) { var packet = parser.decodePacket(data, this.socket.binaryType); this.onPacket(packet); }; /** * Called with a decoded packet. */ Transport.prototype.onPacket = function (packet) { this.emit('packet', packet); }; /** * Called upon close. * * @api private */ Transport.prototype.onClose = function () { this.readyState = 'closed'; this.emit('close'); }; },{"component-emitter":25,"engine.io-parser":42}],33:[function(_dereq_,module,exports){ (function (global){ /** * Module dependencies */ var XMLHttpRequest = _dereq_('xmlhttprequest-ssl'); var XHR = _dereq_('./polling-xhr'); var JSONP = _dereq_('./polling-jsonp'); var websocket = _dereq_('./websocket'); /** * Export transports. */ exports.polling = polling; exports.websocket = websocket; /** * Polling transport polymorphic constructor. * Decides on xhr vs jsonp based on feature detection. * * @api private */ function polling (opts) { var xhr; var xd = false; var xs = false; var jsonp = false !== opts.jsonp; if (global.location) { var isSSL = 'https:' === location.protocol; var port = location.port; // some user agents have empty `location.port` if (!port) { port = isSSL ? 443 : 80; } xd = opts.hostname !== location.hostname || port !== opts.port; xs = opts.secure !== isSSL; } opts.xdomain = xd; opts.xscheme = xs; xhr = new XMLHttpRequest(opts); if ('open' in xhr && !opts.forceJSONP) { return new XHR(opts); } else { if (!jsonp) throw new Error('JSONP disabled'); return new JSONP(opts); } } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./polling-jsonp":34,"./polling-xhr":35,"./websocket":37,"xmlhttprequest-ssl":38}],34:[function(_dereq_,module,exports){ (function (global){ /** * Module requirements. */ var Polling = _dereq_('./polling'); var inherit = _dereq_('component-inherit'); /** * Module exports. */ module.exports = JSONPPolling; /** * Cached regular expressions. */ var rNewline = /\n/g; var rEscapedNewline = /\\n/g; /** * Global JSONP callbacks. */ var callbacks; /** * Noop. */ function empty () { } /** * JSONP Polling constructor. * * @param {Object} opts. * @api public */ function JSONPPolling (opts) { Polling.call(this, opts); this.query = this.query || {}; // define global callbacks array if not present // we do this here (lazily) to avoid unneeded global pollution if (!callbacks) { // we need to consider multiple engines in the same page if (!global.___eio) global.___eio = []; callbacks = global.___eio; } // callback identifier this.index = callbacks.length; // add callback to jsonp global var self = this; callbacks.push(function (msg) { self.onData(msg); }); // append to query string this.query.j = this.index; // prevent spurious errors from being emitted when the window is unloaded if (global.document && global.addEventListener) { global.addEventListener('beforeunload', function () { if (self.script) self.script.onerror = empty; }, false); } } /** * Inherits from Polling. */ inherit(JSONPPolling, Polling); /* * JSONP only supports binary as base64 encoded strings */ JSONPPolling.prototype.supportsBinary = false; /** * Closes the socket. * * @api private */ JSONPPolling.prototype.doClose = function () { if (this.script) { this.script.parentNode.removeChild(this.script); this.script = null; } if (this.form) { this.form.parentNode.removeChild(this.form); this.form = null; this.iframe = null; } Polling.prototype.doClose.call(this); }; /** * Starts a poll cycle. * * @api private */ JSONPPolling.prototype.doPoll = function () { var self = this; var script = document.createElement('script'); if (this.script) { this.script.parentNode.removeChild(this.script); this.script = null; } script.async = true; script.src = this.uri(); script.onerror = function (e) { self.onError('jsonp poll error', e); }; var insertAt = document.getElementsByTagName('script')[0]; if (insertAt) { insertAt.parentNode.insertBefore(script, insertAt); } else { (document.head || document.body).appendChild(script); } this.script = script; var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent); if (isUAgecko) { setTimeout(function () { var iframe = document.createElement('iframe'); document.body.appendChild(iframe); document.body.removeChild(iframe); }, 100); } }; /** * Writes with a hidden iframe. * * @param {String} data to send * @param {Function} called upon flush. * @api private */ JSONPPolling.prototype.doWrite = function (data, fn) { var self = this; if (!this.form) { var form = document.createElement('form'); var area = document.createElement('textarea'); var id = this.iframeId = 'eio_iframe_' + this.index; var iframe; form.className = 'socketio'; form.style.position = 'absolute'; form.style.top = '-1000px'; form.style.left = '-1000px'; form.target = id; form.method = 'POST'; form.setAttribute('accept-charset', 'utf-8'); area.name = 'd'; form.appendChild(area); document.body.appendChild(form); this.form = form; this.area = area; } this.form.action = this.uri(); function complete () { initIframe(); fn(); } function initIframe () { if (self.iframe) { try { self.form.removeChild(self.iframe); } catch (e) { self.onError('jsonp polling iframe removal error', e); } } try { // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) var html = '