diff --git a/audio/skill/reyingzi1_sunce.mp3 b/audio/skill/reyingzi1_sunce.mp3
new file mode 100644
index 000000000..923f2b7ec
--- /dev/null
+++ b/audio/skill/reyingzi1_sunce.mp3
@@ -0,0 +1,7 @@
+
+
+404 Not Found
+
+Not Found
+The requested URL /audio/skill/reyingzi1_sunce.mp3 was not found on this server.
+
diff --git a/audio/skill/reyingzi2_sunce.mp3 b/audio/skill/reyingzi2_sunce.mp3
new file mode 100644
index 000000000..97f74d461
--- /dev/null
+++ b/audio/skill/reyingzi2_sunce.mp3
@@ -0,0 +1,7 @@
+
+
+404 Not Found
+
+Not Found
+The requested URL /audio/skill/reyingzi2_sunce.mp3 was not found on this server.
+
diff --git a/audio/skill/reyingzi_cunce1.mp3 b/audio/skill/reyingzi_cunce1.mp3
new file mode 100755
index 000000000..c6f10e3c3
Binary files /dev/null and b/audio/skill/reyingzi_cunce1.mp3 differ
diff --git a/audio/skill/reyingzi_sunce2.mp3 b/audio/skill/reyingzi_sunce2.mp3
new file mode 100755
index 000000000..4e13666cc
Binary files /dev/null and b/audio/skill/reyingzi_sunce2.mp3 differ
diff --git a/character/refresh.js b/character/refresh.js
index d4d011112..1f8330dc5 100755
--- a/character/refresh.js
+++ b/character/refresh.js
@@ -349,6 +349,7 @@ character.refresh={
},
reyingzi:{
audio:2,
+ audioname:['sunce'],
trigger:{player:'phaseDrawBegin'},
forced:true,
content:function(){
diff --git a/game/asset.js b/game/asset.js
index 2fe45b9f9..e7533f8db 100644
--- a/game/asset.js
+++ b/game/asset.js
@@ -1,5 +1,5 @@
window.noname_asset_list=[
- '1.8.1',
+ '1.8.1.1',
'image/background/huangtian_bg.jpg',
'image/background/shengshi_bg.jpg',
'image/background/xueji_bg.jpg',
@@ -1854,6 +1854,8 @@ window.noname_asset_list=[
'audio/skill/reyiji2.mp3',
'audio/skill/reyingzi1.mp3',
'audio/skill/reyingzi2.mp3',
+ 'audio/skill/reyingzi1_sunce.mp3',
+ 'audio/skill/reyingzi2_sunce.mp3',
'audio/skill/roulin1.mp3',
'audio/skill/roulin2.mp3',
'audio/skill/ruoyu1.mp3',
@@ -2160,4 +2162,36 @@ window.noname_asset_list=[
'font/xiaozhuan.ttf',
'font/huangcao.ttf',
'font/xinwei.ttf',
+ 'theme/music/grid.png',
+ 'theme/music/style.css',
+ 'theme/music/wood.png',
+ 'theme/music/wood3.png',
+ 'theme/simple/card.png',
+ 'theme/simple/grid.png',
+ 'theme/simple/style.css',
+ 'theme/simple/unknown.png',
+ 'theme/simple/wood.png',
+ 'theme/simple/wood3.png',
+ 'theme/woodden/grid.png',
+ 'theme/woodden/style.css',
+ 'theme/woodden/wood.jpg',
+ 'theme/woodden/wood.png',
+ 'theme/woodden/wood2.jpg',
+ 'theme/woodden/wood2.png',
+ 'theme/style/card/default.css',
+ 'theme/style/card/music.css',
+ 'theme/style/card/simple.css',
+ 'theme/style/card/wood.css',
+ 'theme/style/cardback/default.css',
+ 'theme/style/cardback/music.css',
+ 'theme/style/cardback/official.css',
+ 'theme/style/cardback/wood.css',
+ 'theme/style/cardback/image/official.png',
+ 'theme/style/cardback/image/official2.png',
+ 'theme/style/hp/default.css',
+ 'theme/style/hp/official.css',
+ 'theme/style/hp/image/hp1.png',
+ 'theme/style/hp/image/hp2.png',
+ 'theme/style/hp/image/hp3.png',
+ 'theme/style/hp/image/hp4.png',
]
diff --git a/game/game.js b/game/game.js
index 064b095db..eb7cea84e 100755
--- a/game/game.js
+++ b/game/game.js
@@ -3291,6 +3291,9 @@
}
});
}
+ if(lib.config.debug){
+ require('remote').getCurrentWindow().openDevTools();
+ }
ui.background=ui.create.div('.background');
ui.background.style.backgroundSize="cover";
if(lib.config.image_background&&lib.config.image_background!='default'&&lib.config.image_background!='custom'){
@@ -3737,8 +3740,9 @@
if(path[path.length-1]=='/'){
path=path.slice(0,path.length-1);
}
- if(path=='mode'&&lib.config.all.stockmode.indexOf(file)==-1){
+ if(path==lib.assetURL+'mode'&&lib.config.all.stockmode.indexOf(file)==-1){
lib.init['setMode_'+file]();
+ onload();
return;
}
if(Array.isArray(file)){
@@ -17600,6 +17604,7 @@
text.style.position='absolute';
text.style.left='30px';
text.style.top='12px';
+ text.style.wordBreak='break-all';
page.appendChild(text);
var caption=ui.create.div('','输入命令',page);
@@ -17633,7 +17638,7 @@
else{
try{
var result=eval(text2.value);
- if(result){
+ if(result!==undefined){
game.print(result);
}
}
@@ -17941,6 +17946,11 @@
script.remove();
var updates=window.noname_source_list;
delete window.noname_source_list;
+ for(var i=0;i
+ * MIT Licensed
+ */
+
+var fs = require('fs');
+
+function Options(defaults) {
+ var internalValues = {};
+ var values = this.value = {};
+ Object.keys(defaults).forEach(function(key) {
+ internalValues[key] = defaults[key];
+ Object.defineProperty(values, key, {
+ get: function() { return internalValues[key]; },
+ configurable: false,
+ enumerable: true
+ });
+ });
+ this.reset = function() {
+ Object.keys(defaults).forEach(function(key) {
+ internalValues[key] = defaults[key];
+ });
+ return this;
+ };
+ this.merge = function(options, required) {
+ options = options || {};
+ if (Object.prototype.toString.call(required) === '[object Array]') {
+ var missing = [];
+ for (var i = 0, l = required.length; i < l; ++i) {
+ var key = required[i];
+ if (!(key in options)) {
+ missing.push(key);
+ }
+ }
+ if (missing.length > 0) {
+ if (missing.length > 1) {
+ throw new Error('options ' +
+ missing.slice(0, missing.length - 1).join(', ') + ' and ' +
+ missing[missing.length - 1] + ' must be defined');
+ }
+ else throw new Error('option ' + missing[0] + ' must be defined');
+ }
+ }
+ Object.keys(options).forEach(function(key) {
+ if (key in internalValues) {
+ internalValues[key] = options[key];
+ }
+ });
+ return this;
+ };
+ this.copy = function(keys) {
+ var obj = {};
+ Object.keys(defaults).forEach(function(key) {
+ if (keys.indexOf(key) !== -1) {
+ obj[key] = values[key];
+ }
+ });
+ return obj;
+ };
+ this.read = function(filename, cb) {
+ if (typeof cb == 'function') {
+ var self = this;
+ fs.readFile(filename, function(error, data) {
+ if (error) return cb(error);
+ var conf = JSON.parse(data);
+ self.merge(conf);
+ cb();
+ });
+ }
+ else {
+ var conf = JSON.parse(fs.readFileSync(filename));
+ this.merge(conf);
+ }
+ return this;
+ };
+ this.isDefined = function(key) {
+ return typeof values[key] != 'undefined';
+ };
+ this.isDefinedAndNonNull = function(key) {
+ return typeof values[key] != 'undefined' && values[key] !== null;
+ };
+ Object.freeze(values);
+ Object.freeze(this);
+}
+
+module.exports = Options;
diff --git a/node_modules/options/package.json b/node_modules/options/package.json
new file mode 100644
index 000000000..7a62d8e3c
--- /dev/null
+++ b/node_modules/options/package.json
@@ -0,0 +1,51 @@
+{
+ "author": {
+ "name": "Einar Otto Stangvik",
+ "email": "einaros@gmail.com",
+ "url": "http://2x.io"
+ },
+ "name": "options",
+ "description": "A very light-weight in-code option parsers for node.js.",
+ "version": "0.0.6",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/einaros/options.js.git"
+ },
+ "main": "lib/options",
+ "scripts": {
+ "test": "make test"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ },
+ "dependencies": {},
+ "devDependencies": {
+ "mocha": "latest"
+ },
+ "gitHead": "ff53d0a092c897cb95964232a96fe17da65c11af",
+ "bugs": {
+ "url": "https://github.com/einaros/options.js/issues"
+ },
+ "homepage": "https://github.com/einaros/options.js",
+ "_id": "options@0.0.6",
+ "_shasum": "ec22d312806bb53e731773e7cdaefcf1c643128f",
+ "_from": "options@>=0.0.5",
+ "_npmVersion": "1.4.21",
+ "_npmUser": {
+ "name": "einaros",
+ "email": "einaros@gmail.com"
+ },
+ "maintainers": [
+ {
+ "name": "einaros",
+ "email": "einaros@gmail.com"
+ }
+ ],
+ "dist": {
+ "shasum": "ec22d312806bb53e731773e7cdaefcf1c643128f",
+ "tarball": "http://registry.npmjs.org/options/-/options-0.0.6.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/node_modules/ultron/.npmignore b/node_modules/ultron/.npmignore
new file mode 100644
index 000000000..66210a2a6
--- /dev/null
+++ b/node_modules/ultron/.npmignore
@@ -0,0 +1,3 @@
+node_modules
+coverage
+.tern-port
diff --git a/node_modules/ultron/.travis.yml b/node_modules/ultron/.travis.yml
new file mode 100644
index 000000000..a505004be
--- /dev/null
+++ b/node_modules/ultron/.travis.yml
@@ -0,0 +1,21 @@
+sudo: false
+language: node_js
+node_js:
+ - "0.12"
+ - "0.10"
+ - "0.8"
+ - "iojs"
+before_install:
+ - 'if [ "${TRAVIS_NODE_VERSION}" == "0.8" ]; then npm install -g npm@2.11.1; fi'
+script:
+ - "npm run test-travis"
+after_script:
+ - "npm install coveralls@2.11.x && cat coverage/lcov.info | coveralls"
+matrix:
+ fast_finish: true
+notifications:
+ irc:
+ channels:
+ - "irc.freenode.org#unshift"
+ on_success: change
+ on_failure: change
diff --git a/node_modules/ultron/LICENSE b/node_modules/ultron/LICENSE
new file mode 100644
index 000000000..6dc9316a6
--- /dev/null
+++ b/node_modules/ultron/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/node_modules/ultron/README.md b/node_modules/ultron/README.md
new file mode 100644
index 000000000..84fa3f238
--- /dev/null
+++ b/node_modules/ultron/README.md
@@ -0,0 +1,97 @@
+# Ultron
+
+[![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](http://img.shields.io/npm/v/ultron.svg?style=flat-square)](http://browsenpm.org/package/ultron)[![Build Status](http://img.shields.io/travis/unshiftio/ultron/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/ultron)[![Dependencies](https://img.shields.io/david/unshiftio/ultron.svg?style=flat-square)](https://david-dm.org/unshiftio/ultron)[![Coverage Status](http://img.shields.io/coveralls/unshiftio/ultron/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/ultron?branch=master)[![IRC channel](http://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](http://webchat.freenode.net/?channels=unshift)
+
+Ultron is a high-intelligence robot. It gathers intelligence so it can start
+improving upon his rudimentary design. It will learn your event emitting
+patterns and find ways to exterminate them. Allowing you to remove only the
+event emitters that **you** assigned and not the ones that your users or
+developers assigned. This can prevent race conditions, memory leaks and even file
+descriptor leaks from ever happening as you won't remove clean up processes.
+
+## Installation
+
+The module is designed to be used in browsers using browserify and in Node.js.
+You can install the module through the public npm registry by running the
+following command in CLI:
+
+```
+npm install --save ultron
+```
+
+## Usage
+
+In all examples we assume that you've required the library as following:
+
+```js
+'use strict';
+
+var Ultron = require('ultron');
+```
+
+Now that we've required the library we can construct our first `Ultron` instance.
+The constructor requires one argument which should be the `EventEmitter`
+instance that we need to operate upon. This can be the `EventEmitter` module
+that ships with Node.js or `EventEmitter3` or anything else as long as it
+follow the same API and internal structure as these 2. So with that in mind we
+can create the instance:
+
+```js
+//
+// For the sake of this example we're going to construct an empty EventEmitter
+//
+var EventEmitter = require('events').EventEmitter; // or require('eventmitter3');
+var events = new EventEmitter();
+
+var ultron = new Ultron(events);
+```
+
+You can now use the following API's from the Ultron instance:
+
+### Ultron.on
+
+Register a new event listener for the given event. It follows the exact same API
+as `EventEmitter.on` but it will return itself instead of returning the
+EventEmitter instance. If you are using EventEmitter3 it also supports the
+context param:
+
+```js
+ultron.on('event-name', handler, { custom: 'function context' });
+```
+
+### Ultron.once
+
+Exactly the same as the [Ultron.on](#ultronon) but it only allows the execution
+once.
+
+### Ultron.remove
+
+This is where all the magic happens and the safe removal starts. This function
+accepts different argument styles:
+
+- No arguments, assume that all events need to be removed so it will work as
+ `removeAllListeners()` API.
+- 1 argument, when it's a string it will be split on ` ` and `,` to create a
+ list of events that need to be cleared.
+- Multiple arguments, we assume that they are all names of events that need to
+ be cleared.
+
+```js
+ultron.remove('foo, bar baz'); // Removes foo, bar and baz.
+ultron.remove('foo', 'bar', 'baz'); // Removes foo, bar and baz.
+ultron.remove(); // Removes everything.
+```
+
+If you just want to remove a single event listener using a function reference
+you can still use the EventEmitter's `removeListener(event, fn)` API:
+
+```js
+function foo() {}
+
+ulton.on('foo', foo);
+events.removeListener('foo', foo);
+```
+
+## License
+
+MIT
diff --git a/node_modules/ultron/index.js b/node_modules/ultron/index.js
new file mode 100644
index 000000000..af17ab7cc
--- /dev/null
+++ b/node_modules/ultron/index.js
@@ -0,0 +1,129 @@
+'use strict';
+
+var has = Object.prototype.hasOwnProperty;
+
+/**
+ * An auto incrementing id which we can use to create "unique" Ultron instances
+ * so we can track the event emitters that are added through the Ultron
+ * interface.
+ *
+ * @type {Number}
+ * @private
+ */
+var id = 0;
+
+/**
+ * Ultron is high-intelligence robot. It gathers intelligence so it can start improving
+ * upon his rudimentary design. It will learn from your EventEmitting patterns
+ * and exterminate them.
+ *
+ * @constructor
+ * @param {EventEmitter} ee EventEmitter instance we need to wrap.
+ * @api public
+ */
+function Ultron(ee) {
+ if (!(this instanceof Ultron)) return new Ultron(ee);
+
+ this.id = id++;
+ this.ee = ee;
+}
+
+/**
+ * Register a new EventListener for the given event.
+ *
+ * @param {String} event Name of the event.
+ * @param {Functon} fn Callback function.
+ * @param {Mixed} context The context of the function.
+ * @returns {Ultron}
+ * @api public
+ */
+Ultron.prototype.on = function on(event, fn, context) {
+ fn.__ultron = this.id;
+ this.ee.on(event, fn, context);
+
+ return this;
+};
+/**
+ * Add an EventListener that's only called once.
+ *
+ * @param {String} event Name of the event.
+ * @param {Function} fn Callback function.
+ * @param {Mixed} context The context of the function.
+ * @returns {Ultron}
+ * @api public
+ */
+Ultron.prototype.once = function once(event, fn, context) {
+ fn.__ultron = this.id;
+ this.ee.once(event, fn, context);
+
+ return this;
+};
+
+/**
+ * Remove the listeners we assigned for the given event.
+ *
+ * @returns {Ultron}
+ * @api public
+ */
+Ultron.prototype.remove = function remove() {
+ var args = arguments
+ , event;
+
+ //
+ // When no event names are provided we assume that we need to clear all the
+ // events that were assigned through us.
+ //
+ if (args.length === 1 && 'string' === typeof args[0]) {
+ args = args[0].split(/[, ]+/);
+ } else if (!args.length) {
+ args = [];
+
+ for (event in this.ee._events) {
+ if (has.call(this.ee._events, event)) args.push(event);
+ }
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ var listeners = this.ee.listeners(args[i]);
+
+ for (var j = 0; j < listeners.length; j++) {
+ event = listeners[j];
+
+ //
+ // Once listeners have a `listener` property that stores the real listener
+ // in the EventEmitter that ships with Node.js.
+ //
+ if (event.listener) {
+ if (event.listener.__ultron !== this.id) continue;
+ delete event.listener.__ultron;
+ } else {
+ if (event.__ultron !== this.id) continue;
+ delete event.__ultron;
+ }
+
+ this.ee.removeListener(args[i], event);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Destroy the Ultron instance, remove all listeners and release all references.
+ *
+ * @returns {Boolean}
+ * @api public
+ */
+Ultron.prototype.destroy = function destroy() {
+ if (!this.ee) return false;
+
+ this.remove();
+ this.ee = null;
+
+ return true;
+};
+
+//
+// Expose the module.
+//
+module.exports = Ultron;
diff --git a/node_modules/ultron/package.json b/node_modules/ultron/package.json
new file mode 100644
index 000000000..d68bc5882
--- /dev/null
+++ b/node_modules/ultron/package.json
@@ -0,0 +1,74 @@
+{
+ "name": "ultron",
+ "version": "1.0.2",
+ "description": "Ultron is high-intelligence robot. It gathers intel so it can start improving upon his rudimentary design",
+ "main": "index.js",
+ "scripts": {
+ "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100",
+ "test": "mocha test.js",
+ "watch": "mocha --watch test.js",
+ "coverage": "istanbul cover ./node_modules/.bin/_mocha -- test.js",
+ "test-travis": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/unshiftio/ultron.git"
+ },
+ "keywords": [
+ "Ultron",
+ "robot",
+ "gather",
+ "intelligence",
+ "event",
+ "events",
+ "eventemitter",
+ "emitter",
+ "cleanup"
+ ],
+ "author": {
+ "name": "Arnout Kazemier"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "assume": "1.2.x",
+ "eventemitter3": "1.1.x",
+ "istanbul": "0.3.x",
+ "mocha": "2.2.x",
+ "pre-commit": "1.0.x"
+ },
+ "bugs": {
+ "url": "https://github.com/unshiftio/ultron/issues"
+ },
+ "homepage": "https://github.com/unshiftio/ultron",
+ "gitHead": "a10482ae98a09120821545456c90c6d60d540f7c",
+ "_id": "ultron@1.0.2",
+ "_shasum": "ace116ab557cd197386a4e88f4685378c8b2e4fa",
+ "_from": "ultron@>=1.0.0 <1.1.0",
+ "_npmVersion": "2.9.1",
+ "_nodeVersion": "0.12.3",
+ "_npmUser": {
+ "name": "3rdeden",
+ "email": "npm@3rd-Eden.com"
+ },
+ "maintainers": [
+ {
+ "name": "unshift",
+ "email": "npm@unshift.io"
+ },
+ {
+ "name": "v1",
+ "email": "info@3rd-Eden.com"
+ },
+ {
+ "name": "3rdeden",
+ "email": "npm@3rd-Eden.com"
+ }
+ ],
+ "dist": {
+ "shasum": "ace116ab557cd197386a4e88f4685378c8b2e4fa",
+ "tarball": "http://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/node_modules/ultron/test.js b/node_modules/ultron/test.js
new file mode 100644
index 000000000..1fd4f1bb5
--- /dev/null
+++ b/node_modules/ultron/test.js
@@ -0,0 +1,327 @@
+/* istanbul ignore next */
+describe('Ultron', function () {
+ 'use strict';
+
+ var EventEmitter = require('eventemitter3')
+ , EE = require('events').EventEmitter
+ , assume = require('assume')
+ , Ultron = require('./')
+ , ultron
+ , ee;
+
+ beforeEach(function () {
+ ee = new EventEmitter();
+ ultron = new Ultron(ee);
+ });
+
+ afterEach(function () {
+ ultron.destroy();
+ ee.removeAllListeners();
+ });
+
+ it('is exposed as a function', function () {
+ assume(Ultron).is.a('function');
+ });
+
+ it('can be initialized without the new keyword', function () {
+ assume(Ultron(ee)).is.instanceOf(Ultron);
+ });
+
+ it('assigns a unique id to every instance', function () {
+ for (var i = 0; i < 100; i++) {
+ assume(ultron.id).does.not.equal((new Ultron()).id);
+ }
+ });
+
+ it('allows removal through the event emitter', function () {
+ function foo() {}
+ function bar() {}
+
+ ultron.on('foo', foo);
+ ultron.once('foo', bar);
+
+ assume(foo.__ultron).equals(ultron.id);
+ assume(bar.__ultron).equals(ultron.id);
+ assume(ee.listeners('foo').length).equals(2);
+
+ ee.removeListener('foo', foo);
+ assume(ee.listeners('foo').length).equals(1);
+
+ ee.removeListener('foo', bar);
+ assume(ee.listeners('foo').length).equals(0);
+ });
+
+ describe('#on', function () {
+ it('assigns a listener', function () {
+ assume(ee.listeners('foo').length).equals(0);
+
+ function foo() {}
+
+ ultron.on('foo', foo);
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('foo')[0]).equals(foo);
+ });
+
+ it('tags the assigned function', function () {
+ assume(ee.listeners('foo').length).equals(0);
+
+ ultron.on('foo', function () {});
+ assume(ee.listeners('foo')[0].__ultron).equals(ultron.id);
+ });
+
+ it('also passes in the context', function (next) {
+ var context = 1313;
+
+ ultron.on('foo', function (a, b, c) {
+ assume(a).equals('a');
+ assume(b).equals('b');
+ assume(c).equals('c');
+
+ assume(this).equals(context);
+
+ next();
+ }, context);
+
+ ee.emit('foo', 'a', 'b', 'c');
+ });
+
+ it('works with regular eventemitters as well', function (next) {
+ var ee = new EE()
+ , ultron = new Ultron(ee);
+
+ ultron.on('foo', function (a, b, c) {
+ assume(a).equals('a');
+ assume(b).equals('b');
+ assume(c).equals('c');
+
+ next();
+ });
+
+ ee.emit('foo', 'a', 'b', 'c');
+ });
+ });
+
+ describe('#once', function () {
+ it('assigns a listener', function () {
+ assume(ee.listeners('foo').length).equals(0);
+
+ function foo() {}
+ ultron.once('foo', foo);
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('foo')[0]).equals(foo);
+ });
+
+ it('tags the assigned function', function () {
+ assume(ee.listeners('foo').length).equals(0);
+
+ ultron.once('foo', function () {});
+ assume(ee.listeners('foo')[0].__ultron).equals(ultron.id);
+ });
+
+ it('also passes in the context', function (next) {
+ var context = 1313;
+
+ ultron.once('foo', function (a, b, c) {
+ assume(a).equals('a');
+ assume(b).equals('b');
+ assume(c).equals('c');
+
+ assume(this).equals(context);
+
+ next();
+ }, context);
+
+ ee.emit('foo', 'a', 'b', 'c');
+ ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute
+ });
+
+ it('works with regular eventemitters as well', function (next) {
+ var ee = new EE()
+ , ultron = new Ultron(ee);
+
+ ultron.once('foo', function (a, b, c) {
+ assume(a).equals('a');
+ assume(b).equals('b');
+ assume(c).equals('c');
+
+ next();
+ });
+
+ ee.emit('foo', 'a', 'b', 'c');
+ ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute
+ });
+ });
+
+ describe('#remove', function () {
+ it('removes only our assigned `on` listeners', function () {
+ function foo() {}
+ function bar() {}
+
+ ee.on('foo', foo);
+ ultron.on('foo', bar);
+ assume(ee.listeners('foo').length).equals(2);
+
+ ultron.remove('foo');
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('foo')[0]).equals(foo);
+ });
+
+ it('removes our private __ultron references', function () {
+ function once() {}
+ function on() {}
+
+ assume('__ultron' in once).is.false();
+ assume('__ultron' in on).is.false();
+
+ ultron.on('foo', on);
+ ultron.once('bar', once);
+
+ assume('__ultron' in once).is.true();
+ assume('__ultron' in on).is.true();
+
+ ultron.remove('foo, bar');
+
+ assume('__ultron' in once).is.false();
+ assume('__ultron' in on).is.false();
+
+ ultron.destroy();
+
+ ee = new EE();
+ ultron = new Ultron(ee);
+
+ assume('__ultron' in once).is.false();
+ assume('__ultron' in on).is.false();
+
+ ultron.on('foo', on);
+ ultron.once('bar', once);
+
+ assume('__ultron' in once).is.true();
+ assume('__ultron' in on).is.true();
+
+ ultron.remove('foo, bar');
+
+ assume('__ultron' in once).is.false();
+ assume('__ultron' in on).is.false();
+ });
+
+ it('removes only our assigned `once` listeners', function () {
+ function foo() {}
+ function bar() {}
+
+ ee.once('foo', foo);
+ ultron.once('foo', bar);
+ assume(ee.listeners('foo').length).equals(2);
+
+ ultron.remove('foo');
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('foo')[0]).equals(foo);
+ });
+
+ it('removes only our assigned `once` listeners from regular EE', function () {
+ var ee = new EE()
+ , ultron = new Ultron(ee);
+
+ function foo() {}
+ function bar() {}
+
+ ee.once('foo', foo);
+ ultron.once('foo', bar);
+ assume(ee.listeners('foo').length).equals(2);
+
+ ultron.remove('foo');
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('foo')[0].listener).equals(foo);
+ });
+
+ it('removes all assigned events if called without args', function () {
+ function foo() {}
+ function bar() {}
+
+ ultron.on('foo', foo);
+ ultron.on('bar', bar);
+
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('bar').length).equals(1);
+
+ ultron.remove();
+
+ assume(ee.listeners('foo').length).equals(0);
+ assume(ee.listeners('bar').length).equals(0);
+ });
+
+ it('removes multiple listeners based on args', function () {
+ function foo() {}
+ function bar() {}
+ function baz() {}
+
+ ultron.on('foo', foo);
+ ultron.on('bar', bar);
+ ultron.on('baz', baz);
+
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('bar').length).equals(1);
+ assume(ee.listeners('baz').length).equals(1);
+
+ ultron.remove('foo', 'bar');
+
+ assume(ee.listeners('foo').length).equals(0);
+ assume(ee.listeners('bar').length).equals(0);
+ assume(ee.listeners('baz').length).equals(1);
+ });
+
+ it('removes multiple listeners if first arg is seperated string', function () {
+ function foo() {}
+ function bar() {}
+ function baz() {}
+
+ ultron.on('foo', foo);
+ ultron.on('bar', bar);
+ ultron.on('baz', baz);
+
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('bar').length).equals(1);
+ assume(ee.listeners('baz').length).equals(1);
+
+ ultron.remove('foo, bar');
+
+ assume(ee.listeners('foo').length).equals(0);
+ assume(ee.listeners('bar').length).equals(0);
+ assume(ee.listeners('baz').length).equals(1);
+ });
+ });
+
+ describe('#destroy', function () {
+ it('removes all listeners', function () {
+ function foo() {}
+ function bar() {}
+ function baz() {}
+
+ ultron.on('foo', foo);
+ ultron.on('bar', bar);
+ ultron.on('baz', baz);
+
+ assume(ee.listeners('foo').length).equals(1);
+ assume(ee.listeners('bar').length).equals(1);
+ assume(ee.listeners('baz').length).equals(1);
+
+ ultron.destroy();
+
+ assume(ee.listeners('foo').length).equals(0);
+ assume(ee.listeners('bar').length).equals(0);
+ assume(ee.listeners('baz').length).equals(0);
+ });
+
+ it('removes the .ee reference', function () {
+ assume(ultron.ee).equals(ee);
+ ultron.destroy();
+ assume(ultron.ee).equals(null);
+ });
+
+ it('returns booleans for state indication', function () {
+ assume(ultron.destroy()).is.true();
+ assume(ultron.destroy()).is.false();
+ assume(ultron.destroy()).is.false();
+ assume(ultron.destroy()).is.false();
+ });
+ });
+});
diff --git a/node_modules/ws/.npmignore b/node_modules/ws/.npmignore
new file mode 100644
index 000000000..1eba800f8
--- /dev/null
+++ b/node_modules/ws/.npmignore
@@ -0,0 +1,11 @@
+npm-debug.log
+node_modules
+.*.swp
+.lock-*
+build
+
+bench
+doc
+examples
+test
+
diff --git a/node_modules/ws/.travis.yml b/node_modules/ws/.travis.yml
new file mode 100644
index 000000000..5002b4984
--- /dev/null
+++ b/node_modules/ws/.travis.yml
@@ -0,0 +1,15 @@
+language: node_js
+sudo: false
+node_js:
+ - "5"
+ - "4"
+ - "0.12"
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - gcc-4.9
+ - g++-4.9
+before_install:
+ - export CC="gcc-4.9" CXX="g++-4.9"
diff --git a/node_modules/ws/Makefile b/node_modules/ws/Makefile
new file mode 100644
index 000000000..00f19fa01
--- /dev/null
+++ b/node_modules/ws/Makefile
@@ -0,0 +1,40 @@
+ALL_TESTS = $(shell find test/ -name '*.test.js')
+ALL_INTEGRATION = $(shell find test/ -name '*.integration.js')
+
+all:
+ node-gyp configure build
+
+clean:
+ node-gyp clean
+
+run-tests:
+ @./node_modules/.bin/mocha \
+ -t 5000 \
+ -s 2400 \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+run-integrationtests:
+ @./node_modules/.bin/mocha \
+ -t 5000 \
+ -s 6000 \
+ $(TESTFLAGS) \
+ $(TESTS)
+
+test:
+ @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
+
+integrationtest:
+ @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests
+
+benchmark:
+ @node bench/sender.benchmark.js
+ @node bench/parser.benchmark.js
+
+autobahn:
+ @NODE_PATH=lib node test/autobahn.js
+
+autobahn-server:
+ @NODE_PATH=lib node test/autobahn-server.js
+
+.PHONY: test
diff --git a/node_modules/ws/README.md b/node_modules/ws/README.md
new file mode 100644
index 000000000..9be2e51d9
--- /dev/null
+++ b/node_modules/ws/README.md
@@ -0,0 +1,242 @@
+# ws: a node.js websocket library
+
+[![Build Status](https://travis-ci.org/websockets/ws.svg?branch=master)](https://travis-ci.org/websockets/ws)
+
+`ws` is a simple to use WebSocket implementation, up-to-date against RFC-6455,
+and [probably the fastest WebSocket library for node.js][archive].
+
+Passes the quite extensive Autobahn test suite. See http://websockets.github.com/ws
+for the full reports.
+
+## Protocol support
+
+* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera.
+ Added to ws version 0.4.2, but server only. Can be disabled by setting the
+ `disableHixie` option to true.)
+* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
+* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`)
+
+### Installing
+
+```
+npm install --save ws
+```
+
+### Opt-in for performance
+
+There are 2 optional modules that can be installed along side with the `ws`
+module. These modules are binary addons which improve certain operations, but as
+they are binary addons they require compilation which can fail if no c++
+compiler is installed on the host system.
+
+- `npm install --save bufferutil`: Improves internal buffer operations which
+ allows for faster processing of masked WebSocket frames and general buffer
+ operations.
+- `npm install --save utf-8-validate`: The specification requires validation of
+ invalid UTF-8 chars, some of these validations could not be done in JavaScript
+ hence the need for a binary addon. In most cases you will already be
+ validating the input that you receive for security purposes leading to double
+ validation. But if you want to be 100% spec conform and fast validation of UTF-8
+ then this module is a must.
+
+### Sending and receiving text data
+
+```js
+var WebSocket = require('ws');
+var ws = new WebSocket('ws://www.host.com/path');
+
+ws.on('open', function open() {
+ ws.send('something');
+});
+
+ws.on('message', function(data, flags) {
+ // flags.binary will be set if a binary data is received.
+ // flags.masked will be set if the data was masked.
+});
+```
+
+### Sending binary data
+
+```js
+var WebSocket = require('ws');
+var ws = new WebSocket('ws://www.host.com/path');
+
+ws.on('open', function open() {
+ var array = new Float32Array(5);
+
+ for (var i = 0; i < array.length; ++i) {
+ array[i] = i / 2;
+ }
+
+ ws.send(array, { binary: true, mask: true });
+});
+```
+
+Setting `mask`, as done for the send options above, will cause the data to be
+masked according to the WebSocket protocol. The same option applies for text
+data.
+
+### Server example
+
+```js
+var WebSocketServer = require('ws').Server
+ , wss = new WebSocketServer({ port: 8080 });
+
+wss.on('connection', function connection(ws) {
+ ws.on('message', function incoming(message) {
+ console.log('received: %s', message);
+ });
+
+ ws.send('something');
+});
+```
+
+### ExpressJS example
+
+```js
+var server = require('http').createServer()
+ , url = require('url')
+ , WebSocketServer = require('ws').Server
+ , wss = new WebSocketServer({ server: server })
+ , express = require('express')
+ , app = express()
+ , port = 4080;
+
+app.use(function (req, res) {
+ res.send({ msg: "hello" });
+});
+
+wss.on('connection', function connection(ws) {
+ var location = url.parse(ws.upgradeReq.url, true);
+ // you might use location.query.access_token to authenticate or share sessions
+ // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
+
+ ws.on('message', function incoming(message) {
+ console.log('received: %s', message);
+ });
+
+ ws.send('something');
+});
+
+server.on('request', app);
+server.listen(port, function () { console.log('Listening on ' + server.address().port) });
+```
+
+### Server sending broadcast data
+
+```js
+var WebSocketServer = require('ws').Server
+ , wss = new WebSocketServer({ port: 8080 });
+
+wss.broadcast = function broadcast(data) {
+ wss.clients.forEach(function each(client) {
+ client.send(data);
+ });
+};
+```
+
+### Error handling best practices
+
+```js
+// If the WebSocket is closed before the following send is attempted
+ws.send('something');
+
+// Errors (both immediate and async write errors) can be detected in an optional
+// callback. The callback is also the only way of being notified that data has
+// actually been sent.
+ws.send('something', function ack(error) {
+ // if error is not defined, the send has been completed,
+ // otherwise the error object will indicate what failed.
+});
+
+// Immediate errors can also be handled with try/catch-blocks, but **note** that
+// since sends are inherently asynchronous, socket write failures will *not* be
+// captured when this technique is used.
+try { ws.send('something'); }
+catch (e) { /* handle error */ }
+```
+
+### echo.websocket.org demo
+
+```js
+var WebSocket = require('ws');
+var ws = new WebSocket('ws://echo.websocket.org/', {
+ protocolVersion: 8,
+ origin: 'http://websocket.org'
+});
+
+ws.on('open', function open() {
+ console.log('connected');
+ ws.send(Date.now().toString(), {mask: true});
+});
+
+ws.on('close', function close() {
+ console.log('disconnected');
+});
+
+ws.on('message', function message(data, flags) {
+ console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags);
+
+ setTimeout(function timeout() {
+ ws.send(Date.now().toString(), {mask: true});
+ }, 500);
+});
+```
+
+### Browserify users
+When including ws via a browserify bundle, ws returns global.WebSocket which has slightly different API.
+You should use the standard WebSockets API instead.
+
+https://developer.mozilla.org/en-US/docs/WebSockets/Writing_WebSocket_client_applications#Availability_of_WebSockets
+
+
+### Other examples
+
+For a full example with a browser client communicating with a ws server, see the
+examples folder.
+
+Note that the usage together with Express 3.0 is quite different from Express
+2.x. The difference is expressed in the two different serverstats-examples.
+
+Otherwise, see the test cases.
+
+### Running the tests
+
+```
+make test
+```
+
+## API Docs
+
+See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md) for Node.js-like docs for the ws classes.
+
+## Changelog
+
+We're using the GitHub [`releases`](https://github.com/websockets/ws/releases) for changelog entries.
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+[archive]: http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs
diff --git a/node_modules/ws/index.js b/node_modules/ws/index.js
new file mode 100644
index 000000000..a7e8644b9
--- /dev/null
+++ b/node_modules/ws/index.js
@@ -0,0 +1,49 @@
+'use strict';
+
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var WS = module.exports = require('./lib/WebSocket');
+
+WS.Server = require('./lib/WebSocketServer');
+WS.Sender = require('./lib/Sender');
+WS.Receiver = require('./lib/Receiver');
+
+/**
+ * Create a new WebSocket server.
+ *
+ * @param {Object} options Server options
+ * @param {Function} fn Optional connection listener.
+ * @returns {WS.Server}
+ * @api public
+ */
+WS.createServer = function createServer(options, fn) {
+ var server = new WS.Server(options);
+
+ if (typeof fn === 'function') {
+ server.on('connection', fn);
+ }
+
+ return server;
+};
+
+/**
+ * Create a new WebSocket connection.
+ *
+ * @param {String} address The URL/address we need to connect to.
+ * @param {Function} fn Open listener.
+ * @returns {WS}
+ * @api public
+ */
+WS.connect = WS.createConnection = function connect(address, fn) {
+ var client = new WS(address);
+
+ if (typeof fn === 'function') {
+ client.on('open', fn);
+ }
+
+ return client;
+};
diff --git a/node_modules/ws/lib/BufferPool.js b/node_modules/ws/lib/BufferPool.js
new file mode 100644
index 000000000..8ee599057
--- /dev/null
+++ b/node_modules/ws/lib/BufferPool.js
@@ -0,0 +1,63 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var util = require('util');
+
+function BufferPool(initialSize, growStrategy, shrinkStrategy) {
+ if (this instanceof BufferPool === false) {
+ throw new TypeError("Classes can't be function-called");
+ }
+
+ if (typeof initialSize === 'function') {
+ shrinkStrategy = growStrategy;
+ growStrategy = initialSize;
+ initialSize = 0;
+ }
+ else if (typeof initialSize === 'undefined') {
+ initialSize = 0;
+ }
+ this._growStrategy = (growStrategy || function(db, size) {
+ return db.used + size;
+ }).bind(null, this);
+ this._shrinkStrategy = (shrinkStrategy || function(db) {
+ return initialSize;
+ }).bind(null, this);
+ this._buffer = initialSize ? new Buffer(initialSize) : null;
+ this._offset = 0;
+ this._used = 0;
+ this._changeFactor = 0;
+ this.__defineGetter__('size', function(){
+ return this._buffer == null ? 0 : this._buffer.length;
+ });
+ this.__defineGetter__('used', function(){
+ return this._used;
+ });
+}
+
+BufferPool.prototype.get = function(length) {
+ if (this._buffer == null || this._offset + length > this._buffer.length) {
+ var newBuf = new Buffer(this._growStrategy(length));
+ this._buffer = newBuf;
+ this._offset = 0;
+ }
+ this._used += length;
+ var buf = this._buffer.slice(this._offset, this._offset + length);
+ this._offset += length;
+ return buf;
+}
+
+BufferPool.prototype.reset = function(forceNewBuffer) {
+ var len = this._shrinkStrategy();
+ if (len < this.size) this._changeFactor -= 1;
+ if (forceNewBuffer || this._changeFactor < -2) {
+ this._changeFactor = 0;
+ this._buffer = len ? new Buffer(len) : null;
+ }
+ this._offset = 0;
+ this._used = 0;
+}
+
+module.exports = BufferPool;
diff --git a/node_modules/ws/lib/BufferUtil.fallback.js b/node_modules/ws/lib/BufferUtil.fallback.js
new file mode 100644
index 000000000..508542c9e
--- /dev/null
+++ b/node_modules/ws/lib/BufferUtil.fallback.js
@@ -0,0 +1,47 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+module.exports.BufferUtil = {
+ merge: function(mergedBuffer, buffers) {
+ var offset = 0;
+ for (var i = 0, l = buffers.length; i < l; ++i) {
+ var buf = buffers[i];
+ buf.copy(mergedBuffer, offset);
+ offset += buf.length;
+ }
+ },
+ mask: function(source, mask, output, offset, length) {
+ var maskNum = mask.readUInt32LE(0, true);
+ var i = 0;
+ for (; i < length - 3; i += 4) {
+ var num = maskNum ^ source.readUInt32LE(i, true);
+ if (num < 0) num = 4294967296 + num;
+ output.writeUInt32LE(num, offset + i, true);
+ }
+ switch (length % 4) {
+ case 3: output[offset + i + 2] = source[i + 2] ^ mask[2];
+ case 2: output[offset + i + 1] = source[i + 1] ^ mask[1];
+ case 1: output[offset + i] = source[i] ^ mask[0];
+ case 0:;
+ }
+ },
+ unmask: function(data, mask) {
+ var maskNum = mask.readUInt32LE(0, true);
+ var length = data.length;
+ var i = 0;
+ for (; i < length - 3; i += 4) {
+ var num = maskNum ^ data.readUInt32LE(i, true);
+ if (num < 0) num = 4294967296 + num;
+ data.writeUInt32LE(num, i, true);
+ }
+ switch (length % 4) {
+ case 3: data[i + 2] = data[i + 2] ^ mask[2];
+ case 2: data[i + 1] = data[i + 1] ^ mask[1];
+ case 1: data[i] = data[i] ^ mask[0];
+ case 0:;
+ }
+ }
+}
diff --git a/node_modules/ws/lib/BufferUtil.js b/node_modules/ws/lib/BufferUtil.js
new file mode 100644
index 000000000..18c699894
--- /dev/null
+++ b/node_modules/ws/lib/BufferUtil.js
@@ -0,0 +1,13 @@
+'use strict';
+
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+try {
+ module.exports = require('bufferutil');
+} catch (e) {
+ module.exports = require('./BufferUtil.fallback');
+}
diff --git a/node_modules/ws/lib/ErrorCodes.js b/node_modules/ws/lib/ErrorCodes.js
new file mode 100644
index 000000000..55ebd529b
--- /dev/null
+++ b/node_modules/ws/lib/ErrorCodes.js
@@ -0,0 +1,24 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+module.exports = {
+ isValidErrorCode: function(code) {
+ return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) ||
+ (code >= 3000 && code <= 4999);
+ },
+ 1000: 'normal',
+ 1001: 'going away',
+ 1002: 'protocol error',
+ 1003: 'unsupported data',
+ 1004: 'reserved',
+ 1005: 'reserved for extensions',
+ 1006: 'reserved for extensions',
+ 1007: 'inconsistent or invalid data',
+ 1008: 'policy violation',
+ 1009: 'message too big',
+ 1010: 'extension handshake missing',
+ 1011: 'an unexpected condition prevented the request from being fulfilled',
+};
\ No newline at end of file
diff --git a/node_modules/ws/lib/Extensions.js b/node_modules/ws/lib/Extensions.js
new file mode 100644
index 000000000..a465ace2b
--- /dev/null
+++ b/node_modules/ws/lib/Extensions.js
@@ -0,0 +1,70 @@
+
+var util = require('util');
+
+/**
+ * Module exports.
+ */
+
+exports.parse = parse;
+exports.format = format;
+
+/**
+ * Parse extensions header value
+ */
+
+function parse(value) {
+ value = value || '';
+
+ var extensions = {};
+
+ value.split(',').forEach(function(v) {
+ var params = v.split(';');
+ var token = params.shift().trim();
+ var paramsList = extensions[token] = extensions[token] || [];
+ var parsedParams = {};
+
+ params.forEach(function(param) {
+ var parts = param.trim().split('=');
+ var key = parts[0];
+ var value = parts[1];
+ if (typeof value === 'undefined') {
+ value = true;
+ } else {
+ // unquote value
+ if (value[0] === '"') {
+ value = value.slice(1);
+ }
+ if (value[value.length - 1] === '"') {
+ value = value.slice(0, value.length - 1);
+ }
+ }
+ (parsedParams[key] = parsedParams[key] || []).push(value);
+ });
+
+ paramsList.push(parsedParams);
+ });
+
+ return extensions;
+}
+
+/**
+ * Format extensions header value
+ */
+
+function format(value) {
+ return Object.keys(value).map(function(token) {
+ var paramsList = value[token];
+ if (!util.isArray(paramsList)) {
+ paramsList = [paramsList];
+ }
+ return paramsList.map(function(params) {
+ return [token].concat(Object.keys(params).map(function(k) {
+ var p = params[k];
+ if (!util.isArray(p)) p = [p];
+ return p.map(function(v) {
+ return v === true ? k : k + '=' + v;
+ }).join('; ');
+ })).join('; ');
+ }).join(', ');
+ }).join(', ');
+}
diff --git a/node_modules/ws/lib/PerMessageDeflate.js b/node_modules/ws/lib/PerMessageDeflate.js
new file mode 100644
index 000000000..5324bd8e6
--- /dev/null
+++ b/node_modules/ws/lib/PerMessageDeflate.js
@@ -0,0 +1,325 @@
+
+var zlib = require('zlib');
+
+var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
+var DEFAULT_WINDOW_BITS = 15;
+var DEFAULT_MEM_LEVEL = 8;
+
+PerMessageDeflate.extensionName = 'permessage-deflate';
+
+/**
+ * Per-message Compression Extensions implementation
+ */
+
+function PerMessageDeflate(options, isServer) {
+ if (this instanceof PerMessageDeflate === false) {
+ throw new TypeError("Classes can't be function-called");
+ }
+
+ this._options = options || {};
+ this._isServer = !!isServer;
+ this._inflate = null;
+ this._deflate = null;
+ this.params = null;
+}
+
+/**
+ * Create extension parameters offer
+ *
+ * @api public
+ */
+
+PerMessageDeflate.prototype.offer = function() {
+ var params = {};
+ if (this._options.serverNoContextTakeover) {
+ params.server_no_context_takeover = true;
+ }
+ if (this._options.clientNoContextTakeover) {
+ params.client_no_context_takeover = true;
+ }
+ if (this._options.serverMaxWindowBits) {
+ params.server_max_window_bits = this._options.serverMaxWindowBits;
+ }
+ if (this._options.clientMaxWindowBits) {
+ params.client_max_window_bits = this._options.clientMaxWindowBits;
+ } else if (this._options.clientMaxWindowBits == null) {
+ params.client_max_window_bits = true;
+ }
+ return params;
+};
+
+/**
+ * Accept extension offer
+ *
+ * @api public
+ */
+
+PerMessageDeflate.prototype.accept = function(paramsList) {
+ paramsList = this.normalizeParams(paramsList);
+
+ var params;
+ if (this._isServer) {
+ params = this.acceptAsServer(paramsList);
+ } else {
+ params = this.acceptAsClient(paramsList);
+ }
+
+ this.params = params;
+ return params;
+};
+
+/**
+ * Releases all resources used by the extension
+ *
+ * @api public
+ */
+
+PerMessageDeflate.prototype.cleanup = function() {
+ if (this._inflate) {
+ if (this._inflate.writeInProgress) {
+ this._inflate.pendingClose = true;
+ } else {
+ if (this._inflate.close) this._inflate.close();
+ this._inflate = null;
+ }
+ }
+ if (this._deflate) {
+ if (this._deflate.writeInProgress) {
+ this._deflate.pendingClose = true;
+ } else {
+ if (this._deflate.close) this._deflate.close();
+ this._deflate = null;
+ }
+ }
+};
+
+/**
+ * Accept extension offer from client
+ *
+ * @api private
+ */
+
+PerMessageDeflate.prototype.acceptAsServer = function(paramsList) {
+ var accepted = {};
+ var result = paramsList.some(function(params) {
+ accepted = {};
+ if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) {
+ return;
+ }
+ if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) {
+ return;
+ }
+ if (typeof this._options.serverMaxWindowBits === 'number' &&
+ typeof params.server_max_window_bits === 'number' &&
+ this._options.serverMaxWindowBits > params.server_max_window_bits) {
+ return;
+ }
+ if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) {
+ return;
+ }
+
+ if (this._options.serverNoContextTakeover || params.server_no_context_takeover) {
+ accepted.server_no_context_takeover = true;
+ }
+ if (this._options.clientNoContextTakeover) {
+ accepted.client_no_context_takeover = true;
+ }
+ if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) {
+ accepted.client_no_context_takeover = true;
+ }
+ if (typeof this._options.serverMaxWindowBits === 'number') {
+ accepted.server_max_window_bits = this._options.serverMaxWindowBits;
+ } else if (typeof params.server_max_window_bits === 'number') {
+ accepted.server_max_window_bits = params.server_max_window_bits;
+ }
+ if (typeof this._options.clientMaxWindowBits === 'number') {
+ accepted.client_max_window_bits = this._options.clientMaxWindowBits;
+ } else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') {
+ accepted.client_max_window_bits = params.client_max_window_bits;
+ }
+ return true;
+ }, this);
+
+ if (!result) {
+ throw new Error('Doesn\'t support the offered configuration');
+ }
+
+ return accepted;
+};
+
+/**
+ * Accept extension response from server
+ *
+ * @api privaye
+ */
+
+PerMessageDeflate.prototype.acceptAsClient = function(paramsList) {
+ var params = paramsList[0];
+ if (this._options.clientNoContextTakeover != null) {
+ if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) {
+ throw new Error('Invalid value for "client_no_context_takeover"');
+ }
+ }
+ if (this._options.clientMaxWindowBits != null) {
+ if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) {
+ throw new Error('Invalid value for "client_max_window_bits"');
+ }
+ if (typeof this._options.clientMaxWindowBits === 'number' &&
+ (!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) {
+ throw new Error('Invalid value for "client_max_window_bits"');
+ }
+ }
+ return params;
+};
+
+/**
+ * Normalize extensions parameters
+ *
+ * @api private
+ */
+
+PerMessageDeflate.prototype.normalizeParams = function(paramsList) {
+ return paramsList.map(function(params) {
+ Object.keys(params).forEach(function(key) {
+ var value = params[key];
+ if (value.length > 1) {
+ throw new Error('Multiple extension parameters for ' + key);
+ }
+
+ value = value[0];
+
+ switch (key) {
+ case 'server_no_context_takeover':
+ case 'client_no_context_takeover':
+ if (value !== true) {
+ throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
+ }
+ params[key] = true;
+ break;
+ case 'server_max_window_bits':
+ case 'client_max_window_bits':
+ if (typeof value === 'string') {
+ value = parseInt(value, 10);
+ if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) {
+ throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
+ }
+ }
+ if (!this._isServer && value === true) {
+ throw new Error('Missing extension parameter value for ' + key);
+ }
+ params[key] = value;
+ break;
+ default:
+ throw new Error('Not defined extension parameter (' + key + ')');
+ }
+ }, this);
+ return params;
+ }, this);
+};
+
+/**
+ * Decompress message
+ *
+ * @api public
+ */
+
+PerMessageDeflate.prototype.decompress = function (data, fin, callback) {
+ var endpoint = this._isServer ? 'client' : 'server';
+
+ if (!this._inflate) {
+ var maxWindowBits = this.params[endpoint + '_max_window_bits'];
+ this._inflate = zlib.createInflateRaw({
+ windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS
+ });
+ }
+ this._inflate.writeInProgress = true;
+
+ var self = this;
+ var buffers = [];
+
+ this._inflate.on('error', onError).on('data', onData);
+ this._inflate.write(data);
+ if (fin) {
+ this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff]));
+ }
+ this._inflate.flush(function() {
+ cleanup();
+ callback(null, Buffer.concat(buffers));
+ });
+
+ function onError(err) {
+ cleanup();
+ callback(err);
+ }
+
+ function onData(data) {
+ buffers.push(data);
+ }
+
+ function cleanup() {
+ if (!self._inflate) return;
+ self._inflate.removeListener('error', onError);
+ self._inflate.removeListener('data', onData);
+ self._inflate.writeInProgress = false;
+ if ((fin && self.params[endpoint + '_no_context_takeover']) || self._inflate.pendingClose) {
+ if (self._inflate.close) self._inflate.close();
+ self._inflate = null;
+ }
+ }
+};
+
+/**
+ * Compress message
+ *
+ * @api public
+ */
+
+PerMessageDeflate.prototype.compress = function (data, fin, callback) {
+ var endpoint = this._isServer ? 'server' : 'client';
+
+ if (!this._deflate) {
+ var maxWindowBits = this.params[endpoint + '_max_window_bits'];
+ this._deflate = zlib.createDeflateRaw({
+ flush: zlib.Z_SYNC_FLUSH,
+ windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS,
+ memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL
+ });
+ }
+ this._deflate.writeInProgress = true;
+
+ var self = this;
+ var buffers = [];
+
+ this._deflate.on('error', onError).on('data', onData);
+ this._deflate.write(data);
+ this._deflate.flush(function() {
+ cleanup();
+ var data = Buffer.concat(buffers);
+ if (fin) {
+ data = data.slice(0, data.length - 4);
+ }
+ callback(null, data);
+ });
+
+ function onError(err) {
+ cleanup();
+ callback(err);
+ }
+
+ function onData(data) {
+ buffers.push(data);
+ }
+
+ function cleanup() {
+ if (!self._deflate) return;
+ self._deflate.removeListener('error', onError);
+ self._deflate.removeListener('data', onData);
+ self._deflate.writeInProgress = false;
+ if ((fin && self.params[endpoint + '_no_context_takeover']) || self._deflate.pendingClose) {
+ if (self._deflate.close) self._deflate.close();
+ self._deflate = null;
+ }
+ }
+};
+
+module.exports = PerMessageDeflate;
diff --git a/node_modules/ws/lib/Receiver.hixie.js b/node_modules/ws/lib/Receiver.hixie.js
new file mode 100644
index 000000000..66bc561b7
--- /dev/null
+++ b/node_modules/ws/lib/Receiver.hixie.js
@@ -0,0 +1,184 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var util = require('util');
+
+/**
+ * State constants
+ */
+
+var EMPTY = 0
+ , BODY = 1;
+var BINARYLENGTH = 2
+ , BINARYBODY = 3;
+
+/**
+ * Hixie Receiver implementation
+ */
+
+function Receiver () {
+ if (this instanceof Receiver === false) {
+ throw new TypeError("Classes can't be function-called");
+ }
+
+ this.state = EMPTY;
+ this.buffers = [];
+ this.messageEnd = -1;
+ this.spanLength = 0;
+ this.dead = false;
+
+ this.onerror = function() {};
+ this.ontext = function() {};
+ this.onbinary = function() {};
+ this.onclose = function() {};
+ this.onping = function() {};
+ this.onpong = function() {};
+}
+
+module.exports = Receiver;
+
+/**
+ * Add new data to the parser.
+ *
+ * @api public
+ */
+
+Receiver.prototype.add = function(data) {
+ var self = this;
+ function doAdd() {
+ if (self.state === EMPTY) {
+ if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) {
+ self.reset();
+ self.onclose();
+ return;
+ }
+ if (data[0] === 0x80) {
+ self.messageEnd = 0;
+ self.state = BINARYLENGTH;
+ data = data.slice(1);
+ } else {
+
+ if (data[0] !== 0x00) {
+ self.error('payload must start with 0x00 byte', true);
+ return;
+ }
+ data = data.slice(1);
+ self.state = BODY;
+
+ }
+ }
+ if (self.state === BINARYLENGTH) {
+ var i = 0;
+ while ((i < data.length) && (data[i] & 0x80)) {
+ self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
+ ++i;
+ }
+ if (i < data.length) {
+ self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f);
+ self.state = BINARYBODY;
+ ++i;
+ }
+ if (i > 0)
+ data = data.slice(i);
+ }
+ if (self.state === BINARYBODY) {
+ var dataleft = self.messageEnd - self.spanLength;
+ if (data.length >= dataleft) {
+ // consume the whole buffer to finish the frame
+ self.buffers.push(data);
+ self.spanLength += dataleft;
+ self.messageEnd = dataleft;
+ return self.parse();
+ }
+ // frame's not done even if we consume it all
+ self.buffers.push(data);
+ self.spanLength += data.length;
+ return;
+ }
+ self.buffers.push(data);
+ if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) {
+ self.spanLength += self.messageEnd;
+ return self.parse();
+ }
+ else self.spanLength += data.length;
+ }
+ while(data) data = doAdd();
+};
+
+/**
+ * Releases all resources used by the receiver.
+ *
+ * @api public
+ */
+
+Receiver.prototype.cleanup = function() {
+ this.dead = true;
+ this.state = EMPTY;
+ this.buffers = [];
+};
+
+/**
+ * Process buffered data.
+ *
+ * @api public
+ */
+
+Receiver.prototype.parse = function() {
+ var output = new Buffer(this.spanLength);
+ var outputIndex = 0;
+ for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) {
+ var buffer = this.buffers[bi];
+ buffer.copy(output, outputIndex);
+ outputIndex += buffer.length;
+ }
+ var lastBuffer = this.buffers[this.buffers.length - 1];
+ if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd);
+ if (this.state !== BODY) --this.messageEnd;
+ var tail = null;
+ if (this.messageEnd < lastBuffer.length - 1) {
+ tail = lastBuffer.slice(this.messageEnd + 1);
+ }
+ this.reset();
+ this.ontext(output.toString('utf8'));
+ return tail;
+};
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Receiver.prototype.error = function (reason, terminate) {
+ this.reset();
+ this.onerror(reason, terminate);
+ return this;
+};
+
+/**
+ * Reset parser state
+ *
+ * @api private
+ */
+
+Receiver.prototype.reset = function (reason) {
+ if (this.dead) return;
+ this.state = EMPTY;
+ this.buffers = [];
+ this.messageEnd = -1;
+ this.spanLength = 0;
+};
+
+/**
+ * Internal api
+ */
+
+function bufferIndex(buffer, byte) {
+ for (var i = 0, l = buffer.length; i < l; ++i) {
+ if (buffer[i] === byte) return i;
+ }
+ return -1;
+}
diff --git a/node_modules/ws/lib/Receiver.js b/node_modules/ws/lib/Receiver.js
new file mode 100644
index 000000000..b3183bfb4
--- /dev/null
+++ b/node_modules/ws/lib/Receiver.js
@@ -0,0 +1,702 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var util = require('util')
+ , Validation = require('./Validation').Validation
+ , ErrorCodes = require('./ErrorCodes')
+ , BufferPool = require('./BufferPool')
+ , bufferUtil = require('./BufferUtil').BufferUtil
+ , PerMessageDeflate = require('./PerMessageDeflate');
+
+/**
+ * HyBi Receiver implementation
+ */
+
+function Receiver (extensions) {
+ if (this instanceof Receiver === false) {
+ throw new TypeError("Classes can't be function-called");
+ }
+
+ // memory pool for fragmented messages
+ var fragmentedPoolPrevUsed = -1;
+ this.fragmentedBufferPool = new BufferPool(1024, function(db, length) {
+ return db.used + length;
+ }, function(db) {
+ return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ?
+ Math.ceil((fragmentedPoolPrevUsed + db.used) / 2) :
+ db.used;
+ });
+
+ // memory pool for unfragmented messages
+ var unfragmentedPoolPrevUsed = -1;
+ this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) {
+ return db.used + length;
+ }, function(db) {
+ return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ?
+ Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) :
+ db.used;
+ });
+
+ this.extensions = extensions || {};
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0,
+ fragmentedOperation: false
+ };
+ this.overflow = [];
+ this.headerBuffer = new Buffer(10);
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.currentMessage = [];
+ this.messageHandlers = [];
+ this.expectHeader(2, this.processPacket);
+ this.dead = false;
+ this.processing = false;
+
+ this.onerror = function() {};
+ this.ontext = function() {};
+ this.onbinary = function() {};
+ this.onclose = function() {};
+ this.onping = function() {};
+ this.onpong = function() {};
+}
+
+module.exports = Receiver;
+
+/**
+ * Add new data to the parser.
+ *
+ * @api public
+ */
+
+Receiver.prototype.add = function(data) {
+ var dataLength = data.length;
+ if (dataLength == 0) return;
+ if (this.expectBuffer == null) {
+ this.overflow.push(data);
+ return;
+ }
+ var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset);
+ fastCopy(toRead, data, this.expectBuffer, this.expectOffset);
+ this.expectOffset += toRead;
+ if (toRead < dataLength) {
+ this.overflow.push(data.slice(toRead));
+ }
+ while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) {
+ var bufferForHandler = this.expectBuffer;
+ this.expectBuffer = null;
+ this.expectOffset = 0;
+ this.expectHandler.call(this, bufferForHandler);
+ }
+};
+
+/**
+ * Releases all resources used by the receiver.
+ *
+ * @api public
+ */
+
+Receiver.prototype.cleanup = function() {
+ this.dead = true;
+ this.overflow = null;
+ this.headerBuffer = null;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.unfragmentedBufferPool = null;
+ this.fragmentedBufferPool = null;
+ this.state = null;
+ this.currentMessage = null;
+ this.onerror = null;
+ this.ontext = null;
+ this.onbinary = null;
+ this.onclose = null;
+ this.onping = null;
+ this.onpong = null;
+};
+
+/**
+ * Waits for a certain amount of header bytes to be available, then fires a callback.
+ *
+ * @api private
+ */
+
+Receiver.prototype.expectHeader = function(length, handler) {
+ if (length == 0) {
+ handler(null);
+ return;
+ }
+ this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length);
+ this.expectHandler = handler;
+ var toRead = length;
+ while (toRead > 0 && this.overflow.length > 0) {
+ var fromOverflow = this.overflow.pop();
+ if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
+ var read = Math.min(fromOverflow.length, toRead);
+ fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
+ this.expectOffset += read;
+ toRead -= read;
+ }
+};
+
+/**
+ * Waits for a certain amount of data bytes to be available, then fires a callback.
+ *
+ * @api private
+ */
+
+Receiver.prototype.expectData = function(length, handler) {
+ if (length == 0) {
+ handler(null);
+ return;
+ }
+ this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation);
+ this.expectHandler = handler;
+ var toRead = length;
+ while (toRead > 0 && this.overflow.length > 0) {
+ var fromOverflow = this.overflow.pop();
+ if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead));
+ var read = Math.min(fromOverflow.length, toRead);
+ fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
+ this.expectOffset += read;
+ toRead -= read;
+ }
+};
+
+/**
+ * Allocates memory from the buffer pool.
+ *
+ * @api private
+ */
+
+Receiver.prototype.allocateFromPool = function(length, isFragmented) {
+ return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length);
+};
+
+/**
+ * Start processing a new packet.
+ *
+ * @api private
+ */
+
+Receiver.prototype.processPacket = function (data) {
+ if (this.extensions[PerMessageDeflate.extensionName]) {
+ if ((data[0] & 0x30) != 0) {
+ this.error('reserved fields (2, 3) must be empty', 1002);
+ return;
+ }
+ } else {
+ if ((data[0] & 0x70) != 0) {
+ this.error('reserved fields must be empty', 1002);
+ return;
+ }
+ }
+ this.state.lastFragment = (data[0] & 0x80) == 0x80;
+ this.state.masked = (data[1] & 0x80) == 0x80;
+ var compressed = (data[0] & 0x40) == 0x40;
+ var opcode = data[0] & 0xf;
+ if (opcode === 0) {
+ if (compressed) {
+ this.error('continuation frame cannot have the Per-message Compressed bits', 1002);
+ return;
+ }
+ // continuation frame
+ this.state.fragmentedOperation = true;
+ this.state.opcode = this.state.activeFragmentedOperation;
+ if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
+ this.error('continuation frame cannot follow current opcode', 1002);
+ return;
+ }
+ }
+ else {
+ if (opcode < 3 && this.state.activeFragmentedOperation != null) {
+ this.error('data frames after the initial data frame must have opcode 0', 1002);
+ return;
+ }
+ if (opcode >= 8 && compressed) {
+ this.error('control frames cannot have the Per-message Compressed bits', 1002);
+ return;
+ }
+ this.state.compressed = compressed;
+ this.state.opcode = opcode;
+ if (this.state.lastFragment === false) {
+ this.state.fragmentedOperation = true;
+ this.state.activeFragmentedOperation = opcode;
+ }
+ else this.state.fragmentedOperation = false;
+ }
+ var handler = opcodes[this.state.opcode];
+ if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002);
+ else {
+ handler.start.call(this, data);
+ }
+};
+
+/**
+ * Endprocessing a packet.
+ *
+ * @api private
+ */
+
+Receiver.prototype.endPacket = function() {
+ if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true);
+ else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true);
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) {
+ // end current fragmented operation
+ this.state.activeFragmentedOperation = null;
+ }
+ this.state.lastFragment = false;
+ this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0;
+ this.state.masked = false;
+ this.expectHeader(2, this.processPacket);
+};
+
+/**
+ * Reset the parser state.
+ *
+ * @api private
+ */
+
+Receiver.prototype.reset = function() {
+ if (this.dead) return;
+ this.state = {
+ activeFragmentedOperation: null,
+ lastFragment: false,
+ masked: false,
+ opcode: 0,
+ fragmentedOperation: false
+ };
+ this.fragmentedBufferPool.reset(true);
+ this.unfragmentedBufferPool.reset(true);
+ this.expectOffset = 0;
+ this.expectBuffer = null;
+ this.expectHandler = null;
+ this.overflow = [];
+ this.currentMessage = [];
+ this.messageHandlers = [];
+};
+
+/**
+ * Unmask received data.
+ *
+ * @api private
+ */
+
+Receiver.prototype.unmask = function (mask, buf, binary) {
+ if (mask != null && buf != null) bufferUtil.unmask(buf, mask);
+ if (binary) return buf;
+ return buf != null ? buf.toString('utf8') : '';
+};
+
+/**
+ * Concatenates a list of buffers.
+ *
+ * @api private
+ */
+
+Receiver.prototype.concatBuffers = function(buffers) {
+ var length = 0;
+ for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length;
+ var mergedBuffer = new Buffer(length);
+ bufferUtil.merge(mergedBuffer, buffers);
+ return mergedBuffer;
+};
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Receiver.prototype.error = function (reason, protocolErrorCode) {
+ this.reset();
+ this.onerror(reason, protocolErrorCode);
+ return this;
+};
+
+/**
+ * Execute message handler buffers
+ *
+ * @api private
+ */
+
+Receiver.prototype.flush = function() {
+ if (this.processing || this.dead) return;
+
+ var handler = this.messageHandlers.shift();
+ if (!handler) return;
+
+ this.processing = true;
+ var self = this;
+
+ handler(function() {
+ self.processing = false;
+ self.flush();
+ });
+};
+
+/**
+ * Apply extensions to message
+ *
+ * @api private
+ */
+
+Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) {
+ var self = this;
+ if (compressed) {
+ this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) {
+ if (self.dead) return;
+ if (err) {
+ callback(new Error('invalid compressed data'));
+ return;
+ }
+ callback(null, buffer);
+ });
+ } else {
+ callback(null, messageBuffer);
+ }
+};
+
+/**
+ * Buffer utilities
+ */
+
+function readUInt16BE(start) {
+ return (this[start]<<8) +
+ this[start+1];
+}
+
+function readUInt32BE(start) {
+ return (this[start]<<24) +
+ (this[start+1]<<16) +
+ (this[start+2]<<8) +
+ this[start+3];
+}
+
+function fastCopy(length, srcBuffer, dstBuffer, dstOffset) {
+ switch (length) {
+ default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break;
+ case 16: dstBuffer[dstOffset+15] = srcBuffer[15];
+ case 15: dstBuffer[dstOffset+14] = srcBuffer[14];
+ case 14: dstBuffer[dstOffset+13] = srcBuffer[13];
+ case 13: dstBuffer[dstOffset+12] = srcBuffer[12];
+ case 12: dstBuffer[dstOffset+11] = srcBuffer[11];
+ case 11: dstBuffer[dstOffset+10] = srcBuffer[10];
+ case 10: dstBuffer[dstOffset+9] = srcBuffer[9];
+ case 9: dstBuffer[dstOffset+8] = srcBuffer[8];
+ case 8: dstBuffer[dstOffset+7] = srcBuffer[7];
+ case 7: dstBuffer[dstOffset+6] = srcBuffer[6];
+ case 6: dstBuffer[dstOffset+5] = srcBuffer[5];
+ case 5: dstBuffer[dstOffset+4] = srcBuffer[4];
+ case 4: dstBuffer[dstOffset+3] = srcBuffer[3];
+ case 3: dstBuffer[dstOffset+2] = srcBuffer[2];
+ case 2: dstBuffer[dstOffset+1] = srcBuffer[1];
+ case 1: dstBuffer[dstOffset] = srcBuffer[0];
+ }
+}
+
+function clone(obj) {
+ var cloned = {};
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ cloned[k] = obj[k];
+ }
+ }
+ return cloned;
+}
+
+/**
+ * Opcode handlers
+ */
+
+var opcodes = {
+ // text
+ '1': {
+ start: function(data) {
+ var self = this;
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['1'].getData.call(self, firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expectHeader(2, function(data) {
+ opcodes['1'].getData.call(self, readUInt16BE.call(data, 0));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expectHeader(8, function(data) {
+ if (readUInt32BE.call(data, 0) != 0) {
+ self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
+ return;
+ }
+ opcodes['1'].getData.call(self, readUInt32BE.call(data, 4));
+ });
+ }
+ },
+ getData: function(length) {
+ var self = this;
+ if (self.state.masked) {
+ self.expectHeader(4, function(data) {
+ var mask = data;
+ self.expectData(length, function(data) {
+ opcodes['1'].finish.call(self, mask, data);
+ });
+ });
+ }
+ else {
+ self.expectData(length, function(data) {
+ opcodes['1'].finish.call(self, null, data);
+ });
+ }
+ },
+ finish: function(mask, data) {
+ var self = this;
+ var packet = this.unmask(mask, data, true) || new Buffer(0);
+ var state = clone(this.state);
+ this.messageHandlers.push(function(callback) {
+ self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
+ if (err) return self.error(err.message, 1007);
+ if (buffer != null) self.currentMessage.push(buffer);
+
+ if (state.lastFragment) {
+ var messageBuffer = self.concatBuffers(self.currentMessage);
+ self.currentMessage = [];
+ if (!Validation.isValidUTF8(messageBuffer)) {
+ self.error('invalid utf8 sequence', 1007);
+ return;
+ }
+ self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer});
+ }
+ callback();
+ });
+ });
+ this.flush();
+ this.endPacket();
+ }
+ },
+ // binary
+ '2': {
+ start: function(data) {
+ var self = this;
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['2'].getData.call(self, firstLength);
+ }
+ else if (firstLength == 126) {
+ self.expectHeader(2, function(data) {
+ opcodes['2'].getData.call(self, readUInt16BE.call(data, 0));
+ });
+ }
+ else if (firstLength == 127) {
+ self.expectHeader(8, function(data) {
+ if (readUInt32BE.call(data, 0) != 0) {
+ self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
+ return;
+ }
+ opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true));
+ });
+ }
+ },
+ getData: function(length) {
+ var self = this;
+ if (self.state.masked) {
+ self.expectHeader(4, function(data) {
+ var mask = data;
+ self.expectData(length, function(data) {
+ opcodes['2'].finish.call(self, mask, data);
+ });
+ });
+ }
+ else {
+ self.expectData(length, function(data) {
+ opcodes['2'].finish.call(self, null, data);
+ });
+ }
+ },
+ finish: function(mask, data) {
+ var self = this;
+ var packet = this.unmask(mask, data, true) || new Buffer(0);
+ var state = clone(this.state);
+ this.messageHandlers.push(function(callback) {
+ self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) {
+ if (err) return self.error(err.message, 1007);
+ if (buffer != null) self.currentMessage.push(buffer);
+ if (state.lastFragment) {
+ var messageBuffer = self.concatBuffers(self.currentMessage);
+ self.currentMessage = [];
+ self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer});
+ }
+ callback();
+ });
+ });
+ this.flush();
+ this.endPacket();
+ }
+ },
+ // close
+ '8': {
+ start: function(data) {
+ var self = this;
+ if (self.state.lastFragment == false) {
+ self.error('fragmented close is not supported', 1002);
+ return;
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['8'].getData.call(self, firstLength);
+ }
+ else {
+ self.error('control frames cannot have more than 125 bytes of data', 1002);
+ }
+ },
+ getData: function(length) {
+ var self = this;
+ if (self.state.masked) {
+ self.expectHeader(4, function(data) {
+ var mask = data;
+ self.expectData(length, function(data) {
+ opcodes['8'].finish.call(self, mask, data);
+ });
+ });
+ }
+ else {
+ self.expectData(length, function(data) {
+ opcodes['8'].finish.call(self, null, data);
+ });
+ }
+ },
+ finish: function(mask, data) {
+ var self = this;
+ data = self.unmask(mask, data, true);
+
+ var state = clone(this.state);
+ this.messageHandlers.push(function() {
+ if (data && data.length == 1) {
+ self.error('close packets with data must be at least two bytes long', 1002);
+ return;
+ }
+ var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000;
+ if (!ErrorCodes.isValidErrorCode(code)) {
+ self.error('invalid error code', 1002);
+ return;
+ }
+ var message = '';
+ if (data && data.length > 2) {
+ var messageBuffer = data.slice(2);
+ if (!Validation.isValidUTF8(messageBuffer)) {
+ self.error('invalid utf8 sequence', 1007);
+ return;
+ }
+ message = messageBuffer.toString('utf8');
+ }
+ self.onclose(code, message, {masked: state.masked});
+ self.reset();
+ });
+ this.flush();
+ },
+ },
+ // ping
+ '9': {
+ start: function(data) {
+ var self = this;
+ if (self.state.lastFragment == false) {
+ self.error('fragmented ping is not supported', 1002);
+ return;
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['9'].getData.call(self, firstLength);
+ }
+ else {
+ self.error('control frames cannot have more than 125 bytes of data', 1002);
+ }
+ },
+ getData: function(length) {
+ var self = this;
+ if (self.state.masked) {
+ self.expectHeader(4, function(data) {
+ var mask = data;
+ self.expectData(length, function(data) {
+ opcodes['9'].finish.call(self, mask, data);
+ });
+ });
+ }
+ else {
+ self.expectData(length, function(data) {
+ opcodes['9'].finish.call(self, null, data);
+ });
+ }
+ },
+ finish: function(mask, data) {
+ var self = this;
+ data = this.unmask(mask, data, true);
+ var state = clone(this.state);
+ this.messageHandlers.push(function(callback) {
+ self.onping(data, {masked: state.masked, binary: true});
+ callback();
+ });
+ this.flush();
+ this.endPacket();
+ }
+ },
+ // pong
+ '10': {
+ start: function(data) {
+ var self = this;
+ if (self.state.lastFragment == false) {
+ self.error('fragmented pong is not supported', 1002);
+ return;
+ }
+
+ // decode length
+ var firstLength = data[1] & 0x7f;
+ if (firstLength < 126) {
+ opcodes['10'].getData.call(self, firstLength);
+ }
+ else {
+ self.error('control frames cannot have more than 125 bytes of data', 1002);
+ }
+ },
+ getData: function(length) {
+ var self = this;
+ if (this.state.masked) {
+ this.expectHeader(4, function(data) {
+ var mask = data;
+ self.expectData(length, function(data) {
+ opcodes['10'].finish.call(self, mask, data);
+ });
+ });
+ }
+ else {
+ this.expectData(length, function(data) {
+ opcodes['10'].finish.call(self, null, data);
+ });
+ }
+ },
+ finish: function(mask, data) {
+ var self = this;
+ data = self.unmask(mask, data, true);
+ var state = clone(this.state);
+ this.messageHandlers.push(function(callback) {
+ self.onpong(data, {masked: state.masked, binary: true});
+ callback();
+ });
+ this.flush();
+ this.endPacket();
+ }
+ }
+}
diff --git a/node_modules/ws/lib/Sender.hixie.js b/node_modules/ws/lib/Sender.hixie.js
new file mode 100644
index 000000000..b87d9dd93
--- /dev/null
+++ b/node_modules/ws/lib/Sender.hixie.js
@@ -0,0 +1,124 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var events = require('events')
+ , util = require('util')
+ , EventEmitter = events.EventEmitter;
+
+/**
+ * Hixie Sender implementation
+ */
+
+function Sender(socket) {
+ if (this instanceof Sender === false) {
+ throw new TypeError("Classes can't be function-called");
+ }
+
+ events.EventEmitter.call(this);
+
+ this.socket = socket;
+ this.continuationFrame = false;
+ this.isClosed = false;
+}
+
+module.exports = Sender;
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+util.inherits(Sender, events.EventEmitter);
+
+/**
+ * Frames and writes data.
+ *
+ * @api public
+ */
+
+Sender.prototype.send = function(data, options, cb) {
+ if (this.isClosed) return;
+
+ var isString = typeof data == 'string'
+ , length = isString ? Buffer.byteLength(data) : data.length
+ , lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes
+ , writeStartMarker = this.continuationFrame == false
+ , writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin)
+ , buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0))
+ , offset = writeStartMarker ? 1 : 0;
+
+ if (writeStartMarker) {
+ if (options && options.binary) {
+ buffer.write('\x80', 'binary');
+ // assume length less than 2**14 bytes
+ if (lengthbytes > 1)
+ buffer.write(String.fromCharCode(128+length/128), offset++, 'binary');
+ buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary');
+ } else
+ buffer.write('\x00', 'binary');
+ }
+
+ if (isString) buffer.write(data, offset, 'utf8');
+ else data.copy(buffer, offset, 0);
+
+ if (writeEndMarker) {
+ if (options && options.binary) {
+ // sending binary, not writing end marker
+ } else
+ buffer.write('\xff', offset + length, 'binary');
+ this.continuationFrame = false;
+ }
+ else this.continuationFrame = true;
+
+ try {
+ this.socket.write(buffer, 'binary', cb);
+ } catch (e) {
+ this.error(e.toString());
+ }
+};
+
+/**
+ * Sends a close instruction to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.close = function(code, data, mask, cb) {
+ if (this.isClosed) return;
+ this.isClosed = true;
+ try {
+ if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary'));
+ this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb);
+ } catch (e) {
+ this.error(e.toString());
+ }
+};
+
+/**
+ * Sends a ping message to the remote party. Not available for hixie.
+ *
+ * @api public
+ */
+
+Sender.prototype.ping = function(data, options) {};
+
+/**
+ * Sends a pong message to the remote party. Not available for hixie.
+ *
+ * @api public
+ */
+
+Sender.prototype.pong = function(data, options) {};
+
+/**
+ * Handles an error
+ *
+ * @api private
+ */
+
+Sender.prototype.error = function (reason) {
+ this.emit('error', reason);
+ return this;
+};
diff --git a/node_modules/ws/lib/Sender.js b/node_modules/ws/lib/Sender.js
new file mode 100644
index 000000000..d34061e07
--- /dev/null
+++ b/node_modules/ws/lib/Sender.js
@@ -0,0 +1,324 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var events = require('events')
+ , util = require('util')
+ , EventEmitter = events.EventEmitter
+ , ErrorCodes = require('./ErrorCodes')
+ , bufferUtil = require('./BufferUtil').BufferUtil
+ , PerMessageDeflate = require('./PerMessageDeflate');
+
+/**
+ * HyBi Sender implementation
+ */
+
+function Sender(socket, extensions) {
+ if (this instanceof Sender === false) {
+ throw new TypeError("Classes can't be function-called");
+ }
+
+ events.EventEmitter.call(this);
+
+ this._socket = socket;
+ this.extensions = extensions || {};
+ this.firstFragment = true;
+ this.compress = false;
+ this.messageHandlers = [];
+ this.processing = false;
+}
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+util.inherits(Sender, events.EventEmitter);
+
+/**
+ * Sends a close instruction to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.close = function(code, data, mask, cb) {
+ if (typeof code !== 'undefined') {
+ if (typeof code !== 'number' ||
+ !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number');
+ }
+ code = code || 1000;
+ var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
+ writeUInt16BE.call(dataBuffer, code, 0);
+ if (dataBuffer.length > 2) dataBuffer.write(data, 2);
+
+ var self = this;
+ this.messageHandlers.push(function(callback) {
+ self.frameAndSend(0x8, dataBuffer, true, mask);
+ callback();
+ if (typeof cb == 'function') cb();
+ });
+ this.flush();
+};
+
+/**
+ * Sends a ping message to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.ping = function(data, options) {
+ var mask = options && options.mask;
+ var self = this;
+ this.messageHandlers.push(function(callback) {
+ self.frameAndSend(0x9, data || '', true, mask);
+ callback();
+ });
+ this.flush();
+};
+
+/**
+ * Sends a pong message to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.pong = function(data, options) {
+ var mask = options && options.mask;
+ var self = this;
+ this.messageHandlers.push(function(callback) {
+ self.frameAndSend(0xa, data || '', true, mask);
+ callback();
+ });
+ this.flush();
+};
+
+/**
+ * Sends text or binary data to the remote party.
+ *
+ * @api public
+ */
+
+Sender.prototype.send = function(data, options, cb) {
+ var finalFragment = options && options.fin === false ? false : true;
+ var mask = options && options.mask;
+ var compress = options && options.compress;
+ var opcode = options && options.binary ? 2 : 1;
+ if (this.firstFragment === false) {
+ opcode = 0;
+ compress = false;
+ } else {
+ this.firstFragment = false;
+ this.compress = compress;
+ }
+ if (finalFragment) this.firstFragment = true
+
+ var compressFragment = this.compress;
+
+ var self = this;
+ this.messageHandlers.push(function(callback) {
+ self.applyExtensions(data, finalFragment, compressFragment, function(err, data) {
+ if (err) {
+ if (typeof cb == 'function') cb(err);
+ else self.emit('error', err);
+ return;
+ }
+ self.frameAndSend(opcode, data, finalFragment, mask, compress, cb);
+ callback();
+ });
+ });
+ this.flush();
+};
+
+/**
+ * Frames and sends a piece of data according to the HyBi WebSocket protocol.
+ *
+ * @api private
+ */
+
+Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, compressed, cb) {
+ var canModifyData = false;
+
+ if (!data) {
+ try {
+ this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb);
+ }
+ catch (e) {
+ if (typeof cb == 'function') cb(e);
+ else this.emit('error', e);
+ }
+ return;
+ }
+
+ if (!Buffer.isBuffer(data)) {
+ canModifyData = true;
+ if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) {
+ data = getArrayBuffer(data);
+ } else {
+ //
+ // If people want to send a number, this would allocate the number in
+ // bytes as memory size instead of storing the number as buffer value. So
+ // we need to transform it to string in order to prevent possible
+ // vulnerabilities / memory attacks.
+ //
+ if (typeof data === 'number') data = data.toString();
+
+ data = new Buffer(data);
+ }
+ }
+
+ var dataLength = data.length
+ , dataOffset = maskData ? 6 : 2
+ , secondByte = dataLength;
+
+ if (dataLength >= 65536) {
+ dataOffset += 8;
+ secondByte = 127;
+ }
+ else if (dataLength > 125) {
+ dataOffset += 2;
+ secondByte = 126;
+ }
+
+ var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData);
+ var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset;
+ var outputBuffer = new Buffer(totalLength);
+ outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode;
+ if (compressed) outputBuffer[0] |= 0x40;
+
+ switch (secondByte) {
+ case 126:
+ writeUInt16BE.call(outputBuffer, dataLength, 2);
+ break;
+ case 127:
+ writeUInt32BE.call(outputBuffer, 0, 2);
+ writeUInt32BE.call(outputBuffer, dataLength, 6);
+ }
+
+ if (maskData) {
+ outputBuffer[1] = secondByte | 0x80;
+ var mask = this._randomMask || (this._randomMask = getRandomMask());
+ outputBuffer[dataOffset - 4] = mask[0];
+ outputBuffer[dataOffset - 3] = mask[1];
+ outputBuffer[dataOffset - 2] = mask[2];
+ outputBuffer[dataOffset - 1] = mask[3];
+ if (mergeBuffers) {
+ bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength);
+ try {
+ this._socket.write(outputBuffer, 'binary', cb);
+ }
+ catch (e) {
+ if (typeof cb == 'function') cb(e);
+ else this.emit('error', e);
+ }
+ }
+ else {
+ bufferUtil.mask(data, mask, data, 0, dataLength);
+ try {
+ this._socket.write(outputBuffer, 'binary');
+ this._socket.write(data, 'binary', cb);
+ }
+ catch (e) {
+ if (typeof cb == 'function') cb(e);
+ else this.emit('error', e);
+ }
+ }
+ }
+ else {
+ outputBuffer[1] = secondByte;
+ if (mergeBuffers) {
+ data.copy(outputBuffer, dataOffset);
+ try {
+ this._socket.write(outputBuffer, 'binary', cb);
+ }
+ catch (e) {
+ if (typeof cb == 'function') cb(e);
+ else this.emit('error', e);
+ }
+ }
+ else {
+ try {
+ this._socket.write(outputBuffer, 'binary');
+ this._socket.write(data, 'binary', cb);
+ }
+ catch (e) {
+ if (typeof cb == 'function') cb(e);
+ else this.emit('error', e);
+ }
+ }
+ }
+};
+
+/**
+ * Execute message handler buffers
+ *
+ * @api private
+ */
+
+Sender.prototype.flush = function() {
+ if (this.processing) return;
+
+ var handler = this.messageHandlers.shift();
+ if (!handler) return;
+
+ this.processing = true;
+
+ var self = this;
+
+ handler(function() {
+ self.processing = false;
+ self.flush();
+ });
+};
+
+/**
+ * Apply extensions to message
+ *
+ * @api private
+ */
+
+Sender.prototype.applyExtensions = function(data, fin, compress, callback) {
+ if (compress && data) {
+ if ((data.buffer || data) instanceof ArrayBuffer) {
+ data = getArrayBuffer(data);
+ }
+ this.extensions[PerMessageDeflate.extensionName].compress(data, fin, callback);
+ } else {
+ callback(null, data);
+ }
+};
+
+module.exports = Sender;
+
+function writeUInt16BE(value, offset) {
+ this[offset] = (value & 0xff00)>>8;
+ this[offset+1] = value & 0xff;
+}
+
+function writeUInt32BE(value, offset) {
+ this[offset] = (value & 0xff000000)>>24;
+ this[offset+1] = (value & 0xff0000)>>16;
+ this[offset+2] = (value & 0xff00)>>8;
+ this[offset+3] = value & 0xff;
+}
+
+function getArrayBuffer(data) {
+ // data is either an ArrayBuffer or ArrayBufferView.
+ var array = new Uint8Array(data.buffer || data)
+ , l = data.byteLength || data.length
+ , o = data.byteOffset || 0
+ , buffer = new Buffer(l);
+ for (var i = 0; i < l; ++i) {
+ buffer[i] = array[o+i];
+ }
+ return buffer;
+}
+
+function getRandomMask() {
+ return new Buffer([
+ ~~(Math.random() * 255),
+ ~~(Math.random() * 255),
+ ~~(Math.random() * 255),
+ ~~(Math.random() * 255)
+ ]);
+}
diff --git a/node_modules/ws/lib/Validation.fallback.js b/node_modules/ws/lib/Validation.fallback.js
new file mode 100644
index 000000000..2c7c4fd48
--- /dev/null
+++ b/node_modules/ws/lib/Validation.fallback.js
@@ -0,0 +1,12 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+module.exports.Validation = {
+ isValidUTF8: function(buffer) {
+ return true;
+ }
+};
+
diff --git a/node_modules/ws/lib/Validation.js b/node_modules/ws/lib/Validation.js
new file mode 100644
index 000000000..0795fb7f0
--- /dev/null
+++ b/node_modules/ws/lib/Validation.js
@@ -0,0 +1,13 @@
+'use strict';
+
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+try {
+ module.exports = require('utf-8-validate');
+} catch (e) {
+ module.exports = require('./Validation.fallback');
+}
diff --git a/node_modules/ws/lib/WebSocket.js b/node_modules/ws/lib/WebSocket.js
new file mode 100644
index 000000000..4e06c8071
--- /dev/null
+++ b/node_modules/ws/lib/WebSocket.js
@@ -0,0 +1,965 @@
+'use strict';
+
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var url = require('url')
+ , util = require('util')
+ , http = require('http')
+ , https = require('https')
+ , crypto = require('crypto')
+ , stream = require('stream')
+ , Ultron = require('ultron')
+ , Options = require('options')
+ , Sender = require('./Sender')
+ , Receiver = require('./Receiver')
+ , SenderHixie = require('./Sender.hixie')
+ , ReceiverHixie = require('./Receiver.hixie')
+ , Extensions = require('./Extensions')
+ , PerMessageDeflate = require('./PerMessageDeflate')
+ , EventEmitter = require('events').EventEmitter;
+
+/**
+ * Constants
+ */
+
+// Default protocol version
+
+var protocolVersion = 13;
+
+// Close timeout
+
+var closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly
+
+/**
+ * WebSocket implementation
+ *
+ * @constructor
+ * @param {String} address Connection address.
+ * @param {String|Array} protocols WebSocket protocols.
+ * @param {Object} options Additional connection options.
+ * @api public
+ */
+function WebSocket(address, protocols, options) {
+ if (this instanceof WebSocket === false) {
+ return new WebSocket(address, protocols, options);
+ }
+
+ EventEmitter.call(this);
+
+ if (protocols && !Array.isArray(protocols) && 'object' === typeof protocols) {
+ // accept the "options" Object as the 2nd argument
+ options = protocols;
+ protocols = null;
+ }
+
+ if ('string' === typeof protocols) {
+ protocols = [ protocols ];
+ }
+
+ if (!Array.isArray(protocols)) {
+ protocols = [];
+ }
+
+ this._socket = null;
+ this._ultron = null;
+ this._closeReceived = false;
+ this.bytesReceived = 0;
+ this.readyState = null;
+ this.supports = {};
+ this.extensions = {};
+
+ if (Array.isArray(address)) {
+ initAsServerClient.apply(this, address.concat(options));
+ } else {
+ initAsClient.apply(this, [address, protocols, options]);
+ }
+}
+
+/**
+ * Inherits from EventEmitter.
+ */
+util.inherits(WebSocket, EventEmitter);
+
+/**
+ * Ready States
+ */
+["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function each(state, index) {
+ WebSocket.prototype[state] = WebSocket[state] = index;
+});
+
+/**
+ * Gracefully closes the connection, after sending a description message to the server
+ *
+ * @param {Object} data to be sent to the server
+ * @api public
+ */
+WebSocket.prototype.close = function close(code, data) {
+ if (this.readyState === WebSocket.CLOSED) return;
+
+ if (this.readyState === WebSocket.CONNECTING) {
+ this.readyState = WebSocket.CLOSED;
+ return;
+ }
+
+ if (this.readyState === WebSocket.CLOSING) {
+ if (this._closeReceived && this._isServer) {
+ this.terminate();
+ }
+ return;
+ }
+
+ var self = this;
+ try {
+ this.readyState = WebSocket.CLOSING;
+ this._closeCode = code;
+ this._closeMessage = data;
+ var mask = !this._isServer;
+ this._sender.close(code, data, mask, function(err) {
+ if (err) self.emit('error', err);
+
+ if (self._closeReceived && self._isServer) {
+ self.terminate();
+ } else {
+ // ensure that the connection is cleaned up even when no response of closing handshake.
+ clearTimeout(self._closeTimer);
+ self._closeTimer = setTimeout(cleanupWebsocketResources.bind(self, true), closeTimeout);
+ }
+ });
+ } catch (e) {
+ this.emit('error', e);
+ }
+};
+
+/**
+ * Pause the client stream
+ *
+ * @api public
+ */
+WebSocket.prototype.pause = function pauser() {
+ if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
+
+ return this._socket.pause();
+};
+
+/**
+ * Sends a ping
+ *
+ * @param {Object} data to be sent to the server
+ * @param {Object} Members - mask: boolean, binary: boolean
+ * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
+ * @api public
+ */
+WebSocket.prototype.ping = function ping(data, options, dontFailWhenClosed) {
+ if (this.readyState !== WebSocket.OPEN) {
+ if (dontFailWhenClosed === true) return;
+ throw new Error('not opened');
+ }
+
+ options = options || {};
+
+ if (typeof options.mask === 'undefined') options.mask = !this._isServer;
+
+ this._sender.ping(data, options);
+};
+
+/**
+ * Sends a pong
+ *
+ * @param {Object} data to be sent to the server
+ * @param {Object} Members - mask: boolean, binary: boolean
+ * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
+ * @api public
+ */
+WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) {
+ if (this.readyState !== WebSocket.OPEN) {
+ if (dontFailWhenClosed === true) return;
+ throw new Error('not opened');
+ }
+
+ options = options || {};
+
+ if (typeof options.mask === 'undefined') options.mask = !this._isServer;
+
+ this._sender.pong(data, options);
+};
+
+/**
+ * Resume the client stream
+ *
+ * @api public
+ */
+WebSocket.prototype.resume = function resume() {
+ if (this.readyState !== WebSocket.OPEN) throw new Error('not opened');
+
+ return this._socket.resume();
+};
+
+/**
+ * Sends a piece of data
+ *
+ * @param {Object} data to be sent to the server
+ * @param {Object} Members - mask: boolean, binary: boolean, compress: boolean
+ * @param {function} Optional callback which is executed after the send completes
+ * @api public
+ */
+
+WebSocket.prototype.send = function send(data, options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+
+ if (this.readyState !== WebSocket.OPEN) {
+ if (typeof cb === 'function') cb(new Error('not opened'));
+ else throw new Error('not opened');
+ return;
+ }
+
+ if (!data) data = '';
+ if (this._queue) {
+ var self = this;
+ this._queue.push(function() { self.send(data, options, cb); });
+ return;
+ }
+
+ options = options || {};
+ options.fin = true;
+
+ if (typeof options.binary === 'undefined') {
+ options.binary = (data instanceof ArrayBuffer || data instanceof Buffer ||
+ data instanceof Uint8Array ||
+ data instanceof Uint16Array ||
+ data instanceof Uint32Array ||
+ data instanceof Int8Array ||
+ data instanceof Int16Array ||
+ data instanceof Int32Array ||
+ data instanceof Float32Array ||
+ data instanceof Float64Array);
+ }
+
+ if (typeof options.mask === 'undefined') options.mask = !this._isServer;
+ if (typeof options.compress === 'undefined') options.compress = true;
+ if (!this.extensions[PerMessageDeflate.extensionName]) {
+ options.compress = false;
+ }
+
+ var readable = typeof stream.Readable === 'function'
+ ? stream.Readable
+ : stream.Stream;
+
+ if (data instanceof readable) {
+ startQueue(this);
+ var self = this;
+
+ sendStream(this, data, options, function send(error) {
+ process.nextTick(function tock() {
+ executeQueueSends(self);
+ });
+
+ if (typeof cb === 'function') cb(error);
+ });
+ } else {
+ this._sender.send(data, options, cb);
+ }
+};
+
+/**
+ * Streams data through calls to a user supplied function
+ *
+ * @param {Object} Members - mask: boolean, binary: boolean, compress: boolean
+ * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'.
+ * @api public
+ */
+WebSocket.prototype.stream = function stream(options, cb) {
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+
+ var self = this;
+
+ if (typeof cb !== 'function') throw new Error('callback must be provided');
+
+ if (this.readyState !== WebSocket.OPEN) {
+ if (typeof cb === 'function') cb(new Error('not opened'));
+ else throw new Error('not opened');
+ return;
+ }
+
+ if (this._queue) {
+ this._queue.push(function () { self.stream(options, cb); });
+ return;
+ }
+
+ options = options || {};
+
+ if (typeof options.mask === 'undefined') options.mask = !this._isServer;
+ if (typeof options.compress === 'undefined') options.compress = true;
+ if (!this.extensions[PerMessageDeflate.extensionName]) {
+ options.compress = false;
+ }
+
+ startQueue(this);
+
+ function send(data, final) {
+ try {
+ if (self.readyState !== WebSocket.OPEN) throw new Error('not opened');
+ options.fin = final === true;
+ self._sender.send(data, options);
+ if (!final) process.nextTick(cb.bind(null, null, send));
+ else executeQueueSends(self);
+ } catch (e) {
+ if (typeof cb === 'function') cb(e);
+ else {
+ delete self._queue;
+ self.emit('error', e);
+ }
+ }
+ }
+
+ process.nextTick(cb.bind(null, null, send));
+};
+
+/**
+ * Immediately shuts down the connection
+ *
+ * @api public
+ */
+WebSocket.prototype.terminate = function terminate() {
+ if (this.readyState === WebSocket.CLOSED) return;
+
+ if (this._socket) {
+ this.readyState = WebSocket.CLOSING;
+
+ // End the connection
+ try { this._socket.end(); }
+ catch (e) {
+ // Socket error during end() call, so just destroy it right now
+ cleanupWebsocketResources.call(this, true);
+ return;
+ }
+
+ // Add a timeout to ensure that the connection is completely
+ // cleaned up within 30 seconds, even if the clean close procedure
+ // fails for whatever reason
+ // First cleanup any pre-existing timeout from an earlier "terminate" call,
+ // if one exists. Otherwise terminate calls in quick succession will leak timeouts
+ // and hold the program open for `closeTimout` time.
+ if (this._closeTimer) { clearTimeout(this._closeTimer); }
+ this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout);
+ } else if (this.readyState === WebSocket.CONNECTING) {
+ cleanupWebsocketResources.call(this, true);
+ }
+};
+
+/**
+ * Expose bufferedAmount
+ *
+ * @api public
+ */
+Object.defineProperty(WebSocket.prototype, 'bufferedAmount', {
+ get: function get() {
+ var amount = 0;
+ if (this._socket) {
+ amount = this._socket.bufferSize || 0;
+ }
+ return amount;
+ }
+});
+
+/**
+ * Emulates the W3C Browser based WebSocket interface using function members.
+ *
+ * @see http://dev.w3.org/html5/websockets/#the-websocket-interface
+ * @api public
+ */
+['open', 'error', 'close', 'message'].forEach(function(method) {
+ Object.defineProperty(WebSocket.prototype, 'on' + method, {
+ /**
+ * Returns the current listener
+ *
+ * @returns {Mixed} the set function or undefined
+ * @api public
+ */
+ get: function get() {
+ var listener = this.listeners(method)[0];
+ return listener ? (listener._listener ? listener._listener : listener) : undefined;
+ },
+
+ /**
+ * Start listening for events
+ *
+ * @param {Function} listener the listener
+ * @returns {Mixed} the set function or undefined
+ * @api public
+ */
+ set: function set(listener) {
+ this.removeAllListeners(method);
+ this.addEventListener(method, listener);
+ }
+ });
+});
+
+/**
+ * Emulates the W3C Browser based WebSocket interface using addEventListener.
+ *
+ * @see https://developer.mozilla.org/en/DOM/element.addEventListener
+ * @see http://dev.w3.org/html5/websockets/#the-websocket-interface
+ * @api public
+ */
+WebSocket.prototype.addEventListener = function(method, listener) {
+ var target = this;
+
+ function onMessage (data, flags) {
+ listener.call(target, new MessageEvent(data, !!flags.binary, target));
+ }
+
+ function onClose (code, message) {
+ listener.call(target, new CloseEvent(code, message, target));
+ }
+
+ function onError (event) {
+ event.type = 'error';
+ event.target = target;
+ listener.call(target, event);
+ }
+
+ function onOpen () {
+ listener.call(target, new OpenEvent(target));
+ }
+
+ if (typeof listener === 'function') {
+ if (method === 'message') {
+ // store a reference so we can return the original function from the
+ // addEventListener hook
+ onMessage._listener = listener;
+ this.on(method, onMessage);
+ } else if (method === 'close') {
+ // store a reference so we can return the original function from the
+ // addEventListener hook
+ onClose._listener = listener;
+ this.on(method, onClose);
+ } else if (method === 'error') {
+ // store a reference so we can return the original function from the
+ // addEventListener hook
+ onError._listener = listener;
+ this.on(method, onError);
+ } else if (method === 'open') {
+ // store a reference so we can return the original function from the
+ // addEventListener hook
+ onOpen._listener = listener;
+ this.on(method, onOpen);
+ } else {
+ this.on(method, listener);
+ }
+ }
+};
+
+module.exports = WebSocket;
+module.exports.buildHostHeader = buildHostHeader
+
+/**
+ * W3C MessageEvent
+ *
+ * @see http://www.w3.org/TR/html5/comms.html
+ * @constructor
+ * @api private
+ */
+function MessageEvent(dataArg, isBinary, target) {
+ this.type = 'message';
+ this.data = dataArg;
+ this.target = target;
+ this.binary = isBinary; // non-standard.
+}
+
+/**
+ * W3C CloseEvent
+ *
+ * @see http://www.w3.org/TR/html5/comms.html
+ * @constructor
+ * @api private
+ */
+function CloseEvent(code, reason, target) {
+ this.type = 'close';
+ this.wasClean = (typeof code === 'undefined' || code === 1000);
+ this.code = code;
+ this.reason = reason;
+ this.target = target;
+}
+
+/**
+ * W3C OpenEvent
+ *
+ * @see http://www.w3.org/TR/html5/comms.html
+ * @constructor
+ * @api private
+ */
+function OpenEvent(target) {
+ this.type = 'open';
+ this.target = target;
+}
+
+// Append port number to Host header, only if specified in the url
+// and non-default
+function buildHostHeader(isSecure, hostname, port) {
+ var headerHost = hostname;
+ if (hostname) {
+ if ((isSecure && (port != 443)) || (!isSecure && (port != 80))){
+ headerHost = headerHost + ':' + port;
+ }
+ }
+ return headerHost;
+}
+
+/**
+ * Entirely private apis,
+ * which may or may not be bound to a sepcific WebSocket instance.
+ */
+function initAsServerClient(req, socket, upgradeHead, options) {
+ options = new Options({
+ protocolVersion: protocolVersion,
+ protocol: null,
+ extensions: {}
+ }).merge(options);
+
+ // expose state properties
+ this.protocol = options.value.protocol;
+ this.protocolVersion = options.value.protocolVersion;
+ this.extensions = options.value.extensions;
+ this.supports.binary = (this.protocolVersion !== 'hixie-76');
+ this.upgradeReq = req;
+ this.readyState = WebSocket.CONNECTING;
+ this._isServer = true;
+
+ // establish connection
+ if (options.value.protocolVersion === 'hixie-76') {
+ establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead);
+ } else {
+ establishConnection.call(this, Receiver, Sender, socket, upgradeHead);
+ }
+}
+
+function initAsClient(address, protocols, options) {
+ options = new Options({
+ origin: null,
+ protocolVersion: protocolVersion,
+ host: null,
+ headers: null,
+ protocol: protocols.join(','),
+ agent: null,
+
+ // ssl-related options
+ pfx: null,
+ key: null,
+ passphrase: null,
+ cert: null,
+ ca: null,
+ ciphers: null,
+ rejectUnauthorized: null,
+ perMessageDeflate: true,
+ localAddress: null
+ }).merge(options);
+
+ if (options.value.protocolVersion !== 8 && options.value.protocolVersion !== 13) {
+ throw new Error('unsupported protocol version');
+ }
+
+ // verify URL and establish http class
+ var serverUrl = url.parse(address);
+ var isUnixSocket = serverUrl.protocol === 'ws+unix:';
+ if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url');
+ var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
+ var httpObj = isSecure ? https : http;
+ var port = serverUrl.port || (isSecure ? 443 : 80);
+ var auth = serverUrl.auth;
+
+ // prepare extensions
+ var extensionsOffer = {};
+ var perMessageDeflate;
+ if (options.value.perMessageDeflate) {
+ perMessageDeflate = new PerMessageDeflate(typeof options.value.perMessageDeflate !== true ? options.value.perMessageDeflate : {}, false);
+ extensionsOffer[PerMessageDeflate.extensionName] = perMessageDeflate.offer();
+ }
+
+ // expose state properties
+ this._isServer = false;
+ this.url = address;
+ this.protocolVersion = options.value.protocolVersion;
+ this.supports.binary = (this.protocolVersion !== 'hixie-76');
+
+ // begin handshake
+ var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64');
+ var shasum = crypto.createHash('sha1');
+ shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
+ var expectedServerKey = shasum.digest('base64');
+
+ var agent = options.value.agent;
+
+ var headerHost = buildHostHeader(isSecure, serverUrl.hostname, port)
+
+ var requestOptions = {
+ port: port,
+ host: serverUrl.hostname,
+ headers: {
+ 'Connection': 'Upgrade',
+ 'Upgrade': 'websocket',
+ 'Host': headerHost,
+ 'Sec-WebSocket-Version': options.value.protocolVersion,
+ 'Sec-WebSocket-Key': key
+ }
+ };
+
+ // If we have basic auth.
+ if (auth) {
+ requestOptions.headers.Authorization = 'Basic ' + new Buffer(auth).toString('base64');
+ }
+
+ if (options.value.protocol) {
+ requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
+ }
+
+ if (options.value.host) {
+ requestOptions.headers.Host = options.value.host;
+ }
+
+ if (options.value.headers) {
+ for (var header in options.value.headers) {
+ if (options.value.headers.hasOwnProperty(header)) {
+ requestOptions.headers[header] = options.value.headers[header];
+ }
+ }
+ }
+
+ if (Object.keys(extensionsOffer).length) {
+ requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format(extensionsOffer);
+ }
+
+ if (options.isDefinedAndNonNull('pfx')
+ || options.isDefinedAndNonNull('key')
+ || options.isDefinedAndNonNull('passphrase')
+ || options.isDefinedAndNonNull('cert')
+ || options.isDefinedAndNonNull('ca')
+ || options.isDefinedAndNonNull('ciphers')
+ || options.isDefinedAndNonNull('rejectUnauthorized')) {
+
+ if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx;
+ if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key;
+ if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase;
+ if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert;
+ if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca;
+ if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers;
+ if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized;
+
+ if (!agent) {
+ // global agent ignores client side certificates
+ agent = new httpObj.Agent(requestOptions);
+ }
+ }
+
+ requestOptions.path = serverUrl.path || '/';
+
+ if (agent) {
+ requestOptions.agent = agent;
+ }
+
+ if (isUnixSocket) {
+ requestOptions.socketPath = serverUrl.pathname;
+ }
+
+ if (options.value.localAddress) {
+ requestOptions.localAddress = options.value.localAddress;
+ }
+
+ if (options.value.origin) {
+ if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
+ else requestOptions.headers.Origin = options.value.origin;
+ }
+
+ var self = this;
+ var req = httpObj.request(requestOptions);
+
+ req.on('error', function onerror(error) {
+ self.emit('error', error);
+ cleanupWebsocketResources.call(self, error);
+ });
+
+ req.once('response', function response(res) {
+ var error;
+
+ if (!self.emit('unexpected-response', req, res)) {
+ error = new Error('unexpected server response (' + res.statusCode + ')');
+ req.abort();
+ self.emit('error', error);
+ }
+
+ cleanupWebsocketResources.call(self, error);
+ });
+
+ req.once('upgrade', function upgrade(res, socket, upgradeHead) {
+ if (self.readyState === WebSocket.CLOSED) {
+ // client closed before server accepted connection
+ self.emit('close');
+ self.removeAllListeners();
+ socket.end();
+ return;
+ }
+
+ var serverKey = res.headers['sec-websocket-accept'];
+ if (typeof serverKey === 'undefined' || serverKey !== expectedServerKey) {
+ self.emit('error', 'invalid server key');
+ self.removeAllListeners();
+ socket.end();
+ return;
+ }
+
+ var serverProt = res.headers['sec-websocket-protocol'];
+ var protList = (options.value.protocol || "").split(/, */);
+ var protError = null;
+
+ if (!options.value.protocol && serverProt) {
+ protError = 'server sent a subprotocol even though none requested';
+ } else if (options.value.protocol && !serverProt) {
+ protError = 'server sent no subprotocol even though requested';
+ } else if (serverProt && protList.indexOf(serverProt) === -1) {
+ protError = 'server responded with an invalid protocol';
+ }
+
+ if (protError) {
+ self.emit('error', protError);
+ self.removeAllListeners();
+ socket.end();
+ return;
+ } else if (serverProt) {
+ self.protocol = serverProt;
+ }
+
+ var serverExtensions = Extensions.parse(res.headers['sec-websocket-extensions']);
+ if (perMessageDeflate && serverExtensions[PerMessageDeflate.extensionName]) {
+ try {
+ perMessageDeflate.accept(serverExtensions[PerMessageDeflate.extensionName]);
+ } catch (err) {
+ self.emit('error', 'invalid extension parameter');
+ self.removeAllListeners();
+ socket.end();
+ return;
+ }
+ self.extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
+ }
+
+ establishConnection.call(self, Receiver, Sender, socket, upgradeHead);
+
+ // perform cleanup on http resources
+ req.removeAllListeners();
+ req = null;
+ agent = null;
+ });
+
+ req.end();
+ this.readyState = WebSocket.CONNECTING;
+}
+
+function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) {
+ var ultron = this._ultron = new Ultron(socket)
+ , called = false
+ , self = this;
+
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+
+ this._receiver = new ReceiverClass(this.extensions);
+ this._socket = socket;
+
+ // socket cleanup handlers
+ ultron.on('end', cleanupWebsocketResources.bind(this));
+ ultron.on('close', cleanupWebsocketResources.bind(this));
+ ultron.on('error', cleanupWebsocketResources.bind(this));
+
+ // ensure that the upgradeHead is added to the receiver
+ function firstHandler(data) {
+ if (called || self.readyState === WebSocket.CLOSED) return;
+
+ called = true;
+ socket.removeListener('data', firstHandler);
+ ultron.on('data', realHandler);
+
+ if (upgradeHead && upgradeHead.length > 0) {
+ realHandler(upgradeHead);
+ upgradeHead = null;
+ }
+
+ if (data) realHandler(data);
+ }
+
+ // subsequent packets are pushed straight to the receiver
+ function realHandler(data) {
+ self.bytesReceived += data.length;
+ self._receiver.add(data);
+ }
+
+ ultron.on('data', firstHandler);
+
+ // if data was passed along with the http upgrade,
+ // this will schedule a push of that on to the receiver.
+ // this has to be done on next tick, since the caller
+ // hasn't had a chance to set event handlers on this client
+ // object yet.
+ process.nextTick(firstHandler);
+
+ // receiver event handlers
+ self._receiver.ontext = function ontext(data, flags) {
+ flags = flags || {};
+
+ self.emit('message', data, flags);
+ };
+
+ self._receiver.onbinary = function onbinary(data, flags) {
+ flags = flags || {};
+
+ flags.binary = true;
+ self.emit('message', data, flags);
+ };
+
+ self._receiver.onping = function onping(data, flags) {
+ flags = flags || {};
+
+ self.pong(data, {
+ mask: !self._isServer,
+ binary: flags.binary === true
+ }, true);
+
+ self.emit('ping', data, flags);
+ };
+
+ self._receiver.onpong = function onpong(data, flags) {
+ self.emit('pong', data, flags || {});
+ };
+
+ self._receiver.onclose = function onclose(code, data, flags) {
+ flags = flags || {};
+
+ self._closeReceived = true;
+ self.close(code, data);
+ };
+
+ self._receiver.onerror = function onerror(reason, errorCode) {
+ // close the connection when the receiver reports a HyBi error code
+ self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, '');
+ self.emit('error', reason, errorCode);
+ };
+
+ // finalize the client
+ this._sender = new SenderClass(socket, this.extensions);
+ this._sender.on('error', function onerror(error) {
+ self.close(1002, '');
+ self.emit('error', error);
+ });
+
+ this.readyState = WebSocket.OPEN;
+ this.emit('open');
+}
+
+function startQueue(instance) {
+ instance._queue = instance._queue || [];
+}
+
+function executeQueueSends(instance) {
+ var queue = instance._queue;
+ if (typeof queue === 'undefined') return;
+
+ delete instance._queue;
+ for (var i = 0, l = queue.length; i < l; ++i) {
+ queue[i]();
+ }
+}
+
+function sendStream(instance, stream, options, cb) {
+ stream.on('data', function incoming(data) {
+ if (instance.readyState !== WebSocket.OPEN) {
+ if (typeof cb === 'function') cb(new Error('not opened'));
+ else {
+ delete instance._queue;
+ instance.emit('error', new Error('not opened'));
+ }
+ return;
+ }
+
+ options.fin = false;
+ instance._sender.send(data, options);
+ });
+
+ stream.on('end', function end() {
+ if (instance.readyState !== WebSocket.OPEN) {
+ if (typeof cb === 'function') cb(new Error('not opened'));
+ else {
+ delete instance._queue;
+ instance.emit('error', new Error('not opened'));
+ }
+ return;
+ }
+
+ options.fin = true;
+ instance._sender.send(null, options);
+
+ if (typeof cb === 'function') cb(null);
+ });
+}
+
+function cleanupWebsocketResources(error) {
+ if (this.readyState === WebSocket.CLOSED) return;
+
+ var emitClose = this.readyState !== WebSocket.CONNECTING;
+ this.readyState = WebSocket.CLOSED;
+
+ clearTimeout(this._closeTimer);
+ this._closeTimer = null;
+
+ if (emitClose) {
+ // If the connection was closed abnormally (with an error), or if
+ // the close control frame was not received then the close code
+ // must default to 1006.
+ if (error || !this._closeReceived) {
+ this._closeCode = 1006;
+ }
+ this.emit('close', this._closeCode || 1000, this._closeMessage || '');
+ }
+
+ if (this._socket) {
+ if (this._ultron) this._ultron.destroy();
+ this._socket.on('error', function onerror() {
+ try { this.destroy(); }
+ catch (e) {}
+ });
+
+ try {
+ if (!error) this._socket.end();
+ else this._socket.destroy();
+ } catch (e) { /* Ignore termination errors */ }
+
+ this._socket = null;
+ this._ultron = null;
+ }
+
+ if (this._sender) {
+ this._sender.removeAllListeners();
+ this._sender = null;
+ }
+
+ if (this._receiver) {
+ this._receiver.cleanup();
+ this._receiver = null;
+ }
+
+ if (this.extensions[PerMessageDeflate.extensionName]) {
+ this.extensions[PerMessageDeflate.extensionName].cleanup();
+ }
+
+ this.extensions = null;
+
+ this.removeAllListeners();
+ this.on('error', function onerror() {}); // catch all errors after this
+ delete this._queue;
+}
diff --git a/node_modules/ws/lib/WebSocketServer.js b/node_modules/ws/lib/WebSocketServer.js
new file mode 100644
index 000000000..ba0e4c050
--- /dev/null
+++ b/node_modules/ws/lib/WebSocketServer.js
@@ -0,0 +1,513 @@
+/*!
+ * ws: a node.js websocket client
+ * Copyright(c) 2011 Einar Otto Stangvik
+ * MIT Licensed
+ */
+
+var util = require('util')
+ , events = require('events')
+ , http = require('http')
+ , crypto = require('crypto')
+ , Options = require('options')
+ , WebSocket = require('./WebSocket')
+ , Extensions = require('./Extensions')
+ , PerMessageDeflate = require('./PerMessageDeflate')
+ , tls = require('tls')
+ , url = require('url');
+
+/**
+ * WebSocket Server implementation
+ */
+
+function WebSocketServer(options, callback) {
+ if (this instanceof WebSocketServer === false) {
+ return new WebSocketServer(options, callback);
+ }
+
+ events.EventEmitter.call(this);
+
+ options = new Options({
+ host: '0.0.0.0',
+ port: null,
+ server: null,
+ verifyClient: null,
+ handleProtocols: null,
+ path: null,
+ noServer: false,
+ disableHixie: false,
+ clientTracking: true,
+ perMessageDeflate: true
+ }).merge(options);
+
+ if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) {
+ throw new TypeError('`port` or a `server` must be provided');
+ }
+
+ var self = this;
+
+ if (options.isDefinedAndNonNull('port')) {
+ this._server = http.createServer(function (req, res) {
+ var body = http.STATUS_CODES[426];
+ res.writeHead(426, {
+ 'Content-Length': body.length,
+ 'Content-Type': 'text/plain'
+ });
+ res.end(body);
+ });
+ this._server.allowHalfOpen = false;
+ this._server.listen(options.value.port, options.value.host, callback);
+ this._closeServer = function() { if (self._server) self._server.close(); };
+ }
+ else if (options.value.server) {
+ this._server = options.value.server;
+ if (options.value.path) {
+ // take note of the path, to avoid collisions when multiple websocket servers are
+ // listening on the same http server
+ if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) {
+ throw new Error('two instances of WebSocketServer cannot listen on the same http server path');
+ }
+ if (typeof this._server._webSocketPaths !== 'object') {
+ this._server._webSocketPaths = {};
+ }
+ this._server._webSocketPaths[options.value.path] = 1;
+ }
+ }
+ if (this._server) this._server.once('listening', function() { self.emit('listening'); });
+
+ if (typeof this._server != 'undefined') {
+ this._server.on('error', function(error) {
+ self.emit('error', error)
+ });
+ this._server.on('upgrade', function(req, socket, upgradeHead) {
+ //copy upgradeHead to avoid retention of large slab buffers used in node core
+ var head = new Buffer(upgradeHead.length);
+ upgradeHead.copy(head);
+
+ self.handleUpgrade(req, socket, head, function(client) {
+ self.emit('connection'+req.url, client);
+ self.emit('connection', client);
+ });
+ });
+ }
+
+ this.options = options.value;
+ this.path = options.value.path;
+ this.clients = [];
+}
+
+/**
+ * Inherits from EventEmitter.
+ */
+
+util.inherits(WebSocketServer, events.EventEmitter);
+
+/**
+ * Immediately shuts down the connection.
+ *
+ * @api public
+ */
+
+WebSocketServer.prototype.close = function(callback) {
+ // terminate all associated clients
+ var error = null;
+ try {
+ for (var i = 0, l = this.clients.length; i < l; ++i) {
+ this.clients[i].terminate();
+ }
+ }
+ catch (e) {
+ error = e;
+ }
+
+ // remove path descriptor, if any
+ if (this.path && this._server._webSocketPaths) {
+ delete this._server._webSocketPaths[this.path];
+ if (Object.keys(this._server._webSocketPaths).length == 0) {
+ delete this._server._webSocketPaths;
+ }
+ }
+
+ // close the http server if it was internally created
+ try {
+ if (typeof this._closeServer !== 'undefined') {
+ this._closeServer();
+ }
+ }
+ finally {
+ delete this._server;
+ }
+ if(callback)
+ callback(error);
+ else if(error)
+ throw error;
+}
+
+/**
+ * Handle a HTTP Upgrade request.
+ *
+ * @api public
+ */
+
+WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) {
+ // check for wrong path
+ if (this.options.path) {
+ var u = url.parse(req.url);
+ if (u && u.pathname !== this.options.path) return;
+ }
+
+ if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') {
+ abortConnection(socket, 400, 'Bad Request');
+ return;
+ }
+
+ if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments);
+ else handleHybiUpgrade.apply(this, arguments);
+}
+
+module.exports = WebSocketServer;
+
+/**
+ * Entirely private apis,
+ * which may or may not be bound to a sepcific WebSocket instance.
+ */
+
+function handleHybiUpgrade(req, socket, upgradeHead, cb) {
+ // handle premature socket errors
+ var errorHandler = function() {
+ try { socket.destroy(); } catch (e) {}
+ }
+ socket.on('error', errorHandler);
+
+ // verify key presence
+ if (!req.headers['sec-websocket-key']) {
+ abortConnection(socket, 400, 'Bad Request');
+ return;
+ }
+
+ // verify version
+ var version = parseInt(req.headers['sec-websocket-version']);
+ if ([8, 13].indexOf(version) === -1) {
+ abortConnection(socket, 400, 'Bad Request');
+ return;
+ }
+
+ // verify protocol
+ var protocols = req.headers['sec-websocket-protocol'];
+
+ // verify client
+ var origin = version < 13 ?
+ req.headers['sec-websocket-origin'] :
+ req.headers['origin'];
+
+ // handle extensions offer
+ var extensionsOffer = Extensions.parse(req.headers['sec-websocket-extensions']);
+
+ // handler to call when the connection sequence completes
+ var self = this;
+ var completeHybiUpgrade2 = function(protocol) {
+
+ // calc key
+ var key = req.headers['sec-websocket-key'];
+ var shasum = crypto.createHash('sha1');
+ shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ key = shasum.digest('base64');
+
+ var headers = [
+ 'HTTP/1.1 101 Switching Protocols'
+ , 'Upgrade: websocket'
+ , 'Connection: Upgrade'
+ , 'Sec-WebSocket-Accept: ' + key
+ ];
+
+ if (typeof protocol != 'undefined') {
+ headers.push('Sec-WebSocket-Protocol: ' + protocol);
+ }
+
+ var extensions = {};
+ try {
+ extensions = acceptExtensions.call(self, extensionsOffer);
+ } catch (err) {
+ abortConnection(socket, 400, 'Bad Request');
+ return;
+ }
+
+ if (Object.keys(extensions).length) {
+ var serverExtensions = {};
+ Object.keys(extensions).forEach(function(token) {
+ serverExtensions[token] = [extensions[token].params]
+ });
+ headers.push('Sec-WebSocket-Extensions: ' + Extensions.format(serverExtensions));
+ }
+
+ // allows external modification/inspection of handshake headers
+ self.emit('headers', headers);
+
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+ try {
+ socket.write(headers.concat('', '').join('\r\n'));
+ }
+ catch (e) {
+ // if the upgrade write fails, shut the connection down hard
+ try { socket.destroy(); } catch (e) {}
+ return;
+ }
+
+ var client = new WebSocket([req, socket, upgradeHead], {
+ protocolVersion: version,
+ protocol: protocol,
+ extensions: extensions
+ });
+
+ if (self.options.clientTracking) {
+ self.clients.push(client);
+ client.on('close', function() {
+ var index = self.clients.indexOf(client);
+ if (index != -1) {
+ self.clients.splice(index, 1);
+ }
+ });
+ }
+
+ // signal upgrade complete
+ socket.removeListener('error', errorHandler);
+ cb(client);
+ }
+
+ // optionally call external protocol selection handler before
+ // calling completeHybiUpgrade2
+ var completeHybiUpgrade1 = function() {
+ // choose from the sub-protocols
+ if (typeof self.options.handleProtocols == 'function') {
+ var protList = (protocols || "").split(/, */);
+ var callbackCalled = false;
+ var res = self.options.handleProtocols(protList, function(result, protocol) {
+ callbackCalled = true;
+ if (!result) abortConnection(socket, 401, 'Unauthorized');
+ else completeHybiUpgrade2(protocol);
+ });
+ if (!callbackCalled) {
+ // the handleProtocols handler never called our callback
+ abortConnection(socket, 501, 'Could not process protocols');
+ }
+ return;
+ } else {
+ if (typeof protocols !== 'undefined') {
+ completeHybiUpgrade2(protocols.split(/, */)[0]);
+ }
+ else {
+ completeHybiUpgrade2();
+ }
+ }
+ }
+
+ // optionally call external client verification handler
+ if (typeof this.options.verifyClient == 'function') {
+ var info = {
+ origin: origin,
+ secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined',
+ req: req
+ };
+ if (this.options.verifyClient.length == 2) {
+ this.options.verifyClient(info, function(result, code, name) {
+ if (typeof code === 'undefined') code = 401;
+ if (typeof name === 'undefined') name = http.STATUS_CODES[code];
+
+ if (!result) abortConnection(socket, code, name);
+ else completeHybiUpgrade1();
+ });
+ return;
+ }
+ else if (!this.options.verifyClient(info)) {
+ abortConnection(socket, 401, 'Unauthorized');
+ return;
+ }
+ }
+
+ completeHybiUpgrade1();
+}
+
+function handleHixieUpgrade(req, socket, upgradeHead, cb) {
+ // handle premature socket errors
+ var errorHandler = function() {
+ try { socket.destroy(); } catch (e) {}
+ }
+ socket.on('error', errorHandler);
+
+ // bail if options prevent hixie
+ if (this.options.disableHixie) {
+ abortConnection(socket, 401, 'Hixie support disabled');
+ return;
+ }
+
+ // verify key presence
+ if (!req.headers['sec-websocket-key2']) {
+ abortConnection(socket, 400, 'Bad Request');
+ return;
+ }
+
+ var origin = req.headers['origin']
+ , self = this;
+
+ // setup handshake completion to run after client has been verified
+ var onClientVerified = function() {
+ var wshost;
+ if (!req.headers['x-forwarded-host'])
+ wshost = req.headers.host;
+ else
+ wshost = req.headers['x-forwarded-host'];
+ var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url
+ , protocol = req.headers['sec-websocket-protocol'];
+
+ // handshake completion code to run once nonce has been successfully retrieved
+ var completeHandshake = function(nonce, rest) {
+ // calculate key
+ var k1 = req.headers['sec-websocket-key1']
+ , k2 = req.headers['sec-websocket-key2']
+ , md5 = crypto.createHash('md5');
+
+ [k1, k2].forEach(function (k) {
+ var n = parseInt(k.replace(/[^\d]/g, ''))
+ , spaces = k.replace(/[^ ]/g, '').length;
+ if (spaces === 0 || n % spaces !== 0){
+ abortConnection(socket, 400, 'Bad Request');
+ return;
+ }
+ n /= spaces;
+ md5.update(String.fromCharCode(
+ n >> 24 & 0xFF,
+ n >> 16 & 0xFF,
+ n >> 8 & 0xFF,
+ n & 0xFF));
+ });
+ md5.update(nonce.toString('binary'));
+
+ var headers = [
+ 'HTTP/1.1 101 Switching Protocols'
+ , 'Upgrade: WebSocket'
+ , 'Connection: Upgrade'
+ , 'Sec-WebSocket-Location: ' + location
+ ];
+ if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol);
+ if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin);
+
+ socket.setTimeout(0);
+ socket.setNoDelay(true);
+ try {
+ // merge header and hash buffer
+ var headerBuffer = new Buffer(headers.concat('', '').join('\r\n'));
+ var hashBuffer = new Buffer(md5.digest('binary'), 'binary');
+ var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length);
+ headerBuffer.copy(handshakeBuffer, 0);
+ hashBuffer.copy(handshakeBuffer, headerBuffer.length);
+
+ // do a single write, which - upon success - causes a new client websocket to be setup
+ socket.write(handshakeBuffer, 'binary', function(err) {
+ if (err) return; // do not create client if an error happens
+ var client = new WebSocket([req, socket, rest], {
+ protocolVersion: 'hixie-76',
+ protocol: protocol
+ });
+ if (self.options.clientTracking) {
+ self.clients.push(client);
+ client.on('close', function() {
+ var index = self.clients.indexOf(client);
+ if (index != -1) {
+ self.clients.splice(index, 1);
+ }
+ });
+ }
+
+ // signal upgrade complete
+ socket.removeListener('error', errorHandler);
+ cb(client);
+ });
+ }
+ catch (e) {
+ try { socket.destroy(); } catch (e) {}
+ return;
+ }
+ }
+
+ // retrieve nonce
+ var nonceLength = 8;
+ if (upgradeHead && upgradeHead.length >= nonceLength) {
+ var nonce = upgradeHead.slice(0, nonceLength);
+ var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
+ completeHandshake.call(self, nonce, rest);
+ }
+ else {
+ // nonce not present in upgradeHead, so we must wait for enough data
+ // data to arrive before continuing
+ var nonce = new Buffer(nonceLength);
+ upgradeHead.copy(nonce, 0);
+ var received = upgradeHead.length;
+ var rest = null;
+ var handler = function (data) {
+ var toRead = Math.min(data.length, nonceLength - received);
+ if (toRead === 0) return;
+ data.copy(nonce, received, 0, toRead);
+ received += toRead;
+ if (received == nonceLength) {
+ socket.removeListener('data', handler);
+ if (toRead < data.length) rest = data.slice(toRead);
+ completeHandshake.call(self, nonce, rest);
+ }
+ }
+ socket.on('data', handler);
+ }
+ }
+
+ // verify client
+ if (typeof this.options.verifyClient == 'function') {
+ var info = {
+ origin: origin,
+ secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined',
+ req: req
+ };
+ if (this.options.verifyClient.length == 2) {
+ var self = this;
+ this.options.verifyClient(info, function(result, code, name) {
+ if (typeof code === 'undefined') code = 401;
+ if (typeof name === 'undefined') name = http.STATUS_CODES[code];
+
+ if (!result) abortConnection(socket, code, name);
+ else onClientVerified.apply(self);
+ });
+ return;
+ }
+ else if (!this.options.verifyClient(info)) {
+ abortConnection(socket, 401, 'Unauthorized');
+ return;
+ }
+ }
+
+ // no client verification required
+ onClientVerified();
+}
+
+function acceptExtensions(offer) {
+ var extensions = {};
+ var options = this.options.perMessageDeflate;
+ if (options && offer[PerMessageDeflate.extensionName]) {
+ var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true);
+ perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]);
+ extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
+ }
+ return extensions;
+}
+
+function abortConnection(socket, code, name) {
+ try {
+ var response = [
+ 'HTTP/1.1 ' + code + ' ' + name,
+ 'Content-type: text/html'
+ ];
+ socket.write(response.concat('', '').join('\r\n'));
+ }
+ catch (e) { /* ignore errors - we've aborted this connection */ }
+ finally {
+ // ensure that an early aborted connection is shut down completely
+ try { socket.destroy(); } catch (e) {}
+ }
+}
diff --git a/node_modules/ws/package.json b/node_modules/ws/package.json
new file mode 100644
index 000000000..2d32fd9b7
--- /dev/null
+++ b/node_modules/ws/package.json
@@ -0,0 +1,76 @@
+{
+ "author": {
+ "name": "Einar Otto Stangvik",
+ "email": "einaros@gmail.com",
+ "url": "http://2x.io"
+ },
+ "name": "ws",
+ "description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455",
+ "version": "1.0.1",
+ "license": "MIT",
+ "keywords": [
+ "Hixie",
+ "HyBi",
+ "Push",
+ "RFC-6455",
+ "WebSocket",
+ "WebSockets",
+ "real-time"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/websockets/ws.git"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "dependencies": {
+ "options": ">=0.0.5",
+ "ultron": "1.0.x"
+ },
+ "devDependencies": {
+ "ansi": "0.3.x",
+ "benchmark": "0.3.x",
+ "bufferutil": "1.2.x",
+ "expect.js": "0.3.x",
+ "mocha": "2.3.x",
+ "should": "8.0.x",
+ "tinycolor": "0.0.x",
+ "utf-8-validate": "1.2.x"
+ },
+ "gypfile": true,
+ "gitHead": "40a9d686288b5d0be13f2bf2f3f5da07afc8cda2",
+ "bugs": {
+ "url": "https://github.com/websockets/ws/issues"
+ },
+ "homepage": "https://github.com/websockets/ws#readme",
+ "_id": "ws@1.0.1",
+ "_shasum": "7d0b2a2e58cddd819039c29c9de65045e1b310e9",
+ "_from": "ws@latest",
+ "_npmVersion": "3.5.1",
+ "_nodeVersion": "4.2.3",
+ "_npmUser": {
+ "name": "3rdeden",
+ "email": "npm@3rd-Eden.com"
+ },
+ "maintainers": [
+ {
+ "name": "einaros",
+ "email": "einaros@gmail.com"
+ },
+ {
+ "name": "v1",
+ "email": "info@3rd-Eden.com"
+ },
+ {
+ "name": "3rdeden",
+ "email": "npm@3rd-Eden.com"
+ }
+ ],
+ "dist": {
+ "shasum": "7d0b2a2e58cddd819039c29c9de65045e1b310e9",
+ "tarball": "http://registry.npmjs.org/ws/-/ws-1.0.1.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ws/-/ws-1.0.1.tgz"
+}