[Pkg-javascript-commits] [node-engine.io-parser] 01/02: Imported Upstream version 1.2.1

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sun Mar 29 16:19:24 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository node-engine.io-parser.

commit 9a6c51987a316ca77853bf9d3d8f73d47439d216
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sun Mar 29 16:32:29 2015 +0200

    Imported Upstream version 1.2.1
---
 .gitignore                    |   1 +
 .npmignore                    |  17 ++
 .travis.yml                   |  45 ++++
 .zuul.yml                     |  18 ++
 History.md                    | 100 +++++++
 LICENSE                       |  22 ++
 Makefile                      |  26 ++
 Readme.md                     | 202 ++++++++++++++
 index.js                      |   2 +
 lib/browser.js                | 594 ++++++++++++++++++++++++++++++++++++++++++
 lib/index.js                  | 467 +++++++++++++++++++++++++++++++++
 lib/keys.js                   |  19 ++
 package.json                  |  33 +++
 test/browser/arraybuffer.js   |  84 ++++++
 test/browser/base64_object.js |  41 +++
 test/browser/blob.js          |  74 ++++++
 test/browser/index.js         |  29 +++
 test/index.js                 |  11 +
 test/node/index.js            |  92 +++++++
 test/parser.js                | 264 +++++++++++++++++++
 test/support/env.js           |   5 +
 21 files changed, 2146 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..f841db0
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,17 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+npm-debug.log
+node_modules/*
+
+test
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..de84f50
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,45 @@
+language: node_js
+node_js:
+- 0.10
+notifications:
+  irc: irc.freenode.org##socket.io
+env:
+  global:
+  - secure: ZS6AzV1n1K1exomhZoK0SorBHEy4/7/qdk1p5/dm0tQdStSVwRDJyQk5wUIgJFsJaVlN8O/MH8LBkiLlalohR/DdE2ZtkNJqfMnetE/ZqPX7r8mzwDasnHJNJnKWJlBVqOpy7hciiPV2yZGIJoe2OQfwWxFEcLJ6NGOXCEkyLAg=
+  - secure: hcqk+nIqzrwJSQs+5T1sKN4YiCghQdP849RjH64bb7Ayslh+o0DmihTE3Wl+cCWFcRBvwBJdmDV2gJpsVcODTc2VdOrJnv8ezfQ8zvAyDZFYxno47PtbjQUi0By2wBPp6zlfigcnXxQ2z6997EDRvsI4VgQVqKsGLot4cMU9oz0=
+matrix:
+  include:
+  - node_js: '0.10'
+    env: BROWSER_NAME=chrome BROWSER_VERSION=latest
+  - node_js: '0.10'
+    env: BROWSER_NAME=safari BROWSER_VERSION=latest
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=6
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=7
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=8
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=9
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=10 BROWSER_PLATFORM="Windows 2012"
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=latest BROWSER_PLATFORM="Windows 2012"
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=4.3
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=5.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=6.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=7.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.0
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.2
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.3
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.4
diff --git a/.zuul.yml b/.zuul.yml
new file mode 100644
index 0000000..fb9da14
--- /dev/null
+++ b/.zuul.yml
@@ -0,0 +1,18 @@
+ui: mocha-bdd
+browsers: 
+  - name: chrome
+    version: 29..latest
+  - name: firefox
+    version: latest
+  - name: safari
+    version: latest
+  - name: ie
+    version: 10
+    platform: Windows 2012
+  - name: ie
+    version: 9
+    version: [6..9, latest]
+  - name: iphone
+    version: oldest..latest
+  - name: android
+    version: latest
diff --git a/History.md b/History.md
new file mode 100644
index 0000000..824ca6e
--- /dev/null
+++ b/History.md
@@ -0,0 +1,100 @@
+
+1.2.1 / 2015-01-17
+==================
+
+ * pass has-binary result to encodePacket [rase-]
+ * Fix parse error [rase-]
+
+1.2.0 / 2015-01-11
+==================
+
+ * fix return type for decodePacket
+ * README fixes
+ * use travis matrix for better test runs
+ * encode into binary only if needed
+ * add test cases for base64 object encoding.
+ * add encodeBase64Object to encoder for browser
+ * avoid sending Blobs on PhantomJS (as on Android)
+ * test that utf8 encoding is not on by default but can be switched on manually
+
+1.1.0 / 2014-07-16
+==================
+
+ * make utf8 encoding/decoding optional
+
+1.0.8 / 2014-07-16
+==================
+
+ * adjust protocol revision
+ * handle invalid utf8 errors gracefully
+ * fix memory leak on browser
+
+1.0.7 / 2014-06-24
+==================
+
+ * fix decodePayloadAsBinary memory leak [christophwitzko]
+ * README improvements
+
+1.0.6 / 2014-05-30
+==================
+
+ * utf8 fixes when using binary encoding [nkzawa]
+
+1.0.5 / 2014-05-06
+==================
+
+ * fix range error
+
+1.0.4 / 2014-04-13
+==================
+
+ * fix `encodePayloadAsBinary` method encodes packets to base64
+
+1.0.3 / 2014-04-10
+==================
+
+ * Fix length calculation when encoding as binary [binlain]
+
+1.0.2 / 2014-03-16
+==================
+
+ * fix binary for android due to a bug in Blob XHR2 implementation [Rase-]
+
+1.0.1 / 2014-03-06
+==================
+
+ * implement `blob` module to simplify code
+ * bump `arraybuffer.slice`
+ * style fixes
+
+1.0.0 / 2014-02-18
+==================
+
+ * parser: added binary encoding [Rase-]
+ * parser: switched to an async interface [Rase-]
+
+0.3.0 / 2013-03-16
+==================
+
+  * parser: if callback returns `false` ignore rest of payload
+  * test: fixed all broken tests
+
+0.2.1 / 2013-03-16
+==================
+
+  * added protocol version to index.js [albertyfwu]
+
+0.2.0 / 2013-02-26
+==================
+
+  * Changed `decodePayload` to use a callback instead of returning an array [sweetieSong, albertyfwu]
+
+0.1.1 / 2013-01-26
+==================
+
+  * package.json fixes
+
+0.1.0 / 2013-01-19
+==================
+
+  * Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9006d30
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2014 Automattic <dev at cloudup.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.
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..068eb5f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+
+REPORTER = dot
+
+test:
+	@if [ "x$(BROWSER_NAME)" = "x" ]; then make test-node; else make test-zuul; fi
+
+test-node:
+	@./node_modules/.bin/mocha \
+		--reporter $(REPORTER) \
+		test/index.js
+
+test-zuul:
+	@if [ "x$(BROWSER_PLATFORM)" = "x" ]; then \
+		./node_modules/zuul/bin/zuul \
+		--browser-name $(BROWSER_NAME) \
+		--browser-version $(BROWSER_VERSION) \
+		test/index.js; \
+		else \
+		./node_modules/zuul/bin/zuul \
+		--browser-name $(BROWSER_NAME) \
+		--browser-version $(BROWSER_VERSION) \
+		--browser-platform "$(BROWSER_PLATFORM)" \
+		test/index.js; \
+	fi
+
+.PHONY: test
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..33d3f23
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,202 @@
+
+# engine.io-parser
+
+[![Build Status](https://secure.travis-ci.org/Automattic/engine.io-parser.svg)](http://travis-ci.org/Automattic/engine.io-parser)
+[![NPM version](https://badge.fury.io/js/engine.io-parser.svg)](http://badge.fury.io/js/engine.io-parser)
+
+This is the JavaScript parser for the engine.io protocol encoding,
+shared by both
+[engine.io-client](https://github.com/Automattic/engine.io-client) and
+[engine.io](https://github.com/Automattic/engine.io).
+
+## How to use
+
+### Standalone
+
+The parser can encode/decode packets, payloads, and payloads as binary
+with the following methods: `encodePacket`, `decodePacket`, `encodePayload`,
+`decodePayload`, `encodePayloadAsBinary`, `decodePayloadAsBinary`.
+
+The browser-side parser also includes `encodePayloadAsArrayBuffer` and `encodePayloadAsBlob`.
+
+Example:
+
+```js
+var parser = require('engine.io-parser');
+
+var data = new Buffer(5);
+for (var i = 0; i < data.length; i++) { data[i] = i; }
+
+parser.encodePacket({ type: 'message', data: data }, function(encoded) {
+  var decodedData = parser.decodePacket(encoded); // { type: 'message', data: data }
+});
+```
+
+### With browserify
+
+Engine.IO Parser is a commonjs module, which means you can include it by using
+`require` on the browser and package using [browserify](http://browserify.org/):
+
+1. install the parser package
+
+    ```shell
+    npm install engine.io-parser
+    ```
+
+1. write your app code
+
+    ```js
+    var parser = require('engine.io-parser');
+
+    var testBuffer = new Int8Array(10);
+    for (var i = 0; i < testBuffer.length; i++) testBuffer[i] = i;
+
+    var packets = [{ type: 'message', data: testBuffer.buffer }, { type: 'message', data: 'hello' }];
+
+    parser.encodePayload(packets, function(encoded) {
+      parser.decodePayload(encoded,
+        function(packet, index, total) {
+          var isLast = index + 1 == total;
+          if (!isLast) {
+            var buffer = new Int8Array(packet.data); // testBuffer
+          } else {
+            var message = packet.data; // 'hello'
+          }
+        });
+    });
+    ```
+
+1. build your app bundle
+
+    ```bash
+    $ browserify app.js > bundle.js
+    ```
+
+1. include on your page
+
+    ```html
+    <script src="/path/to/bundle.js"></script>
+    ```
+
+## Features
+
+- Runs on browser and node.js seamlessly
+- Runs inside HTML5 WebWorker
+- Can encode and decode packets
+  - Encodes from/to ArrayBuffer or Blob when in browser, and Buffer or ArrayBuffer in Node
+
+## API
+
+Note: `cb(type)` means the type is a callback function that contains a parameter of type `type` when called.
+
+### Node
+
+- `encodePacket`
+    - Encodes a packet.
+    - **Parameters**
+      - `Object`: the packet to encode, has `type` and `data`.
+        - `data`: can be a `String`, `Number`, `Buffer`, `ArrayBuffer`
+      - `Boolean`: optional, binary support
+      - `Function`: callback, returns the encoded packet (`cb(String)`)
+- `decodePacket`
+    - Decodes a packet. Data also available as an ArrayBuffer if requested.
+    - Returns data as `String` or (`Blob` on browser, `ArrayBuffer` on Node)
+    - **Parameters**
+      - `String` | `ArrayBuffer`: the packet to decode, has `type` and `data`
+      - `String`: optional, the binary type
+
+- `encodeBase64Packet`
+    - Encodes a packet with binary data in a base64 string (`String`)
+    - **Parameters**
+      - `Object`: the packet to encode, has `type` and `data`
+      - `Function`: callback, returns the base64 encoded message (`cb(String)`)
+- `decodeBase64Packet`
+    - Decodes a packet encoded in a base64 string.
+    - **Parameters**
+      - `String`: the base64 encoded message
+      - `String`: optional, the binary type
+
+- `encodePayload`
+    - Encodes multiple messages (payload).
+    - If any contents are binary, they will be encoded as base64 strings. Base64
+      encoded strings are marked with a b before the length specifier
+    - **Parameters**
+      - `Array`: an array of packets
+      - `Boolean`: optional, binary support
+      - `Function`: callback, returns the encoded payload (`cb(String)`)
+- `decodePayload`
+    - Decodes data when a payload is maybe expected. Possible binary contents are
+      decoded from their base64 representation.
+    - **Parameters**
+      - `String`: the payload
+      - `String`: optional, the binary type
+      - `Function`: callback, returns (cb(`Object`: packet, `Number`:packet index, `Number`:packet total))
+
+- `encodePayloadAsBinary`
+    - Encodes multiple messages (payload) as binary.
+    - **Parameters**
+      - `Array`: an array of packets
+      - `Function`: callback, returns the encoded payload (`cb(Buffer)`)
+- `decodePayloadAsBinary`
+    - Decodes data when a payload is maybe expected. Strings are decoded by
+      interpreting each byte as a key code for entries marked to start with 0. See
+      description of encodePayloadAsBinary.
+    - **Parameters**
+      - `Buffer`: the buffer
+      - `String`: optional, the binary type
+      - `Function`: callback, returns the decoded packet (`cb(Object)`)
+
+### Browser
+
+- `encodePayloadAsArrayBuffer`
+    - Encodes multiple messages (payload) as binary.
+    - **Parameters**
+      - `Array`: an array of packets
+      - `Function`: callback, returns the encoded payload (`cb(ArrayBuffer)`)
+- `encodePayloadAsBlob`
+    - Encodes multiple messages (payload) as blob.
+    - **Parameters**
+      - `Array`: an array of packets
+      - `Function`: callback, returns the encoded payload (`cb(Blob)`)
+
+## Tests
+
+Standalone tests can be run with `make test` which will run both node.js and browser tests.
+
+Browser tests are run using [zuul](https://github.com/defunctzombie/zuul).
+(You must have zuul setup with a saucelabs account.)
+
+You can run the tests locally using the following command:
+
+```
+./node_modules/.bin/zuul --local 8080 -- test/index.js
+```
+
+## Support
+
+The support channels for `engine.io-parser` are the same as `socket.io`:
+  - irc.freenode.net **#socket.io**
+  - [Google Groups](http://groups.google.com/group/socket_io)
+  - [Website](http://socket.io)
+
+## Development
+
+To contribute patches, run tests or benchmarks, make sure to clone the
+repository:
+
+```bash
+git clone git://github.com/LearnBoost/engine.io-parser.git
+```
+
+Then:
+
+```bash
+cd engine.io-parser
+npm install
+```
+
+See the `Tests` section above for how to run tests before submitting any patches.
+
+## License
+
+MIT
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..887727f
--- /dev/null
+++ b/index.js
@@ -0,0 +1,2 @@
+
+module.exports = require('./lib/');
diff --git a/lib/browser.js b/lib/browser.js
new file mode 100644
index 0000000..ee39cbf
--- /dev/null
+++ b/lib/browser.js
@@ -0,0 +1,594 @@
+/**
+ * Module dependencies.
+ */
+
+var keys = require('./keys');
+var hasBinary = require('has-binary');
+var sliceBuffer = require('arraybuffer.slice');
+var base64encoder = require('base64-arraybuffer');
+var after = require('after');
+var utf8 = require('utf8');
+
+/**
+ * Check if we are running an android browser. That requires us to use
+ * ArrayBuffer with polling transports...
+ *
+ * http://ghinda.net/jpeg-blob-ajax-android/
+ */
+
+var isAndroid = navigator.userAgent.match(/Android/i);
+
+/**
+ * Check if we are running in PhantomJS.
+ * Uploading a Blob with PhantomJS does not work correctly, as reported here:
+ * https://github.com/ariya/phantomjs/issues/11395
+ * @type boolean
+ */
+var isPhantomJS = /PhantomJS/i.test(navigator.userAgent);
+
+/**
+ * When true, avoids using Blobs to encode payloads.
+ * @type boolean
+ */
+var dontSendBlobs = isAndroid || isPhantomJS;
+
+/**
+ * Current protocol version.
+ */
+
+exports.protocol = 3;
+
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+    open:     0    // non-ws
+  , close:    1    // non-ws
+  , ping:     2
+  , pong:     3
+  , message:  4
+  , upgrade:  5
+  , noop:     6
+};
+
+var packetslist = keys(packets);
+
+/**
+ * Premade error packet.
+ */
+
+var err = { type: 'error', data: 'parser error' };
+
+/**
+ * Create a blob api even for blob builder when vendor prefixes exist
+ */
+
+var Blob = require('blob');
+
+/**
+ * Encodes a packet.
+ *
+ *     <packet type id> [ <data> ]
+ *
+ * Example:
+ *
+ *     5hello world
+ *     3
+ *     4
+ *
+ * Binary is encoded in an identical principle
+ *
+ * @api private
+ */
+
+exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
+  if ('function' == typeof supportsBinary) {
+    callback = supportsBinary;
+    supportsBinary = false;
+  }
+
+  if ('function' == typeof utf8encode) {
+    callback = utf8encode;
+    utf8encode = null;
+  }
+
+  var data = (packet.data === undefined)
+    ? undefined
+    : packet.data.buffer || packet.data;
+
+  if (global.ArrayBuffer && data instanceof ArrayBuffer) {
+    return encodeArrayBuffer(packet, supportsBinary, callback);
+  } else if (Blob && data instanceof global.Blob) {
+    return encodeBlob(packet, supportsBinary, callback);
+  }
+
+  // might be an object with { base64: true, data: dataAsBase64String }
+  if (data && data.base64) {
+    return encodeBase64Object(packet, callback);
+  }
+
+  // Sending data as a utf-8 string
+  var encoded = packets[packet.type];
+
+  // data fragment is optional
+  if (undefined !== packet.data) {
+    encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
+  }
+
+  return callback('' + encoded);
+
+};
+
+function encodeBase64Object(packet, callback) {
+  // packet data is an object { base64: true, data: dataAsBase64String }
+  var message = 'b' + exports.packets[packet.type] + packet.data.data;
+  return callback(message);
+}
+
+/**
+ * Encode packet helpers for binary types
+ */
+
+function encodeArrayBuffer(packet, supportsBinary, callback) {
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  var data = packet.data;
+  var contentArray = new Uint8Array(data);
+  var resultBuffer = new Uint8Array(1 + data.byteLength);
+
+  resultBuffer[0] = packets[packet.type];
+  for (var i = 0; i < contentArray.length; i++) {
+    resultBuffer[i+1] = contentArray[i];
+  }
+
+  return callback(resultBuffer.buffer);
+}
+
+function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  var fr = new FileReader();
+  fr.onload = function() {
+    packet.data = fr.result;
+    exports.encodePacket(packet, supportsBinary, true, callback);
+  };
+  return fr.readAsArrayBuffer(packet.data);
+}
+
+function encodeBlob(packet, supportsBinary, callback) {
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  if (dontSendBlobs) {
+    return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
+  }
+
+  var length = new Uint8Array(1);
+  length[0] = packets[packet.type];
+  var blob = new Blob([length.buffer, packet.data]);
+
+  return callback(blob);
+}
+
+/**
+ * Encodes a packet with binary data in a base64 string
+ *
+ * @param {Object} packet, has `type` and `data`
+ * @return {String} base64 encoded message
+ */
+
+exports.encodeBase64Packet = function(packet, callback) {
+  var message = 'b' + exports.packets[packet.type];
+  if (Blob && packet.data instanceof Blob) {
+    var fr = new FileReader();
+    fr.onload = function() {
+      var b64 = fr.result.split(',')[1];
+      callback(message + b64);
+    };
+    return fr.readAsDataURL(packet.data);
+  }
+
+  var b64data;
+  try {
+    b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
+  } catch (e) {
+    // iPhone Safari doesn't let you apply with typed arrays
+    var typed = new Uint8Array(packet.data);
+    var basic = new Array(typed.length);
+    for (var i = 0; i < typed.length; i++) {
+      basic[i] = typed[i];
+    }
+    b64data = String.fromCharCode.apply(null, basic);
+  }
+  message += global.btoa(b64data);
+  return callback(message);
+};
+
+/**
+ * Decodes a packet. Changes format to Blob if requested.
+ *
+ * @return {Object} with `type` and `data` (if any)
+ * @api private
+ */
+
+exports.decodePacket = function (data, binaryType, utf8decode) {
+  // String data
+  if (typeof data == 'string' || data === undefined) {
+    if (data.charAt(0) == 'b') {
+      return exports.decodeBase64Packet(data.substr(1), binaryType);
+    }
+
+    if (utf8decode) {
+      try {
+        data = utf8.decode(data);
+      } catch (e) {
+        return err;
+      }
+    }
+    var type = data.charAt(0);
+
+    if (Number(type) != type || !packetslist[type]) {
+      return err;
+    }
+
+    if (data.length > 1) {
+      return { type: packetslist[type], data: data.substring(1) };
+    } else {
+      return { type: packetslist[type] };
+    }
+  }
+
+  var asArray = new Uint8Array(data);
+  var type = asArray[0];
+  var rest = sliceBuffer(data, 1);
+  if (Blob && binaryType === 'blob') {
+    rest = new Blob([rest]);
+  }
+  return { type: packetslist[type], data: rest };
+};
+
+/**
+ * Decodes a packet encoded in a base64 string
+ *
+ * @param {String} base64 encoded message
+ * @return {Object} with `type` and `data` (if any)
+ */
+
+exports.decodeBase64Packet = function(msg, binaryType) {
+  var type = packetslist[msg.charAt(0)];
+  if (!global.ArrayBuffer) {
+    return { type: type, data: { base64: true, data: msg.substr(1) } };
+  }
+
+  var data = base64encoder.decode(msg.substr(1));
+
+  if (binaryType === 'blob' && Blob) {
+    data = new Blob([data]);
+  }
+
+  return { type: type, data: data };
+};
+
+/**
+ * Encodes multiple messages (payload).
+ *
+ *     <length>:data
+ *
+ * Example:
+ *
+ *     11:hello world2:hi
+ *
+ * If any contents are binary, they will be encoded as base64 strings. Base64
+ * encoded strings are marked with a b before the length specifier
+ *
+ * @param {Array} packets
+ * @api private
+ */
+
+exports.encodePayload = function (packets, supportsBinary, callback) {
+  if (typeof supportsBinary == 'function') {
+    callback = supportsBinary;
+    supportsBinary = null;
+  }
+
+  var isBinary = hasBinary(packets);
+
+  if (supportsBinary && isBinary) {
+    if (Blob && !dontSendBlobs) {
+      return exports.encodePayloadAsBlob(packets, callback);
+    }
+
+    return exports.encodePayloadAsArrayBuffer(packets, callback);
+  }
+
+  if (!packets.length) {
+    return callback('0:');
+  }
+
+  function setLengthHeader(message) {
+    return message.length + ':' + message;
+  }
+
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function(message) {
+      doneCallback(null, setLengthHeader(message));
+    });
+  }
+
+  map(packets, encodeOne, function(err, results) {
+    return callback(results.join(''));
+  });
+};
+
+/**
+ * Async array map using after
+ */
+
+function map(ary, each, done) {
+  var result = new Array(ary.length);
+  var next = after(ary.length, done);
+
+  var eachWithIndex = function(i, el, cb) {
+    each(el, function(error, msg) {
+      result[i] = msg;
+      cb(error, result);
+    });
+  };
+
+  for (var i = 0; i < ary.length; i++) {
+    eachWithIndex(i, ary[i], next);
+  }
+}
+
+/*
+ * Decodes data when a payload is maybe expected. Possible binary contents are
+ * decoded from their base64 representation
+ *
+ * @param {String} data, callback method
+ * @api public
+ */
+
+exports.decodePayload = function (data, binaryType, callback) {
+  if (typeof data != 'string') {
+    return exports.decodePayloadAsBinary(data, binaryType, callback);
+  }
+
+  if (typeof binaryType === 'function') {
+    callback = binaryType;
+    binaryType = null;
+  }
+
+  var packet;
+  if (data == '') {
+    // parser error - ignoring payload
+    return callback(err, 0, 1);
+  }
+
+  var length = ''
+    , n, msg;
+
+  for (var i = 0, l = data.length; i < l; i++) {
+    var chr = data.charAt(i);
+
+    if (':' != chr) {
+      length += chr;
+    } else {
+      if ('' == length || (length != (n = Number(length)))) {
+        // parser error - ignoring payload
+        return callback(err, 0, 1);
+      }
+
+      msg = data.substr(i + 1, n);
+
+      if (length != msg.length) {
+        // parser error - ignoring payload
+        return callback(err, 0, 1);
+      }
+
+      if (msg.length) {
+        packet = exports.decodePacket(msg, binaryType, true);
+
+        if (err.type == packet.type && err.data == packet.data) {
+          // parser error in individual packet - ignoring payload
+          return callback(err, 0, 1);
+        }
+
+        var ret = callback(packet, i + n, l);
+        if (false === ret) return;
+      }
+
+      // advance cursor
+      i += n;
+      length = '';
+    }
+  }
+
+  if (length != '') {
+    // parser error - ignoring payload
+    return callback(err, 0, 1);
+  }
+
+};
+
+/**
+ * Encodes multiple messages (payload) as binary.
+ *
+ * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
+ * 255><data>
+ *
+ * Example:
+ * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
+ *
+ * @param {Array} packets
+ * @return {ArrayBuffer} encoded payload
+ * @api private
+ */
+
+exports.encodePayloadAsArrayBuffer = function(packets, callback) {
+  if (!packets.length) {
+    return callback(new ArrayBuffer(0));
+  }
+
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, true, true, function(data) {
+      return doneCallback(null, data);
+    });
+  }
+
+  map(packets, encodeOne, function(err, encodedPackets) {
+    var totalLength = encodedPackets.reduce(function(acc, p) {
+      var len;
+      if (typeof p === 'string'){
+        len = p.length;
+      } else {
+        len = p.byteLength;
+      }
+      return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2
+    }, 0);
+
+    var resultArray = new Uint8Array(totalLength);
+
+    var bufferIndex = 0;
+    encodedPackets.forEach(function(p) {
+      var isString = typeof p === 'string';
+      var ab = p;
+      if (isString) {
+        var view = new Uint8Array(p.length);
+        for (var i = 0; i < p.length; i++) {
+          view[i] = p.charCodeAt(i);
+        }
+        ab = view.buffer;
+      }
+
+      if (isString) { // not true binary
+        resultArray[bufferIndex++] = 0;
+      } else { // true binary
+        resultArray[bufferIndex++] = 1;
+      }
+
+      var lenStr = ab.byteLength.toString();
+      for (var i = 0; i < lenStr.length; i++) {
+        resultArray[bufferIndex++] = parseInt(lenStr[i]);
+      }
+      resultArray[bufferIndex++] = 255;
+
+      var view = new Uint8Array(ab);
+      for (var i = 0; i < view.length; i++) {
+        resultArray[bufferIndex++] = view[i];
+      }
+    });
+
+    return callback(resultArray.buffer);
+  });
+};
+
+/**
+ * Encode as Blob
+ */
+
+exports.encodePayloadAsBlob = function(packets, callback) {
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, true, true, function(encoded) {
+      var binaryIdentifier = new Uint8Array(1);
+      binaryIdentifier[0] = 1;
+      if (typeof encoded === 'string') {
+        var view = new Uint8Array(encoded.length);
+        for (var i = 0; i < encoded.length; i++) {
+          view[i] = encoded.charCodeAt(i);
+        }
+        encoded = view.buffer;
+        binaryIdentifier[0] = 0;
+      }
+
+      var len = (encoded instanceof ArrayBuffer)
+        ? encoded.byteLength
+        : encoded.size;
+
+      var lenStr = len.toString();
+      var lengthAry = new Uint8Array(lenStr.length + 1);
+      for (var i = 0; i < lenStr.length; i++) {
+        lengthAry[i] = parseInt(lenStr[i]);
+      }
+      lengthAry[lenStr.length] = 255;
+
+      if (Blob) {
+        var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]);
+        doneCallback(null, blob);
+      }
+    });
+  }
+
+  map(packets, encodeOne, function(err, results) {
+    return callback(new Blob(results));
+  });
+};
+
+/*
+ * Decodes data when a payload is maybe expected. Strings are decoded by
+ * interpreting each byte as a key code for entries marked to start with 0. See
+ * description of encodePayloadAsBinary
+ *
+ * @param {ArrayBuffer} data, callback method
+ * @api public
+ */
+
+exports.decodePayloadAsBinary = function (data, binaryType, callback) {
+  if (typeof binaryType === 'function') {
+    callback = binaryType;
+    binaryType = null;
+  }
+
+  var bufferTail = data;
+  var buffers = [];
+
+  var numberTooLong = false;
+  while (bufferTail.byteLength > 0) {
+    var tailArray = new Uint8Array(bufferTail);
+    var isString = tailArray[0] === 0;
+    var msgLength = '';
+
+    for (var i = 1; ; i++) {
+      if (tailArray[i] == 255) break;
+
+      if (msgLength.length > 310) {
+        numberTooLong = true;
+        break;
+      }
+
+      msgLength += tailArray[i];
+    }
+
+    if(numberTooLong) return callback(err, 0, 1);
+
+    bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
+    msgLength = parseInt(msgLength);
+
+    var msg = sliceBuffer(bufferTail, 0, msgLength);
+    if (isString) {
+      try {
+        msg = String.fromCharCode.apply(null, new Uint8Array(msg));
+      } catch (e) {
+        // iPhone Safari doesn't let you apply to typed arrays
+        var typed = new Uint8Array(msg);
+        msg = '';
+        for (var i = 0; i < typed.length; i++) {
+          msg += String.fromCharCode(typed[i]);
+        }
+      }
+    }
+
+    buffers.push(msg);
+    bufferTail = sliceBuffer(bufferTail, msgLength);
+  }
+
+  var total = buffers.length;
+  buffers.forEach(function(buffer, i) {
+    callback(exports.decodePacket(buffer, binaryType, true), i, total);
+  });
+};
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..bd7ea9a
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,467 @@
+/**
+ * Module dependencies.
+ */
+
+var utf8 = require('utf8');
+var after = require('after');
+var keys = require('./keys');
+
+/**
+ * Current protocol version.
+ */
+exports.protocol = 3;
+
+/**
+ * Packet types.
+ */
+
+var packets = exports.packets = {
+    open:     0    // non-ws
+  , close:    1    // non-ws
+  , ping:     2
+  , pong:     3
+  , message:  4
+  , upgrade:  5
+  , noop:     6
+};
+
+var packetslist = keys(packets);
+
+/**
+ * Premade error packet.
+ */
+
+var err = { type: 'error', data: 'parser error' };
+
+/**
+ * Encodes a packet.
+ *
+ *     <packet type id> [ <data> ]
+ *
+ * Example:
+ *
+ *     5hello world
+ *     3
+ *     4
+ *
+ * Binary is encoded in an identical principle
+ *
+ * @api private
+ */
+
+exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
+  if ('function' == typeof supportsBinary) {
+    callback = supportsBinary;
+    supportsBinary = null;
+  }
+
+  if ('function' == typeof utf8encode ) {
+    callback = utf8encode;
+    utf8encode = null;
+  }
+
+  var data = (packet.data === undefined)
+    ? undefined
+    : packet.data.buffer || packet.data;
+
+  if (Buffer.isBuffer(data)) {
+    return encodeBuffer(packet, supportsBinary, callback);
+  } else if (data instanceof ArrayBuffer) {
+    return encodeArrayBuffer(packet, supportsBinary, callback);
+  }
+
+  // Sending data as a utf-8 string
+  var encoded = packets[packet.type];
+
+  // data fragment is optional
+  if (undefined !== packet.data) {
+    encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
+  }
+
+  return callback('' + encoded);
+};
+
+/**
+ * Encode Buffer data
+ */
+
+function encodeBuffer(packet, supportsBinary, callback) {
+  var data = packet.data;
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  var typeBuffer = new Buffer(1);
+  typeBuffer[0] = packets[packet.type];
+  return callback(Buffer.concat([typeBuffer, data]));
+}
+
+function encodeArrayBuffer(packet, supportsBinary, callback) {
+  var data = (packet.data === undefined)
+    ? undefined
+    : packet.data.buffer || packet.data;
+
+  if (!supportsBinary) {
+    return exports.encodeBase64Packet(packet, callback);
+  }
+
+  var contentArray = new Uint8Array(data);
+  var resultBuffer = new Buffer(1 + data.byteLength);
+
+  resultBuffer[0] = packets[packet.type];
+  for (var i = 0; i < contentArray.length; i++){
+    resultBuffer[i+1] = contentArray[i];
+  }
+  return callback(resultBuffer);
+}
+
+/**
+ * Encodes a packet with binary data in a base64 string
+ *
+ * @param {Object} packet, has `type` and `data`
+ * @return {String} base64 encoded message
+ */
+
+exports.encodeBase64Packet = function(packet, callback){
+  var data = packet.data.buffer || packet.data;
+  if (data instanceof ArrayBuffer) {
+    var buf = new Buffer(data.byteLength);
+    for (var i = 0; i < buf.length; i++) {
+      buf[i] = data[i];
+    }
+    packet.data = buf;
+  }
+
+  var message = 'b' + packets[packet.type];
+  message += packet.data.toString('base64');
+  return callback(message);
+};
+
+/**
+ * Decodes a packet. Data also available as an ArrayBuffer if requested.
+ *
+ * @return {Object} with `type` and `data` (if any)
+ * @api private
+ */
+
+exports.decodePacket = function (data, binaryType, utf8decode) {
+  // String data
+  if (typeof data == 'string' || data === undefined) {
+    if (data.charAt(0) == 'b') {
+      return exports.decodeBase64Packet(data.substr(1), binaryType);
+    }
+
+    var type = data.charAt(0);
+    if (utf8decode) {
+      try {
+        data = utf8.decode(data);
+      } catch (e) {
+        return err;
+      }
+    }
+
+    if (Number(type) != type || !packetslist[type]) {
+      return err;
+    }
+
+    if (data.length > 1) {
+      return { type: packetslist[type], data: data.substring(1) };
+    } else {
+      return { type: packetslist[type] };
+    }
+  }
+
+  // Binary data
+  if (binaryType === 'arraybuffer') {
+    var type = data[0];
+    var intArray = new Uint8Array(data.length - 1);
+    for (var i = 1; i < data.length; i++) {
+      intArray[i - 1] = data[i];
+    }
+    return { type: packetslist[type], data: intArray.buffer };
+  }
+  var type = data[0];
+  return { type: packetslist[type], data: data.slice(1) };
+};
+
+/**
+ * Decodes a packet encoded in a base64 string.
+ *
+ * @param {String} base64 encoded message
+ * @return {Object} with `type` and `data` (if any)
+ */
+
+exports.decodeBase64Packet = function(msg, binaryType) {
+  var type = packetslist[msg.charAt(0)];
+  var data = new Buffer(msg.substr(1), 'base64');
+  if (binaryType === 'arraybuffer') {
+    var abv = new Uint8Array(data.length);
+    for (var i = 0; i < abv.length; i++){
+      abv[i] = data[i];
+    }
+    data = abv.buffer;
+  }
+  return { type: type, data: data };
+};
+
+/**
+ * Encodes multiple messages (payload).
+ *
+ *     <length>:data
+ *
+ * Example:
+ *
+ *     11:hello world2:hi
+ *
+ * If any contents are binary, they will be encoded as base64 strings. Base64
+ * encoded strings are marked with a b before the length specifier
+ *
+ * @param {Array} packets
+ * @api private
+ */
+
+exports.encodePayload = function (packets, supportsBinary, callback) {
+  if (typeof supportsBinary == 'function') {
+    callback = supportsBinary;
+    supportsBinary = null;
+  }
+
+  if (supportsBinary) {
+    return exports.encodePayloadAsBinary(packets, callback);
+  }
+
+  if (!packets.length) {
+    return callback('0:');
+  }
+
+  function setLengthHeader(message) {
+    return message.length + ':' + message;
+  }
+
+  function encodeOne(packet, doneCallback) {
+    exports.encodePacket(packet, supportsBinary, true, function(message) {
+      doneCallback(null, setLengthHeader(message));
+    });
+  }
+
+  map(packets, encodeOne, function(err, results) {
+    return callback(results.join(''));
+  });
+};
+
+/**
+ * Async array map using after
+ */
+
+function map(ary, each, done) {
+  var result = new Array(ary.length);
+  var next = after(ary.length, done);
+
+  var eachWithIndex = function(i, el, cb) {
+    each(el, function(error, msg) {
+      result[i] = msg;
+      cb(error, result);
+    });
+  };
+
+  for (var i = 0; i < ary.length; i++) {
+    eachWithIndex(i, ary[i], next);
+  }
+}
+
+/*
+ * Decodes data when a payload is maybe expected. Possible binary contents are
+ * decoded from their base64 representation
+ *
+ * @param {String} data, callback method
+ * @api public
+ */
+
+exports.decodePayload = function (data, binaryType, callback) {
+  if ('string' != typeof data) {
+    return exports.decodePayloadAsBinary(data, binaryType, callback);
+  }
+
+  if (typeof binaryType === 'function') {
+    callback = binaryType;
+    binaryType = null;
+  }
+
+  var packet;
+  if (data == '') {
+    // parser error - ignoring payload
+    return callback(err, 0, 1);
+  }
+
+  var length = ''
+    , n, msg;
+
+  for (var i = 0, l = data.length; i < l; i++) {
+    var chr = data.charAt(i);
+
+    if (':' != chr) {
+      length += chr;
+    } else {
+      if ('' == length || (length != (n = Number(length)))) {
+        // parser error - ignoring payload
+        return callback(err, 0, 1);
+      }
+
+      msg = data.substr(i + 1, n);
+
+      if (length != msg.length) {
+        // parser error - ignoring payload
+        return callback(err, 0, 1);
+      }
+
+      if (msg.length) {
+        packet = exports.decodePacket(msg, binaryType, true);
+
+        if (err.type == packet.type && err.data == packet.data) {
+          // parser error in individual packet - ignoring payload
+          return callback(err, 0, 1);
+        }
+
+        var ret = callback(packet, i + n, l);
+        if (false === ret) return;
+      }
+
+      // advance cursor
+      i += n;
+      length = '';
+    }
+  }
+
+  if (length != '') {
+    // parser error - ignoring payload
+    return callback(err, 0, 1);
+  }
+
+};
+
+/**
+ *
+ * Converts a buffer to a utf8.js encoded string
+ *
+ * @api private
+ */
+
+function bufferToString(buffer) {
+  var str = '';
+  for (var i = 0; i < buffer.length; i++) {
+    str += String.fromCharCode(buffer[i]);
+  }
+  return str;
+}
+
+/**
+ *
+ * Converts a utf8.js encoded string to a buffer
+ *
+ * @api private
+ */
+
+function stringToBuffer(string) {
+  var buf = new Buffer(string.length);
+  for (var i = 0; i < string.length; i++) {
+    buf.writeUInt8(string.charCodeAt(i), i);
+  }
+  return buf;
+}
+
+/**
+ * Encodes multiple messages (payload) as binary.
+ *
+ * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
+ * 255><data>
+ *
+ * Example:
+ * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
+ *
+ * @param {Array} packets
+ * @return {Buffer} encoded payload
+ * @api private
+ */
+
+exports.encodePayloadAsBinary = function (packets, callback) {
+  if (!packets.length) {
+    return callback(new Buffer(0));
+  }
+
+  function encodeOne(p, doneCallback) {
+    exports.encodePacket(p, true, true, function(packet) {
+
+      if (typeof packet === 'string') {
+        var encodingLength = '' + packet.length;
+        var sizeBuffer = new Buffer(encodingLength.length + 2);
+        sizeBuffer[0] = 0; // is a string (not true binary = 0)
+        for (var i = 0; i < encodingLength.length; i++) {
+          sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
+        }
+        sizeBuffer[sizeBuffer.length - 1] = 255;
+        return doneCallback(null, Buffer.concat([sizeBuffer, stringToBuffer(packet)]));
+      }
+
+      var encodingLength = '' + packet.length;
+      var sizeBuffer = new Buffer(encodingLength.length + 2);
+      sizeBuffer[0] = 1; // is binary (true binary = 1)
+      for (var i = 0; i < encodingLength.length; i++) {
+        sizeBuffer[i + 1] = parseInt(encodingLength[i], 10);
+      }
+      sizeBuffer[sizeBuffer.length - 1] = 255;
+      doneCallback(null, Buffer.concat([sizeBuffer, packet]));
+    });
+  }
+
+  map(packets, encodeOne, function(err, results) {
+    return callback(Buffer.concat(results));
+  });
+};
+
+/*
+ * Decodes data when a payload is maybe expected. Strings are decoded by
+ * interpreting each byte as a key code for entries marked to start with 0. See
+ * description of encodePayloadAsBinary
+
+ * @param {Buffer} data, callback method
+ * @api public
+ */
+
+exports.decodePayloadAsBinary = function (data, binaryType, callback) {
+  if (typeof binaryType === 'function') {
+    callback = binaryType;
+    binaryType = null;
+  }
+
+  var bufferTail = data;
+  var buffers = [];
+
+  while (bufferTail.length > 0) {
+    var strLen = '';
+    var isString = bufferTail[0] === 0;
+    var numberTooLong = false;
+    for (var i = 1; ; i++) {
+      if (bufferTail[i] == 255)  break;
+      // 310 = char length of Number.MAX_VALUE
+      if (strLen.length > 310) {
+        numberTooLong = true;
+        break;
+      }
+      strLen += '' + bufferTail[i];
+    }
+    if(numberTooLong) return callback(err, 0, 1);
+    bufferTail = bufferTail.slice(strLen.length + 1);
+
+    var msgLength = parseInt(strLen, 10);
+
+    var msg = bufferTail.slice(1, msgLength + 1);
+    if (isString) msg = bufferToString(msg);
+    buffers.push(msg);
+    bufferTail = bufferTail.slice(msgLength + 1);
+  }
+
+  var total = buffers.length;
+  buffers.forEach(function(buffer, i) {
+    callback(exports.decodePacket(buffer, binaryType, true), i, total);
+  });
+};
diff --git a/lib/keys.js b/lib/keys.js
new file mode 100644
index 0000000..947dafd
--- /dev/null
+++ b/lib/keys.js
@@ -0,0 +1,19 @@
+
+/**
+ * Gets the keys for an object.
+ *
+ * @return {Array} keys
+ * @api private
+ */
+
+module.exports = Object.keys || function keys (obj){
+  var arr = [];
+  var has = Object.prototype.hasOwnProperty;
+
+  for (var i in obj) {
+    if (has.call(obj, i)) {
+      arr.push(i);
+    }
+  }
+  return arr;
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..61dce88
--- /dev/null
+++ b/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "engine.io-parser",
+  "description": "Parser for the client for the realtime Engine",
+  "version": "1.2.1",
+  "homepage": "https://github.com/LearnBoost/engine.io-protocol",
+  "devDependencies": {
+    "expect.js": "0.3.1",
+    "mocha": "2.1.0",
+    "zuul": "1.10.2"
+  },
+  "dependencies": {
+    "after": "0.8.1",
+    "arraybuffer.slice": "0.0.6",
+    "base64-arraybuffer": "0.1.2",
+    "blob": "0.0.2",
+    "has-binary": "0.1.5",
+    "utf8": "2.0.0"
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "component": {
+    "scripts": {
+      "engine.io-parser/index.js": "lib/index.js",
+      "engine.io-parser/keys.js": "lib/keys.js"
+    }
+  },
+  "repository": {
+    "type": "git",
+    "url": "git at github.com:LearnBoost/engine.io-parser.git"
+  },
+  "browser": "./lib/browser.js"
+}
diff --git a/test/browser/arraybuffer.js b/test/browser/arraybuffer.js
new file mode 100644
index 0000000..003e2db
--- /dev/null
+++ b/test/browser/arraybuffer.js
@@ -0,0 +1,84 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/browser.js');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+var encPayloadAB = parser.encodePayloadAsArrayBuffer;
+var decPayloadB = parser.decodePayloadAsBinary;
+
+/**
+ * Tests.
+ */
+
+describe('parser', function() {
+  it('should encode/decode mixed binary and string contents as b64', function(done) {
+    var data = new Int8Array(5);
+    for (var i = 0; i < data.length; i++) data[i] = i;
+    encPayload([{ type: 'message', data: data.buffer }, { type: 'message', data: 'hello' }], function(encoded) {
+      decPayload(encoded,
+        function(packet, index, total) {
+          var isLast = index + 1 == total;
+          expect(packet.type).to.eql('message');
+          if (!isLast) {
+            expect(new Int8Array(packet.data)).to.eql(data);
+          } else {
+            expect(packet.data).to.eql('hello');
+            done();
+          }
+        });
+    });
+  });
+  
+  it('should encode binary contents as arraybuffer', function(done) {
+    var firstBuffer = new Int8Array(5);
+    for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+    var secondBuffer = new Int8Array(4);
+    for (var i = 0; i < secondBuffer.length; i++) secondBuffer[i] = firstBuffer.length + i;
+  
+    encPayloadAB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: secondBuffer.buffer }], function(data) {
+      decPayloadB(data,
+        function(packet, index, total) {
+          var isLast = index + 1 == total;
+          expect(packet.type).to.eql('message');
+          if (!isLast) {
+            expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+          } else {
+            expect(new Int8Array(packet.data)).to.eql(secondBuffer);
+            done();
+          }
+        });
+    });
+  });
+  
+  it('should encode mixed binary and string contents as arraybuffer', function(done) {
+    var firstBuffer = new Int8Array(123);
+    for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+  
+    encPayloadAB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: 'hello' }, { type: 'close' } ], function(data) {
+      decPayloadB(data,
+        function(packet, index, total) {
+          if (index == 0) {
+            expect(packet.type).to.eql('message');
+            expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+          } else if (index == 1) {
+            expect(packet.type).to.eql('message');
+            expect(packet.data).to.eql('hello');
+          } else {
+            expect(packet.type).to.eql('close');
+            done();
+          }
+        });
+    });
+  });
+});
diff --git a/test/browser/base64_object.js b/test/browser/base64_object.js
new file mode 100644
index 0000000..f9ec5d2
--- /dev/null
+++ b/test/browser/base64_object.js
@@ -0,0 +1,41 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/browser.js');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+
+/**
+ * Tests.
+ */
+describe('parser', function() {
+  it('should encode/decode mixed base64 object and string', function(done) {
+    var data = new Buffer(5);
+    for (var i = 0; i < data.length; i++) data[i] = i;
+    var msg = { base64: true, data: new Buffer(data).toString('base64') };
+    encPayload([{ type: 'message', data: msg }, { type: 'message', data: 'hello' }], function(encoded) {
+      decPayload(encoded,
+        function(packet, index, total) {
+          var isLast = index + 1 == total;
+          expect(packet.type).to.eql('message');
+          if (!isLast) { 
+            if (!global.ArrayBuffer) {
+              expect(packet.data).to.eql(msg);
+            } else {
+              expect(new Int8Array(packet.data)).to.eql(new Int8Array(data));
+            }
+          } else {
+            expect(packet.data).to.eql('hello');
+            done();
+          }
+        });
+    });
+  });
+});
diff --git a/test/browser/blob.js b/test/browser/blob.js
new file mode 100644
index 0000000..1977aaa
--- /dev/null
+++ b/test/browser/blob.js
@@ -0,0 +1,74 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/browser.js');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+var encPayloadB = parser.encodePayloadAsBlob;
+var decPayloadB = parser.decodePayloadAsBinary;
+
+/**
+ * Tests.
+ */
+
+describe('parser', function() {
+  it('should encode binary contents as blob', function(done) {
+    var firstBuffer = new Int8Array(5);
+    for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+    var secondBuffer = new Int8Array(4);
+    for (var i = 0; i < secondBuffer.length; i++) secondBuffer[i] = firstBuffer.length + i;
+
+    encPayloadB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: secondBuffer.buffer }], function(data) {
+      var fr = new FileReader();
+      fr.onload = function() {
+        decPayloadB(this.result,
+          function(packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet.type).to.eql('message');
+            if (!isLast) {
+              expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+            } else {
+              expect(new Int8Array(packet.data)).to.eql(secondBuffer);
+              done();
+            }
+          });
+      };
+      fr.readAsArrayBuffer(data);
+    });
+  });
+
+  it('should encode mixed binary and string contents as blob', function(done) {
+    var firstBuffer = new Int8Array(123);
+    for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+
+    encPayloadB([{ type: 'message', data: firstBuffer.buffer }, { type: 'message', data: 'hello' }, { type: 'close' } ], function(data) {
+      var fr = new FileReader();
+      fr.onload = function() {
+        decPayloadB(this.result,
+          function(packet, index, total) {
+            if (index == 0) {
+              expect(packet.type).to.eql('message');
+              expect(new Int8Array(packet.data)).to.eql(firstBuffer);
+            } else if (index == 1) {
+              expect(packet.type).to.eql('message');
+              expect(packet.data).to.eql('hello');
+            } else {
+              expect(packet.type).to.eql('close');
+              done();
+            }
+          });
+      };
+      fr.readAsArrayBuffer(data);
+    });
+  });
+});
diff --git a/test/browser/index.js b/test/browser/index.js
new file mode 100644
index 0000000..7a60125
--- /dev/null
+++ b/test/browser/index.js
@@ -0,0 +1,29 @@
+var Blob = require('blob');
+
+if (global.ArrayBuffer) {
+  require('./arraybuffer.js');
+}
+
+if (Blob) {
+  require('./blob.js');
+}
+
+require('./base64_object.js');
+
+// General browser only tests
+var parser = require('../../');
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+
+describe('basic functionality', function () {
+  it('should encode string payloads as strings even if binary supported', function (done) {
+    encPayload([{ type: 'ping' }, { type: 'post' }], true, function(data) {
+      expect(data).to.be.a('string');
+      done();
+    });
+  });
+});
+
+
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..f8ca2ad
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,11 @@
+var env = require('./support/env');
+var parser = env.browser ? require('../lib/browser.js') : require('../lib');
+
+// General parsing tests and encoding/decoding strings
+require('./parser.js')(parser);
+
+if (env.browser) {
+  require('./browser');
+} else {
+  require('./node');
+}
diff --git a/test/node/index.js b/test/node/index.js
new file mode 100644
index 0000000..65ddc3e
--- /dev/null
+++ b/test/node/index.js
@@ -0,0 +1,92 @@
+
+/**
+ * Test dependencies.
+ */
+
+var parser = require('../../lib/');
+var expect = require('expect.js');
+
+/**
+ * Shortcuts
+ */
+var encode = parser.encodePacket;
+var decode = parser.decodePacket;
+var encPayload = parser.encodePayload;
+var decPayload = parser.decodePayload;
+var encPayloadB = parser.encodePayloadAsBinary;
+var decPayloadB = parser.decodePayloadAsBinary;
+
+/**
+ * Tests.
+ */
+
+describe('parser', function() {
+  it('should encode a binary message', function(done) {
+    var data = new Buffer(5);
+    for (var i = 0; i < data.length; i++) { data[i] = i; }
+    encode({ type: 'message', data: data }, function(encoded) {
+      expect(decode(encoded)).to.eql({ type: 'message', data: data }); 
+      done();
+    });
+  });
+
+  it('should encode/decode mixed binary and string contents as b64', function(done) {
+    var data = new Buffer(5);
+    for (var i = 0; i < data.length; i++) data[i] = i;
+    encPayload([{ type: 'message', data: data }, { type: 'message', data: 'hello' }], function(encoded) {
+      decPayload(encoded,
+        function(packet, index, total) {
+          var isLast = index + 1 == total;
+          expect(packet.type).to.eql('message');
+          if (!isLast) {
+            expect(packet.data).to.eql(data);
+          } else {
+            expect(packet.data).to.eql('hello');
+            done();
+          }
+        });
+    });
+  });
+
+  it('should encode binary contents as binary', function(done) {
+    var firstBuffer = new Buffer(5);
+      for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+      var secondBuffer = new Buffer(4);
+      for (var i = 0; i < secondBuffer.length; i++) secondBuffer[i] = firstBuffer.length + i;
+
+      encPayloadB([{ type: 'message', data: firstBuffer }, { type: 'message', data: secondBuffer }], function(data) {
+        decPayloadB(data,
+          function(packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet.type).to.eql('message');
+            if (!isLast) {
+              expect(packet.data).to.eql(firstBuffer);
+            } else {
+              expect(packet.data).to.eql(secondBuffer);
+              done();
+            }
+          });
+      });
+  });
+
+  it('should encode mixed binary and string contents as binary', function(done) {
+    var firstBuffer = new Buffer(123);
+    for (var i = 0; i < firstBuffer.length; i++) firstBuffer[i] = i;
+
+    encPayloadB([{ type: 'message', data: firstBuffer }, { type: 'message', data: 'hello' }, { type: 'close' } ], function(data) {
+      decPayloadB(data,
+        function(packet, index, total) {
+          if (index == 0) {
+            expect(packet.type).to.eql('message');
+            expect(packet.data).to.eql(firstBuffer);
+          } else if (index == 1) {
+            expect(packet.type).to.eql('message');
+            expect(packet.data).to.eql('hello');
+          } else {
+            expect(packet.type).to.eql('close');
+            done();
+          }
+        });
+    });
+  });
+});
diff --git a/test/parser.js b/test/parser.js
new file mode 100644
index 0000000..da852d8
--- /dev/null
+++ b/test/parser.js
@@ -0,0 +1,264 @@
+module.exports = function(parser) {
+  /**
+   * Test dependencies.
+   */
+
+  //var parser = require('../lib/');
+  var expect = require('expect.js');
+
+  /**
+   * Shortcuts
+   */
+
+  var encode = parser.encodePacket;
+  var decode = parser.decodePacket;
+  var encPayload = parser.encodePayload;
+  var decPayload = parser.decodePayload;
+
+  /**
+   * Tests.
+   */
+
+  describe('parser', function () {
+
+    describe('packets', function () {
+      describe('basic functionality', function () {
+        it('should encode packets as strings', function (done) {
+          encode({ type: 'message', data: 'test' }, function(data) {
+            expect(data).to.be.a('string');
+            done();
+          });
+        });
+
+        it('should decode packets as objects', function (done) {
+          encode({ type: 'message', data: 'test' }, function(data) {
+            expect(decode(data)).to.be.an('object');
+            done();
+          });
+        });
+      });
+
+      describe('encoding and decoding', function () {
+        it('should allow no data', function (done) {
+          encode({ type: 'message' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'message' });
+            done();
+          });
+        });
+
+        it('should encode an open packet', function (done) {
+          encode({ type: 'open', data: '{"some":"json"}' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'open', data: '{"some":"json"}' });
+            done();
+          });
+        });
+
+        it('should encode a close packet', function (done) {
+          encode({ type: 'close' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'close' });
+            done();
+          });
+        });
+
+        it('should encode a ping packet', function (done) {
+          encode({ type: 'ping', data: '1' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'ping', data: '1' });
+            done();
+          });
+        });
+
+        it('should encode a pong packet', function (done) {
+          encode({ type: 'pong', data: '1' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'pong', data: '1' });
+            done();
+          });
+        });
+
+        it('should encode a message packet', function (done) {
+          encode({ type: 'message', data: 'aaa' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'message', data: 'aaa' });
+            done();
+          });
+        });
+
+        it('should encode a utf8 special chars message packet', function (done) {
+          encode({ type: 'message', data: 'utf8 — string' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'message', data: 'utf8 — string' });
+            done();
+          });
+        });
+
+        it('should not utf8 encode by default', function(done) {
+          encode({ type: 'message', data: '€€€' }, function(data) {
+            expect(data).to.be('4€€€');
+            done();
+          });
+        });
+
+        it('should not utf8 encode by default', function(done) {
+          encode({ type: 'message', data: '€€€' }, true, true, function(data) {
+            expect(data).to.be('4€€€');
+            done();
+          });
+        });
+
+        it('should encode a message packet coercing to string', function (done) {
+          encode({ type: 'message', data: 1 }, function(data) {
+            expect(decode(data)).to.eql({ type: 'message', data: 1 });
+            done();
+          });
+        });
+
+        it('should encode an upgrade packet', function (done) {
+          encode({ type: 'upgrade' }, function(data) {
+            expect(decode(data)).to.eql({ type: 'upgrade' });
+            done();
+          });
+        });
+
+        it('should match the encoding format', function () {
+          encode({ type: 'message', data: 'test' }, function(data) {
+            expect(data).to.match(/^[0-9]/);
+          });
+          encode({ type: 'message' }, function(data) {
+            expect(data).to.match(/^[0-9]$/);
+         });
+        });
+      });
+
+      describe('decoding error handing', function () {
+        var err = { type: 'error', data: 'parser error' };
+
+        it('should disallow bad format', function () {
+          expect(decode(':::')).to.eql(err);
+        });
+
+        it('should disallow inexistent types', function () {
+          expect(decode('94103')).to.eql(err);
+        });
+
+        it('should disallow invalid utf8', function () {
+          expect(decode('4\uffff', false, true)).to.eql(err);
+        });
+      });
+    });
+
+    describe('payloads', function () {
+      describe('basic functionality', function () {
+        it('should encode payloads as strings', function (done) {
+          encPayload([{ type: 'ping' }, { type: 'post' }], function(data) {
+            expect(data).to.be.a('string');
+            done();
+          });
+        });
+      });
+
+      describe('encoding and decoding', function () {
+        var seen = 0;
+        it('should encode/decode packets', function (done) {
+          encPayload([{ type: 'message', data: 'a' }], function(data) {
+            decPayload(data,
+              function(packet, index, total) {
+                var isLast = index + 1 == total;
+                expect(isLast).to.eql(true);
+                seen++;
+              });
+          });
+          encPayload([{type: 'message', data: 'a'}, {type: 'ping'}], function(data) {
+            decPayload(data,
+              function(packet, index, total) {
+                var isLast = index + 1 == total;
+                if (!isLast) {
+                  expect(packet.type).to.eql('message');
+                } else {
+                  expect(packet.type).to.eql('ping');
+                  if (seen == 2) { done(); }
+                }
+                seen++;
+              });
+          });
+        });
+
+        it('should encode/decode empty payloads', function () {
+          encPayload([], function(data) {
+            decPayload(data,
+              function (packet, index, total) {
+                expect(packet.type).to.eql('open');
+                var isLast = index + 1 == total;
+                expect(isLast).to.eql(true);
+              });
+          });
+        });
+      });
+
+      describe('decoding error handling', function () {
+        var err = { type: 'error', data: 'parser error' };
+
+        it('should err on bad payload format', function () {
+          decPayload('1!', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+          decPayload('', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+          decPayload('))', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+
+          if ('undefined' == typeof window || window.Int8Array) {
+            var data = 'undefined' !== typeof window ? (new Int8Array([64])).buffer : new Buffer([64]);
+            decPayload(data, function (packet, index, total) {
+              var isLast = index + 1 == total;
+              expect(packet).to.eql(err);
+              expect(isLast).to.eql(true);
+            });
+          }
+        });
+
+        it('should err on bad payload length', function () {
+          // line 137
+          decPayload('1:', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+        });
+
+        it('should err on bad packet format', function () {
+          // line 137
+          decPayload('3:99:', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+          // line 146
+          decPayload('1:aa', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+          // line 137
+          decPayload('1:a2:b', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+        });
+
+        it('should err on invalid utf8', function () {
+          decPayload('2:4\uffff', function (packet, index, total) {
+            var isLast = index + 1 == total;
+            expect(packet).to.eql(err);
+            expect(isLast).to.eql(true);
+          });
+        });
+      });
+    });
+  });
+};
diff --git a/test/support/env.js b/test/support/env.js
new file mode 100644
index 0000000..c1d494e
--- /dev/null
+++ b/test/support/env.js
@@ -0,0 +1,5 @@
+// WARNING this is bad practice
+// we only do this in our tests because we need to test engine.io-client
+// support in browsers and in node.js
+// some tests do not yet work in both
+module.exports.browser = !!global.window;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-engine.io-parser.git



More information about the Pkg-javascript-commits mailing list