diff --git a/.zuul.yml b/.zuul.yml index b1b45e4a8..b990a01a0 100644 --- a/.zuul.yml +++ b/.zuul.yml @@ -8,6 +8,9 @@ browsers: - name: safari version: latest - name: ie - version: 6..latest + version: 10 + platform: Windows 2012 + - name: ie + version: [6..9, latest] - name: iphone version: oldest..latest diff --git a/lib/manager.js b/lib/manager.js index 808e31e60..bb252a506 100644 --- a/lib/manager.js +++ b/lib/manager.js @@ -43,6 +43,8 @@ function Manager(socket, opts){ this.engine = socket; this.connected = 0; this.attempts = 0; + this.encoding = false; + this.packetBuffer = []; this.open(); } @@ -266,11 +268,36 @@ Manager.prototype.destroy = function(socket){ Manager.prototype.packet = function(packet){ debug('writing packet %j', packet); - this.engine.write(parser.encode(packet)); + var self = this; + + if (!self.encoding) { // encode, then write to engine with result + self.encoding = true; + parser.encode(packet, function(encodedPacket) { + self.engine.write(encodedPacket); + self.encoding = false; + self.processPacketQueue(); + }); + } else { // add packet to the queue + self.packetBuffer.push(packet); + } }; /** - * Clean up transport subscriptions. + * If packet buffer is non-empty, begins encoding the + * next packet in line. + * + * @api private + */ + +Manager.prototype.processPacketQueue = function() { + if (this.packetBuffer.length > 0 && !this.encoding) { + var pack = this.packetBuffer.shift(); + this.packet(pack); + } +} + +/** + * Clean up transport subscriptions and packet buffer. * * @api private */ @@ -278,6 +305,9 @@ Manager.prototype.packet = function(packet){ Manager.prototype.cleanup = function(){ var sub; while (sub = this.subs.shift()) sub.destroy(); + + this.packetBuffer = []; + this.encoding = false; }; /** diff --git a/lib/socket.js b/lib/socket.js index 74f1b56cb..25fb5f153 100644 --- a/lib/socket.js +++ b/lib/socket.js @@ -9,6 +9,8 @@ var toArray = require('to-array'); var on = require('./on'); var bind = require('bind'); var debug = require('debug')('socket.io-client:socket'); +var hasBin = require('has-binarydata'); +var indexOf = require('indexof'); /** * Module exports. @@ -106,7 +108,9 @@ Socket.prototype.emit = function(ev){ } var args = toArray(arguments); - var packet = { type: parser.EVENT, data: args }; + var parserType = parser.EVENT; // default + if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary + var packet = { type: parserType, data: args }; // event ack callback if ('function' == typeof args[args.length - 1]) { @@ -198,6 +202,10 @@ Socket.prototype.onpacket = function(packet){ this.onevent(packet); break; + case parser.BINARY_EVENT: + this.onevent(packet); + break; + case parser.ACK: this.onack(packet); break; diff --git a/package.json b/package.json index 01fb7afd9..e65242605 100644 --- a/package.json +++ b/package.json @@ -10,23 +10,28 @@ "client" ], "dependencies": { - "engine.io-client": "0.9.0", + "engine.io-client": "LearnBoost/engine.io-client#b434277", "emitter": "http://github.com/component/emitter/archive/1.0.1.tar.gz", "bind": "http://github.com/component/bind/archive/0.0.1.tar.gz", "object-component": "0.0.3", - "socket.io-parser": "1.1.2", + "socket.io-parser": "2.0.0", "parseuri": "0.0.2", "to-array": "0.1.3", - "debug": "0.7.4" + "debug": "0.7.4", + "has-binarydata": "0.0.31", + "indexof": "0.0.1" }, "devDependencies": { "socket.io": "https://github.com/LearnBoost/socket.io/archive/980b6a7c40ff4d32bd3377cc1f16009f3bf189c9.tar.gz", "mocha": "1.16.2", - "zuul": "1.5.2", + "zuul": "1.5.4", "istanbul": "0.2.1", "expect.js": "0.2.0", "uglify-js": "2.4.8", - "browserify": "2.35.1" + "browserify": "2.35.1", + "base64-js": "0.0.6", + "text-blob-builder": "0.0.1", + "has-cors": "1.0.3" }, "scripts": { "test": "make test" diff --git a/socket.io.js b/socket.io.js index 8c4148e1d..bf4143a12 100644 --- a/socket.io.js +++ b/socket.io.js @@ -87,7 +87,7 @@ exports.connect = lookup; exports.Manager = require('./manager'); exports.Socket = require('./socket'); -},{"./manager":3,"./socket":5,"./url":6,"debug":8,"socket.io-parser":33}],3:[function(require,module,exports){ +},{"./manager":3,"./socket":5,"./url":6,"debug":9,"socket.io-parser":41}],3:[function(require,module,exports){ /** * Module dependencies. @@ -133,6 +133,8 @@ function Manager(socket, opts){ this.engine = socket; this.connected = 0; this.attempts = 0; + this.encoding = false; + this.packetBuffer = []; this.open(); } @@ -356,11 +358,36 @@ Manager.prototype.destroy = function(socket){ Manager.prototype.packet = function(packet){ debug('writing packet %j', packet); - this.engine.write(parser.encode(packet)); + var self = this; + + if (!self.encoding) { // encode, then write to engine with result + self.encoding = true; + parser.encode(packet, function(encodedPacket) { + self.engine.write(encodedPacket); + self.encoding = false; + self.processPacketQueue(); + }); + } else { // add packet to the queue + self.packetBuffer.push(packet); + } }; /** - * Clean up transport subscriptions. + * If packet buffer is non-empty, begins encoding the + * next packet in line. + * + * @api private + */ + +Manager.prototype.processPacketQueue = function() { + if (this.packetBuffer.length > 0 && !this.encoding) { + var pack = this.packetBuffer.shift(); + this.packet(pack); + } +} + +/** + * Clean up transport subscriptions and packet buffer. * * @api private */ @@ -368,6 +395,9 @@ Manager.prototype.packet = function(packet){ Manager.prototype.cleanup = function(){ var sub; while (sub = this.subs.shift()) sub.destroy(); + + this.packetBuffer = []; + this.encoding = false; }; /** @@ -451,7 +481,7 @@ Manager.prototype.onreconnect = function(){ this.emit('reconnect', attempt); }; -},{"./on":4,"./socket":5,"./url":6,"bind":7,"debug":8,"emitter":9,"engine.io-client":11,"object-component":31,"socket.io-parser":33}],4:[function(require,module,exports){ +},{"./on":4,"./socket":5,"./url":6,"bind":7,"debug":9,"emitter":10,"engine.io-client":12,"object-component":39,"socket.io-parser":41}],4:[function(require,module,exports){ /** * Module exports. @@ -489,6 +519,8 @@ var toArray = require('to-array'); var on = require('./on'); var bind = require('bind'); var debug = require('debug')('socket.io-client:socket'); +var hasBin = require('has-binarydata'); +var indexOf = require('indexof'); /** * Module exports. @@ -586,7 +618,9 @@ Socket.prototype.emit = function(ev){ } var args = toArray(arguments); - var packet = { type: parser.EVENT, data: args }; + var parserType = parser.EVENT; // default + if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary + var packet = { type: parserType, data: args }; // event ack callback if ('function' == typeof args[args.length - 1]) { @@ -678,6 +712,10 @@ Socket.prototype.onpacket = function(packet){ this.onevent(packet); break; + case parser.BINARY_EVENT: + this.onevent(packet); + break; + case parser.ACK: this.onack(packet); break; @@ -829,7 +867,7 @@ Socket.prototype.disconnect = function(){ return this; }; -},{"./on":4,"bind":7,"debug":8,"emitter":9,"socket.io-parser":33,"to-array":35}],6:[function(require,module,exports){ +},{"./on":4,"bind":7,"debug":9,"emitter":10,"has-binarydata":36,"indexof":38,"socket.io-parser":41,"to-array":49}],6:[function(require,module,exports){ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}; /** * Module dependencies. @@ -899,7 +937,7 @@ function url(uri, loc){ return obj; } -},{"debug":8,"parseuri":32}],7:[function(require,module,exports){ +},{"debug":9,"parseuri":40}],7:[function(require,module,exports){ /** * Slice reference. @@ -926,258 +964,2638 @@ module.exports = function(obj, fn){ }; },{}],8:[function(require,module,exports){ +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 1, + nBits = -7, + i = isBE ? 0 : (nBytes - 1), + d = isBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; -/** - * Expose `debug()` as the module. - */ - -module.exports = debug; +exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isBE ? (nBytes - 1) : 0, + d = isBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } -/** - * Create a debugger with the given `name`. - * - * @param {String} name - * @return {Type} - * @api public - */ + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } -function debug(name) { - if (!debug.enabled(name)) return function(){}; + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); - return function(fmt){ - fmt = coerce(fmt); + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); - var curr = new Date; - var ms = curr - (debug[name] || curr); - debug[name] = curr; + buffer[offset + i - d] |= s * 128; +}; - fmt = name - + ' ' - + fmt - + ' +' + debug.humanize(ms); +},{}],"q9TxCC":[function(require,module,exports){ +var assert; +exports.Buffer = Buffer; +exports.SlowBuffer = Buffer; +Buffer.poolSize = 8192; +exports.INSPECT_MAX_BYTES = 50; - // This hackery is required for IE8 - // where `console.log` doesn't have 'apply' - window.console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); - } +function stringtrim(str) { + if (str.trim) return str.trim(); + return str.replace(/^\s+|\s+$/g, ''); } -/** - * The currently active debug mode names. - */ +function Buffer(subject, encoding, offset) { + if(!assert) assert= require('assert'); + if (!(this instanceof Buffer)) { + return new Buffer(subject, encoding, offset); + } + this.parent = this; + this.offset = 0; + + // Work-around: node's base64 implementation + // allows for non-padded strings while base64-js + // does not.. + if (encoding == "base64" && typeof subject == "string") { + subject = stringtrim(subject); + while (subject.length % 4 != 0) { + subject = subject + "="; + } + } -debug.names = []; -debug.skips = []; + var type; -/** - * Enables a debug mode by name. This can include modes - * separated by a colon and wildcards. - * - * @param {String} name - * @api public - */ + // Are we slicing? + if (typeof offset === 'number') { + this.length = coerce(encoding); + // slicing works, with limitations (no parent tracking/update) + // check https://github.com/toots/buffer-browserify/issues/19 + for (var i = 0; i < this.length; i++) { + this[i] = subject.get(i+offset); + } + } else { + // Find the length + switch (type = typeof subject) { + case 'number': + this.length = coerce(subject); + break; -debug.enable = function(name) { - try { - localStorage.debug = name; - } catch(e){} + case 'string': + this.length = Buffer.byteLength(subject, encoding); + break; - var split = (name || '').split(/[\s,]+/) - , len = split.length; + case 'object': // Assume object is an array + this.length = coerce(subject.length); + break; - for (var i = 0; i < len; i++) { - name = split[i].replace('*', '.*?'); - if (name[0] === '-') { - debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + default: + throw new Error('First argument needs to be a number, ' + + 'array or string.'); } - else { - debug.names.push(new RegExp('^' + name + '$')); + + // Treat array-ish objects as a byte array. + if (isArrayIsh(subject)) { + for (var i = 0; i < this.length; i++) { + if (subject instanceof Buffer) { + this[i] = subject.readUInt8(i); + } + else { + this[i] = subject[i]; + } + } + } else if (type == 'string') { + // We are a string + this.length = this.write(subject, 0, encoding); + } else if (type === 'number') { + for (var i = 0; i < this.length; i++) { + this[i] = 0; + } } } -}; +} -/** - * Disable debug output. - * - * @api public - */ +Buffer.prototype.get = function get(i) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this[i]; +}; -debug.disable = function(){ - debug.enable(''); +Buffer.prototype.set = function set(i, v) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this[i] = v; }; -/** - * Humanize the given `ms`. - * - * @param {Number} m - * @return {String} - * @api private - */ +Buffer.byteLength = function (str, encoding) { + switch (encoding || "utf8") { + case 'hex': + return str.length / 2; -debug.humanize = function(ms) { - var sec = 1000 - , min = 60 * 1000 - , hour = 60 * min; + case 'utf8': + case 'utf-8': + return utf8ToBytes(str).length; - if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; - if (ms >= min) return (ms / min).toFixed(1) + 'm'; - if (ms >= sec) return (ms / sec | 0) + 's'; - return ms + 'ms'; -}; + case 'ascii': + case 'binary': + return str.length; -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ + case 'base64': + return base64ToBytes(str).length; -debug.enabled = function(name) { - for (var i = 0, len = debug.skips.length; i < len; i++) { - if (debug.skips[i].test(name)) { - return false; - } - } - for (var i = 0, len = debug.names.length; i < len; i++) { - if (debug.names[i].test(name)) { - return true; - } + default: + throw new Error('Unknown encoding'); } - return false; }; -/** - * Coerce `val`. - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} +Buffer.prototype.utf8Write = function (string, offset, length) { + var bytes, pos; + return Buffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); +}; -// persist +Buffer.prototype.asciiWrite = function (string, offset, length) { + var bytes, pos; + return Buffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); +}; -try { - if (window.localStorage) debug.enable(localStorage.debug); -} catch(e){} +Buffer.prototype.binaryWrite = Buffer.prototype.asciiWrite; -},{}],9:[function(require,module,exports){ +Buffer.prototype.base64Write = function (string, offset, length) { + var bytes, pos; + return Buffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); +}; -/** - * Module dependencies. - */ +Buffer.prototype.base64Slice = function (start, end) { + var bytes = Array.prototype.slice.apply(this, arguments) + return require("base64-js").fromByteArray(bytes); +}; -var index = require('indexof'); +Buffer.prototype.utf8Slice = function () { + var bytes = Array.prototype.slice.apply(this, arguments); + var res = ""; + var tmp = ""; + var i = 0; + while (i < bytes.length) { + if (bytes[i] <= 0x7F) { + res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); + tmp = ""; + } else + tmp += "%" + bytes[i].toString(16); + + i++; + } -/** - * Expose `Emitter`. - */ + return res + decodeUtf8Char(tmp); +} -module.exports = Emitter; +Buffer.prototype.asciiSlice = function () { + var bytes = Array.prototype.slice.apply(this, arguments); + var ret = ""; + for (var i = 0; i < bytes.length; i++) + ret += String.fromCharCode(bytes[i]); + return ret; +} -/** - * Initialize a new `Emitter`. - * - * @api public - */ +Buffer.prototype.binarySlice = Buffer.prototype.asciiSlice; -function Emitter(obj) { - if (obj) return mixin(obj); +Buffer.prototype.inspect = function() { + var out = [], + len = this.length; + for (var i = 0; i < len; i++) { + out[i] = toHex(this[i]); + if (i == exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...'; + break; + } + } + return ''; }; -/** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ -function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; -} +Buffer.prototype.hexSlice = function(start, end) { + var len = this.length; -/** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; -Emitter.prototype.on = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; + var out = ''; + for (var i = start; i < end; i++) { + out += toHex(this[i]); + } + return out; }; -/** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ -Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; +Buffer.prototype.toString = function(encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + start = +start || 0; + if (typeof end == 'undefined') end = this.length; - function on() { - self.off(event, on); - fn.apply(this, arguments); + // Fastpath empty strings + if (+end == start) { + return ''; } - fn._off = on; - this.on(event, on); - return this; -}; + switch (encoding) { + case 'hex': + return this.hexSlice(start, end); -/** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + case 'utf8': + case 'utf-8': + return this.utf8Slice(start, end); -Emitter.prototype.off = -Emitter.prototype.removeListener = -Emitter.prototype.removeAllListeners = function(event, fn){ - this._callbacks = this._callbacks || {}; + case 'ascii': + return this.asciiSlice(start, end); - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } + case 'binary': + return this.binarySlice(start, end); - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; + case 'base64': + return this.base64Slice(start, end); - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } + case 'ucs2': + case 'ucs-2': + return this.ucs2Slice(start, end); - // remove specific handler - var i = index(callbacks, fn._off || fn); - if (~i) callbacks.splice(i, 1); - return this; + default: + throw new Error('Unknown encoding'); + } +}; + + +Buffer.prototype.hexWrite = function(string, offset, length) { + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + + // must be an even number of digits + var strLen = string.length; + if (strLen % 2) { + throw new Error('Invalid hex string'); + } + if (length > strLen / 2) { + length = strLen / 2; + } + for (var i = 0; i < length; i++) { + var byte = parseInt(string.substr(i * 2, 2), 16); + if (isNaN(byte)) throw new Error('Invalid hex string'); + this[offset + i] = byte; + } + Buffer._charsWritten = i * 2; + return i; +}; + + +Buffer.prototype.write = function(string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; + } + + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + encoding = String(encoding || 'utf8').toLowerCase(); + + switch (encoding) { + case 'hex': + return this.hexWrite(string, offset, length); + + case 'utf8': + case 'utf-8': + return this.utf8Write(string, offset, length); + + case 'ascii': + return this.asciiWrite(string, offset, length); + + case 'binary': + return this.binaryWrite(string, offset, length); + + case 'base64': + return this.base64Write(string, offset, length); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Write(string, offset, length); + + default: + throw new Error('Unknown encoding'); + } +}; + +// slice(start, end) +function clamp(index, len, defaultValue) { + if (typeof index !== 'number') return defaultValue; + index = ~~index; // Coerce to integer. + if (index >= len) return len; + if (index >= 0) return index; + index += len; + if (index >= 0) return index; + return 0; +} + +Buffer.prototype.slice = function(start, end) { + var len = this.length; + start = clamp(start, len, 0); + end = clamp(end, len, len); + return new Buffer(this, end - start, +start); +}; + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function(target, target_start, start, end) { + var source = this; + start || (start = 0); + if (end === undefined || isNaN(end)) { + end = this.length; + } + target_start || (target_start = 0); + + if (end < start) throw new Error('sourceEnd < sourceStart'); + + // Copy 0 bytes; we're done + if (end === start) return 0; + if (target.length == 0 || source.length == 0) return 0; + + if (target_start < 0 || target_start >= target.length) { + throw new Error('targetStart out of bounds'); + } + + if (start < 0 || start >= source.length) { + throw new Error('sourceStart out of bounds'); + } + + if (end < 0 || end > source.length) { + throw new Error('sourceEnd out of bounds'); + } + + // Are we oob? + if (end > this.length) { + end = this.length; + } + + if (target.length - target_start < end - start) { + end = target.length - target_start + start; + } + + var temp = []; + for (var i=start; i= this.length) { + throw new Error('start out of bounds'); + } + + if (end < 0 || end > this.length) { + throw new Error('end out of bounds'); + } + + for (var i = start; i < end; i++) { + this[i] = value; + } +} + +// Static methods +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer || b instanceof Buffer; +}; + +Buffer.concat = function (list, totalLength) { + if (!isArray(list)) { + throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ + list should be an Array."); + } + + if (list.length === 0) { + return new Buffer(0); + } else if (list.length === 1) { + return list[0]; + } + + if (typeof totalLength !== 'number') { + totalLength = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + totalLength += buf.length; + } + } + + var buffer = new Buffer(totalLength); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; + } + return buffer; +}; + +Buffer.isEncoding = function(encoding) { + switch ((encoding + '').toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + case 'raw': + return true; + + default: + return false; + } +}; + +// helpers + +function coerce(length) { + // Coerce length to a number (possibly NaN), round up + // in case it's fractional (e.g. 123.456) then do a + // double negate to coerce a NaN to 0. Easy, right? + length = ~~Math.ceil(+length); + return length < 0 ? 0 : length; +} + +function isArray(subject) { + return (Array.isArray || + function(subject){ + return {}.toString.apply(subject) == '[object Array]' + }) + (subject) +} + +function isArrayIsh(subject) { + return isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number'; +} + +function toHex(n) { + if (n < 16) return '0' + n.toString(16); + return n.toString(16); +} + +function utf8ToBytes(str) { + var byteArray = []; + for (var i = 0; i < str.length; i++) + if (str.charCodeAt(i) <= 0x7F) + byteArray.push(str.charCodeAt(i)); + else { + var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); + for (var j = 0; j < h.length; j++) + byteArray.push(parseInt(h[j], 16)); + } + + return byteArray; +} + +function asciiToBytes(str) { + var byteArray = [] + for (var i = 0; i < str.length; i++ ) + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push( str.charCodeAt(i) & 0xFF ); + + return byteArray; +} + +function base64ToBytes(str) { + return require("base64-js").toByteArray(str); +} + +function blitBuffer(src, dst, offset, length) { + var pos, i = 0; + while (i < length) { + if ((i+offset >= dst.length) || (i >= src.length)) + break; + + dst[i + offset] = src[i]; + i++; + } + return i; +} + +function decodeUtf8Char(str) { + try { + return decodeURIComponent(str); + } catch (err) { + return String.fromCharCode(0xFFFD); // UTF 8 invalid char + } +} + +// read/write bit-twiddling + +Buffer.prototype.readUInt8 = function(offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return; + + return buffer[offset]; +}; + +function readUInt16(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return 0; + + if (isBigEndian) { + val = buffer[offset] << 8; + if (offset + 1 < buffer.length) { + val |= buffer[offset + 1]; + } + } else { + val = buffer[offset]; + if (offset + 1 < buffer.length) { + val |= buffer[offset + 1] << 8; + } + } + + return val; +} + +Buffer.prototype.readUInt16LE = function(offset, noAssert) { + return readUInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt16BE = function(offset, noAssert) { + return readUInt16(this, offset, true, noAssert); +}; + +function readUInt32(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return 0; + + if (isBigEndian) { + if (offset + 1 < buffer.length) + val = buffer[offset + 1] << 16; + if (offset + 2 < buffer.length) + val |= buffer[offset + 2] << 8; + if (offset + 3 < buffer.length) + val |= buffer[offset + 3]; + val = val + (buffer[offset] << 24 >>> 0); + } else { + if (offset + 2 < buffer.length) + val = buffer[offset + 2] << 16; + if (offset + 1 < buffer.length) + val |= buffer[offset + 1] << 8; + val |= buffer[offset]; + if (offset + 3 < buffer.length) + val = val + (buffer[offset + 3] << 24 >>> 0); + } + + return val; +} + +Buffer.prototype.readUInt32LE = function(offset, noAssert) { + return readUInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt32BE = function(offset, noAssert) { + return readUInt32(this, offset, true, noAssert); +}; + + +/* + * Signed integer types, yay team! A reminder on how two's complement actually + * works. The first bit is the signed bit, i.e. tells us whether or not the + * number should be positive or negative. If the two's complement value is + * positive, then we're done, as it's equivalent to the unsigned representation. + * + * Now if the number is positive, you're pretty much done, you can just leverage + * the unsigned translations and return those. Unfortunately, negative numbers + * aren't quite that straightforward. + * + * At first glance, one might be inclined to use the traditional formula to + * translate binary numbers between the positive and negative values in two's + * complement. (Though it doesn't quite work for the most negative value) + * Mainly: + * - invert all the bits + * - add one to the result + * + * Of course, this doesn't quite work in Javascript. Take for example the value + * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of + * course, Javascript will do the following: + * + * > ~0xff80 + * -65409 + * + * Whoh there, Javascript, that's not quite right. But wait, according to + * Javascript that's perfectly correct. When Javascript ends up seeing the + * constant 0xff80, it has no notion that it is actually a signed number. It + * assumes that we've input the unsigned value 0xff80. Thus, when it does the + * binary negation, it casts it into a signed value, (positive 0xff80). Then + * when you perform binary negation on that, it turns it into a negative number. + * + * Instead, we're going to have to use the following general formula, that works + * in a rather Javascript friendly way. I'm glad we don't support this kind of + * weird numbering scheme in the kernel. + * + * (BIT-MAX - (unsigned)val + 1) * -1 + * + * The astute observer, may think that this doesn't make sense for 8-bit numbers + * (really it isn't necessary for them). However, when you get 16-bit numbers, + * you do. Let's go back to our prior example and see how this will look: + * + * (0xffff - 0xff80 + 1) * -1 + * (0x007f + 1) * -1 + * (0x0080) * -1 + */ +Buffer.prototype.readInt8 = function(offset, noAssert) { + var buffer = this; + var neg; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return; + + neg = buffer[offset] & 0x80; + if (!neg) { + return (buffer[offset]); + } + + return ((0xff - buffer[offset] + 1) * -1); +}; + +function readInt16(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt16(buffer, offset, isBigEndian, noAssert); + neg = val & 0x8000; + if (!neg) { + return val; + } + + return (0xffff - val + 1) * -1; +} + +Buffer.prototype.readInt16LE = function(offset, noAssert) { + return readInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt16BE = function(offset, noAssert) { + return readInt16(this, offset, true, noAssert); +}; + +function readInt32(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt32(buffer, offset, isBigEndian, noAssert); + neg = val & 0x80000000; + if (!neg) { + return (val); + } + + return (0xffffffff - val + 1) * -1; +} + +Buffer.prototype.readInt32LE = function(offset, noAssert) { + return readInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt32BE = function(offset, noAssert) { + return readInt32(this, offset, true, noAssert); +}; + +function readFloat(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.readFloatLE = function(offset, noAssert) { + return readFloat(this, offset, false, noAssert); +}; + +Buffer.prototype.readFloatBE = function(offset, noAssert) { + return readFloat(this, offset, true, noAssert); +}; + +function readDouble(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return require('./buffer_ieee754').readIEEE754(buffer, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.readDoubleLE = function(offset, noAssert) { + return readDouble(this, offset, false, noAssert); +}; + +Buffer.prototype.readDoubleBE = function(offset, noAssert) { + return readDouble(this, offset, true, noAssert); +}; + + +/* + * We have to make sure that the value is a valid integer. This means that it is + * non-negative. It has no fractional component and that it does not exceed the + * maximum allowed value. + * + * value The number to check for validity + * + * max The maximum value + */ +function verifuint(value, max) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value >= 0, + 'specified a negative value for writing an unsigned value'); + + assert.ok(value <= max, 'value is larger than maximum value for type'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xff); + } + + if (offset < buffer.length) { + buffer[offset] = value; + } +}; + +function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffff); + } + + for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) { + buffer[offset + i] = + (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>> + (isBigEndian ? 1 - i : i) * 8; + } + +} + +Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, true, noAssert); +}; + +function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffffffff); + } + + for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { + buffer[offset + i] = + (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; + } +} + +Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, true, noAssert); +}; + + +/* + * We now move onto our friends in the signed number category. Unlike unsigned + * numbers, we're going to have to worry a bit more about how we put values into + * arrays. Since we are only worrying about signed 32-bit values, we're in + * slightly better shape. Unfortunately, we really can't do our favorite binary + * & in this system. It really seems to do the wrong thing. For example: + * + * > -32 & 0xff + * 224 + * + * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of + * this aren't treated as a signed number. Ultimately a bad thing. + * + * What we're going to want to do is basically create the unsigned equivalent of + * our representation and pass that off to the wuint* functions. To do that + * we're going to do the following: + * + * - if the value is positive + * we can pass it directly off to the equivalent wuint + * - if the value is negative + * we do the following computation: + * mb + val + 1, where + * mb is the maximum unsigned value in that byte size + * val is the Javascript negative integer + * + * + * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If + * you do out the computations: + * + * 0xffff - 128 + 1 + * 0xffff - 127 + * 0xff80 + * + * You can then encode this value as the signed version. This is really rather + * hacky, but it should work and get the job done which is our goal here. + */ + +/* + * A series of checks to make sure we actually have a signed 32-bit number + */ +function verifsint(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +function verifIEEE754(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); +} + +Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7f, -0x80); + } + + if (value >= 0) { + buffer.writeUInt8(value, offset, noAssert); + } else { + buffer.writeUInt8(0xff + value + 1, offset, noAssert); + } +}; + +function writeInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fff, -0x8000); + } + + if (value >= 0) { + writeUInt16(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + writeInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + writeInt16(this, value, offset, true, noAssert); +}; + +function writeInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fffffff, -0x80000000); + } + + if (value >= 0) { + writeUInt32(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + writeInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + writeInt32(this, value, offset, true, noAssert); +}; + +function writeFloat(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); + } + + require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { + writeFloat(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { + writeFloat(this, value, offset, true, noAssert); +}; + +function writeDouble(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); + } + + require('./buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { + writeDouble(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { + writeDouble(this, value, offset, true, noAssert); +}; + +},{"./buffer_ieee754":1,"assert":6,"base64-js":4}],"buffer-browserify":[function(require,module,exports){ +module.exports=require('q9TxCC'); +},{}],4:[function(require,module,exports){ +(function (exports) { + 'use strict'; + + var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + + function b64ToByteArray(b64) { + var i, j, l, tmp, placeHolders, arr; + + if (b64.length % 4 > 0) { + throw 'Invalid string. Length must be a multiple of 4'; + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + placeHolders = b64.indexOf('='); + placeHolders = placeHolders > 0 ? b64.length - placeHolders : 0; + + // base64 is 4/3 + up to two characters of the original data + arr = [];//new Uint8Array(b64.length * 3 / 4 - placeHolders); + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length; + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (lookup.indexOf(b64[i]) << 18) | (lookup.indexOf(b64[i + 1]) << 12) | (lookup.indexOf(b64[i + 2]) << 6) | lookup.indexOf(b64[i + 3]); + arr.push((tmp & 0xFF0000) >> 16); + arr.push((tmp & 0xFF00) >> 8); + arr.push(tmp & 0xFF); + } + + if (placeHolders === 2) { + tmp = (lookup.indexOf(b64[i]) << 2) | (lookup.indexOf(b64[i + 1]) >> 4); + arr.push(tmp & 0xFF); + } else if (placeHolders === 1) { + tmp = (lookup.indexOf(b64[i]) << 10) | (lookup.indexOf(b64[i + 1]) << 4) | (lookup.indexOf(b64[i + 2]) >> 2); + arr.push((tmp >> 8) & 0xFF); + arr.push(tmp & 0xFF); + } + + return arr; + } + + function uint8ToBase64(uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length; + + function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]; + }; + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]); + output += tripletToBase64(temp); + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1]; + output += lookup[temp >> 2]; + output += lookup[(temp << 4) & 0x3F]; + output += '=='; + break; + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]); + output += lookup[temp >> 10]; + output += lookup[(temp >> 4) & 0x3F]; + output += lookup[(temp << 2) & 0x3F]; + output += '='; + break; + } + + return output; + } + + module.exports.toByteArray = b64ToByteArray; + module.exports.fromByteArray = uint8ToBase64; +}()); + +},{}],5:[function(require,module,exports){ + + +// +// The shims in this file are not fully implemented shims for the ES5 +// features, but do work for the particular usecases there is in +// the other modules. +// + +var toString = Object.prototype.toString; +var hasOwnProperty = Object.prototype.hasOwnProperty; + +// Array.isArray is supported in IE9 +function isArray(xs) { + return toString.call(xs) === '[object Array]'; +} +exports.isArray = typeof Array.isArray === 'function' ? Array.isArray : isArray; + +// Array.prototype.indexOf is supported in IE9 +exports.indexOf = function indexOf(xs, x) { + if (xs.indexOf) return xs.indexOf(x); + for (var i = 0; i < xs.length; i++) { + if (x === xs[i]) return i; + } + return -1; +}; + +// Array.prototype.filter is supported in IE9 +exports.filter = function filter(xs, fn) { + if (xs.filter) return xs.filter(fn); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (fn(xs[i], i, xs)) res.push(xs[i]); + } + return res; +}; + +// Array.prototype.forEach is supported in IE9 +exports.forEach = function forEach(xs, fn, self) { + if (xs.forEach) return xs.forEach(fn, self); + for (var i = 0; i < xs.length; i++) { + fn.call(self, xs[i], i, xs); + } +}; + +// Array.prototype.map is supported in IE9 +exports.map = function map(xs, fn) { + if (xs.map) return xs.map(fn); + var out = new Array(xs.length); + for (var i = 0; i < xs.length; i++) { + out[i] = fn(xs[i], i, xs); + } + return out; +}; + +// Array.prototype.reduce is supported in IE9 +exports.reduce = function reduce(array, callback, opt_initialValue) { + if (array.reduce) return array.reduce(callback, opt_initialValue); + var value, isValueSet = false; + + if (2 < arguments.length) { + value = opt_initialValue; + isValueSet = true; + } + for (var i = 0, l = array.length; l > i; ++i) { + if (array.hasOwnProperty(i)) { + if (isValueSet) { + value = callback(value, array[i], i, array); + } + else { + value = array[i]; + isValueSet = true; + } + } + } + + return value; +}; + +// String.prototype.substr - negative index don't work in IE8 +if ('ab'.substr(-1) !== 'b') { + exports.substr = function (str, start, length) { + // did we get a negative start, calculate how much it is from the beginning of the string + if (start < 0) start = str.length + start; + + // call the original function + return str.substr(start, length); + }; +} else { + exports.substr = function (str, start, length) { + return str.substr(start, length); + }; +} + +// String.prototype.trim is supported in IE9 +exports.trim = function (str) { + if (str.trim) return str.trim(); + return str.replace(/^\s+|\s+$/g, ''); +}; + +// Function.prototype.bind is supported in IE9 +exports.bind = function () { + var args = Array.prototype.slice.call(arguments); + var fn = args.shift(); + if (fn.bind) return fn.bind.apply(fn, args); + var self = args.shift(); + return function () { + fn.apply(self, args.concat([Array.prototype.slice.call(arguments)])); + }; +}; + +// Object.create is supported in IE9 +function create(prototype, properties) { + var object; + if (prototype === null) { + object = { '__proto__' : null }; + } + else { + if (typeof prototype !== 'object') { + throw new TypeError( + 'typeof prototype[' + (typeof prototype) + '] != \'object\'' + ); + } + var Type = function () {}; + Type.prototype = prototype; + object = new Type(); + object.__proto__ = prototype; + } + if (typeof properties !== 'undefined' && Object.defineProperties) { + Object.defineProperties(object, properties); + } + return object; +} +exports.create = typeof Object.create === 'function' ? Object.create : create; + +// Object.keys and Object.getOwnPropertyNames is supported in IE9 however +// they do show a description and number property on Error objects +function notObject(object) { + return ((typeof object != "object" && typeof object != "function") || object === null); +} + +function keysShim(object) { + if (notObject(object)) { + throw new TypeError("Object.keys called on a non-object"); + } + + var result = []; + for (var name in object) { + if (hasOwnProperty.call(object, name)) { + result.push(name); + } + } + return result; +} + +// getOwnPropertyNames is almost the same as Object.keys one key feature +// is that it returns hidden properties, since that can't be implemented, +// this feature gets reduced so it just shows the length property on arrays +function propertyShim(object) { + if (notObject(object)) { + throw new TypeError("Object.getOwnPropertyNames called on a non-object"); + } + + var result = keysShim(object); + if (exports.isArray(object) && exports.indexOf(object, 'length') === -1) { + result.push('length'); + } + return result; +} + +var keys = typeof Object.keys === 'function' ? Object.keys : keysShim; +var getOwnPropertyNames = typeof Object.getOwnPropertyNames === 'function' ? + Object.getOwnPropertyNames : propertyShim; + +if (new Error().hasOwnProperty('description')) { + var ERROR_PROPERTY_FILTER = function (obj, array) { + if (toString.call(obj) === '[object Error]') { + array = exports.filter(array, function (name) { + return name !== 'description' && name !== 'number' && name !== 'message'; + }); + } + return array; + }; + + exports.keys = function (object) { + return ERROR_PROPERTY_FILTER(object, keys(object)); + }; + exports.getOwnPropertyNames = function (object) { + return ERROR_PROPERTY_FILTER(object, getOwnPropertyNames(object)); + }; +} else { + exports.keys = keys; + exports.getOwnPropertyNames = getOwnPropertyNames; +} + +// Object.getOwnPropertyDescriptor - supported in IE8 but only on dom elements +function valueObject(value, key) { + return { value: value[key] }; +} + +if (typeof Object.getOwnPropertyDescriptor === 'function') { + try { + Object.getOwnPropertyDescriptor({'a': 1}, 'a'); + exports.getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + } catch (e) { + // IE8 dom element issue - use a try catch and default to valueObject + exports.getOwnPropertyDescriptor = function (value, key) { + try { + return Object.getOwnPropertyDescriptor(value, key); + } catch (e) { + return valueObject(value, key); + } + }; + } +} else { + exports.getOwnPropertyDescriptor = valueObject; +} + +},{}],6:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node 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. + +// UTILITY +var util = require('util'); +var shims = require('_shims'); +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = module.exports = ok; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + this.message = options.message || getMessage(this); +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try { + var ka = shims.keys(a), + kb = shims.keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; +},{"_shims":5,"util":7}],7:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node 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. + +var shims = require('_shims'); + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + shims.forEach(array, function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = shims.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = shims.getOwnPropertyNames(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + + shims.forEach(keys, function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = shims.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (shims.indexOf(ctx.seen, desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = shims.reduce(output, function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return shims.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && objectToString(e) === '[object Error]'; +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +function isBuffer(arg) { + return arg instanceof Buffer; +} +exports.isBuffer = isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = shims.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); +}; + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = shims.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +},{"_shims":5}]},{},[]) +;;module.exports=require("buffer-browserify") + +},{}],9:[function(require,module,exports){ + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + fmt = coerce(fmt); + + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + try { + localStorage.debug = name; + } catch(e){} + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +/** + * Coerce `val`. + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + +// persist + +try { + if (window.localStorage) debug.enable(localStorage.debug); +} catch(e){} + +},{}],10:[function(require,module,exports){ + +/** + * Module dependencies. + */ + +var index = require('indexof'); + +/** + * Expose `Emitter`. + */ + +module.exports = Emitter; + +/** + * Initialize a new `Emitter`. + * + * @api public + */ + +function Emitter(obj) { + if (obj) return mixin(obj); +}; + +/** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; +} + +/** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.on = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; +}; + +/** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; + + function on() { + self.off(event, on); + fn.apply(this, arguments); + } + + fn._off = on; + this.on(event, on); + return this; +}; + +/** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.off = +Emitter.prototype.removeListener = +Emitter.prototype.removeAllListeners = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } + + // remove specific handler + var i = index(callbacks, fn._off || fn); + if (~i) callbacks.splice(i, 1); + return this; }; /** @@ -1228,7 +3646,7 @@ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; -},{"indexof":10}],10:[function(require,module,exports){ +},{"indexof":11}],11:[function(require,module,exports){ var indexOf = [].indexOf; @@ -1239,11 +3657,11 @@ module.exports = function(arr, obj){ } return -1; }; -},{}],11:[function(require,module,exports){ +},{}],12:[function(require,module,exports){ module.exports = require('./lib/'); -},{"./lib/":13}],12:[function(require,module,exports){ +},{"./lib/":14}],13:[function(require,module,exports){ /** * Module dependencies. @@ -1281,7 +3699,7 @@ Emitter.prototype.removeEventListener = Emitter.prototype.off; Emitter.prototype.removeListener = Emitter.prototype.off; -},{"emitter":9}],13:[function(require,module,exports){ +},{"emitter":26}],14:[function(require,module,exports){ module.exports = require('./socket'); @@ -1293,7 +3711,7 @@ module.exports = require('./socket'); */ module.exports.parser = require('engine.io-parser'); -},{"./socket":14,"engine.io-parser":24}],14:[function(require,module,exports){ +},{"./socket":15,"engine.io-parser":27}],15:[function(require,module,exports){ /** * Module dependencies. */ @@ -1371,6 +3789,7 @@ function Socket(uri, opts){ this.upgrade = false !== opts.upgrade; this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; this.forceJSONP = !!opts.forceJSONP; + this.forceBase64 = !!opts.forceBase64; this.timestampParam = opts.timestampParam || 't'; this.timestampRequests = opts.timestampRequests; this.flashPath = opts.flashPath || ''; @@ -1379,9 +3798,14 @@ function Socket(uri, opts){ this.writeBuffer = []; this.callbackBuffer = []; this.policyPort = opts.policyPort || 843; + this.rememberUpgrade = opts.rememberUpgrade || false; this.open(); + this.binaryType = null; + this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; } +Socket.priorWebsocketSuccess = false; + /** * Mix in `Emitter`. */ @@ -1437,10 +3861,12 @@ Socket.prototype.createTransport = function (name) { path: this.path, query: query, forceJSONP: this.forceJSONP, + forceBase64: this.forceBase64, timestampRequests: this.timestampRequests, timestampParam: this.timestampParam, flashPath: this.flashPath, - policyPort: this.policyPort + policyPort: this.policyPort, + socket: this }); return transport; @@ -1461,9 +3887,13 @@ function clone (obj) { * * @api private */ - Socket.prototype.open = function () { - var transport = this.transports[0]; + var transport; + if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { + transport = 'websocket'; + } else { + transport = this.transports[0]; + } this.readyState = 'opening'; var transport = this.createTransport(transport); transport.open(); @@ -1517,7 +3947,13 @@ Socket.prototype.probe = function (name) { , failed = false , self = this; + Socket.priorWebsocketSuccess = false; + transport.once('open', function () { + if (this.onlyBinaryUpgrades) { + var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; + failed = failed || upgradeLosesBinary; + } if (failed) return; debug('probe transport "%s" opened', name); @@ -1528,6 +3964,7 @@ Socket.prototype.probe = function (name) { debug('probe transport "%s" pong', name); self.upgrading = true; self.emit('upgrading', transport); + Socket.priorWebsocketSuccess = 'websocket' == transport.name; debug('pausing current transport "%s"', self.transport.name); self.transport.pause(function () { @@ -1537,9 +3974,9 @@ Socket.prototype.probe = function (name) { } debug('changing transport and sending upgrade packet'); transport.removeListener('error', onerror); - self.emit('upgrade', transport); self.setTransport(transport); transport.send([{ type: 'upgrade' }]); + self.emit('upgrade', transport); transport = null; self.upgrading = false; self.flush(); @@ -1600,6 +4037,7 @@ Socket.prototype.probe = function (name) { Socket.prototype.onOpen = function () { debug('socket open'); this.readyState = 'open'; + Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; this.emit('open'); this.onopen && this.onopen.call(this); this.flush(); @@ -1651,6 +4089,7 @@ Socket.prototype.onPacket = function (packet) { event.toString = function () { return packet.data; }; + this.onmessage && this.onmessage.call(this, event); break; } @@ -1729,7 +4168,7 @@ Socket.prototype.ping = function () { * @api private */ - Socket.prototype.onDrain = function() { +Socket.prototype.onDrain = function() { for (var i = 0; i < this.prevBufferLen; i++) { if (this.callbackBuffer[i]) { this.callbackBuffer[i](); @@ -1825,6 +4264,7 @@ Socket.prototype.close = function () { Socket.prototype.onError = function (err) { debug('socket error %j', err); + Socket.priorWebsocketSuccess = false; this.emit('error', err); this.onerror && this.onerror.call(this, err); this.onClose('transport error', err); @@ -1884,8 +4324,7 @@ Socket.prototype.filterUpgrades = function (upgrades) { return filteredUpgrades; }; -},{"./emitter":12,"./transport":15,"./transports":17,"./util":22,"debug":8,"engine.io-parser":24,"global":27,"indexof":29}],15:[function(require,module,exports){ - +},{"./emitter":13,"./transport":16,"./transports":18,"./util":23,"debug":25,"engine.io-parser":27,"global":32,"indexof":34}],16:[function(require,module,exports){ /** * Module dependencies. */ @@ -1917,6 +4356,7 @@ function Transport (opts) { this.timestampRequests = opts.timestampRequests; this.readyState = ''; this.agent = opts.agent || false; + this.socket = opts.socket; } /** @@ -2006,7 +4446,7 @@ Transport.prototype.onOpen = function () { */ Transport.prototype.onData = function (data) { - this.onPacket(parser.decodePacket(data)); + this.onPacket(parser.decodePacket(data, this.socket.binaryType)); }; /** @@ -2028,7 +4468,7 @@ Transport.prototype.onClose = function () { this.emit('close'); }; -},{"./emitter":12,"./util":22,"engine.io-parser":24}],16:[function(require,module,exports){ +},{"./emitter":13,"./util":23,"engine.io-parser":27}],17:[function(require,module,exports){ /** * Module dependencies. @@ -2082,6 +4522,12 @@ util.inherits(FlashWS, WS); FlashWS.prototype.name = 'flashsocket'; +/* + * FlashSockets only support binary as base64 encoded strings + */ + +FlashWS.prototype.supportsBinary = false; + /** * Opens the transport. * @@ -2122,7 +4568,7 @@ FlashWS.prototype.doOpen = function(){ load(deps, function(){ self.ready(function(){ WebSocket.__addTask(function () { - self.socket = new WebSocket(self.uri()); + self.ws = new WebSocket(self.uri()); self.addEventListeners(); }); }); @@ -2136,7 +4582,7 @@ FlashWS.prototype.doOpen = function(){ */ FlashWS.prototype.doClose = function(){ - if (!this.socket) return; + if (!this.ws) return; var self = this; WebSocket.__addTask(function(){ WS.prototype.doClose.call(self); @@ -2292,7 +4738,7 @@ function load(arr, fn){ process(0); } -},{"../util":22,"./websocket":21,"debug":8,"global":27}],17:[function(require,module,exports){ +},{"../util":23,"./websocket":22,"debug":25,"global":32}],18:[function(require,module,exports){ /** * Module dependencies @@ -2351,7 +4797,7 @@ function polling (opts) { } }; -},{"./flashsocket":16,"./polling-jsonp":18,"./polling-xhr":19,"./websocket":21,"global":27,"xmlhttprequest":23}],18:[function(require,module,exports){ +},{"./flashsocket":17,"./polling-jsonp":19,"./polling-xhr":20,"./websocket":22,"global":32,"xmlhttprequest":24}],19:[function(require,module,exports){ /** * Module requirements. @@ -2433,6 +4879,12 @@ function JSONPPolling (opts) { util.inherits(JSONPPolling, Polling); +/* + * JSONP only supports binary as base64 encoded strings + */ + +JSONPPolling.prototype.supportsBinary = false; + /** * Closes the socket * @@ -2572,7 +5024,7 @@ JSONPPolling.prototype.doWrite = function (data, fn) { } }; -},{"../util":22,"./polling":20,"global":27}],19:[function(require,module,exports){ +},{"../util":23,"./polling":21,"global":32}],20:[function(require,module,exports){ /** * Module requirements. */ @@ -2638,6 +5090,12 @@ function XHR(opts){ util.inherits(XHR, Polling); +/** + * XHR supports binary + */ + +XHR.prototype.supportsBinary = true; + /** * Creates a request. * @@ -2650,6 +5108,7 @@ XHR.prototype.request = function(opts){ opts.uri = this.uri(); opts.xd = this.xd; opts.agent = this.agent || false; + opts.supportsBinary = this.supportsBinary; return new Request(opts); }; @@ -2662,7 +5121,8 @@ XHR.prototype.request = function(opts){ */ XHR.prototype.doWrite = function(data, fn){ - var req = this.request({ method: 'POST', data: data }); + var isBinary = typeof data !== 'string' && data !== undefined; + var req = this.request({ method: 'POST', data: data, isBinary: isBinary }); var self = this; req.on('success', fn); req.on('error', function(err){ @@ -2704,7 +5164,7 @@ function Request(opts){ this.async = false !== opts.async; this.data = undefined != opts.data ? opts.data : null; this.agent = opts.agent; - this.create(); + this.create(opts.isBinary, opts.supportsBinary); } /** @@ -2719,17 +5179,26 @@ Emitter(Request.prototype); * @api private */ -Request.prototype.create = function(){ +Request.prototype.create = function(isBinary, supportsBinary){ var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd }); var self = this; try { debug('xhr open %s: %s', this.method, this.uri); xhr.open(this.method, this.uri, this.async); + if (supportsBinary) { + // This has to be done after open because Firefox is stupid + // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension + xhr.responseType = 'arraybuffer'; + } if ('POST' == this.method) { try { - xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); + if (isBinary) { + xhr.setRequestHeader('Content-type', 'application/octet-stream'); + } else { + xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); + } } catch (e) {} } @@ -2744,7 +5213,16 @@ Request.prototype.create = function(){ try { if (4 != xhr.readyState) return; if (200 == xhr.status || 1223 == xhr.status) { - data = xhr.responseText; + var contentType = xhr.getResponseHeader('Content-Type'); + if (contentType === 'application/octet-stream') { + data = xhr.response; + } else { + if (!supportsBinary) { + data = xhr.responseText; + } else { + data = 'ok'; + } + } } else { // make sure the `error` event handler that's user-set // does not throw in the same tick and gets caught here @@ -2764,7 +5242,7 @@ Request.prototype.create = function(){ debug('xhr data %s', this.data); xhr.send(this.data); } catch (e) { - // Need to defer since .create() is called directly from the constructor + // Need to defer since .create() is called directly fhrom the constructor // and thus the 'error' event can only be only bound *after* this exception // occurs. Therefore, also, we cannot throw here at all. setTimeout(function() { @@ -2855,16 +5333,18 @@ if (hasAttachEvent) { Request.requestsCount = 0; Request.requests = {}; - global.attachEvent('onunload', function(){ + function unloadHandler() { for (var i in Request.requests) { if (Request.requests.hasOwnProperty(i)) { Request.requests[i].abort(); } } - }); + } + + global.attachEvent('onunload', unloadHandler); } -},{"../emitter":12,"../util":22,"./polling":20,"debug":8,"global":27,"xmlhttprequest":23}],20:[function(require,module,exports){ +},{"../emitter":13,"../util":23,"./polling":21,"debug":25,"global":32,"xmlhttprequest":24}],21:[function(require,module,exports){ /** * Module dependencies. */ @@ -2886,6 +5366,16 @@ module.exports = Polling; var global = require('global'); +/** + * Is XHR2 supported? + */ + +var hasXHR2 = (function() { + var XMLHttpRequest = require('xmlhttprequest'); + var xhr = new XMLHttpRequest({ agent: this.agent, xdomain: false }); + return null != xhr.responseType; +})(); + /** * Polling interface. * @@ -2894,6 +5384,10 @@ var global = require('global'); */ function Polling(opts){ + var forceBase64 = (opts && opts.forceBase64); + if (!hasXHR2 || forceBase64) { + this.supportsBinary = false; + } Transport.call(this, opts); } @@ -2986,9 +5480,7 @@ Polling.prototype.poll = function(){ Polling.prototype.onData = function(data){ var self = this; debug('polling got data %s', data); - - // decode payload - parser.decodePayload(data, function(packet, index, total) { + var callback = function(packet, index, total) { // if its the first message we consider the transport open if ('opening' == self.readyState) { self.onOpen(); @@ -3002,7 +5494,10 @@ Polling.prototype.onData = function(data){ // otherwise bypass onData and handle the message self.onPacket(packet); - }); + }; + + // decode payload + parser.decodePayload(data, this.socket.binaryType, callback); // if an event did not trigger closing if ('closed' != this.readyState) { @@ -3054,9 +5549,14 @@ Polling.prototype.doClose = function(){ Polling.prototype.write = function(packets){ var self = this; this.writable = false; - this.doWrite(parser.encodePayload(packets), function(){ + var callbackfn = function() { self.writable = true; self.emit('drain'); + }; + + var self = this; + parser.encodePayload(packets, this.supportsBinary, function(data) { + self.doWrite(data, callbackfn); }); }; @@ -3082,6 +5582,10 @@ Polling.prototype.uri = function(){ } } + if (!this.supportsBinary && !query.sid) { + query.b64 = 1; + } + query = util.qs(query); // avoid port if default for schema @@ -3098,7 +5602,7 @@ Polling.prototype.uri = function(){ return schema + '://' + this.hostname + port + this.path + query; }; -},{"../transport":15,"../util":22,"debug":8,"engine.io-parser":24,"global":27}],21:[function(require,module,exports){ +},{"../transport":16,"../util":23,"debug":25,"engine.io-parser":27,"global":32,"xmlhttprequest":24}],22:[function(require,module,exports){ /** * Module dependencies. */ @@ -3136,6 +5640,10 @@ var global = require('global'); */ function WS(opts){ + var forceBase64 = (opts && opts.forceBase64); + if (forceBase64) { + this.supportsBinary = false; + } Transport.call(this, opts); } @@ -3153,6 +5661,12 @@ util.inherits(WS, Transport); WS.prototype.name = 'websocket'; +/* + * WebSockets support binary + */ + +WS.prototype.supportsBinary = true; + /** * Opens socket. * @@ -3170,7 +5684,13 @@ WS.prototype.doOpen = function(){ var protocols = void(0); var opts = { agent: this.agent }; - this.socket = new WebSocket(uri, protocols, opts); + this.ws = new WebSocket(uri, protocols, opts); + + if (this.ws.binaryType !== undefined) { + this.supportsBinary = false; + } + + this.ws.binaryType = 'arraybuffer'; this.addEventListeners(); }; @@ -3183,16 +5703,16 @@ WS.prototype.doOpen = function(){ WS.prototype.addEventListeners = function(){ var self = this; - this.socket.onopen = function(){ + this.ws.onopen = function(){ self.onOpen(); }; - this.socket.onclose = function(){ + this.ws.onclose = function(){ self.onClose(); }; - this.socket.onmessage = function(ev){ + this.ws.onmessage = function(ev){ self.onData(ev.data); }; - this.socket.onerror = function(e){ + this.ws.onerror = function(e){ self.onError('websocket error', e); }; }; @@ -3227,8 +5747,11 @@ WS.prototype.write = function(packets){ // encodePacket efficient as it uses WS framing // no need for encodePayload for (var i = 0, l = packets.length; i < l; i++) { - this.socket.send(parser.encodePacket(packets[i])); + parser.encodePacket(packets[i], this.supportsBinary, function(data) { + self.ws.send(data); + }); } + function ondrain() { self.writable = true; self.emit('drain'); @@ -3255,8 +5778,8 @@ WS.prototype.onClose = function(){ */ WS.prototype.doClose = function(){ - if (typeof this.socket !== 'undefined') { - this.socket.close(); + if (typeof this.ws !== 'undefined') { + this.ws.close(); } }; @@ -3282,6 +5805,11 @@ WS.prototype.uri = function(){ query[this.timestampParam] = +new Date; } + // communicate binary support capabilities + if (!this.supportsBinary) { + query.b64 = 1; + } + query = util.qs(query); // prepend ? to query @@ -3303,7 +5831,7 @@ WS.prototype.check = function(){ return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); }; -},{"../transport":15,"../util":22,"debug":8,"engine.io-parser":24,"global":27,"ws":30}],22:[function(require,module,exports){ +},{"../transport":16,"../util":23,"debug":25,"engine.io-parser":27,"global":32,"ws":35}],23:[function(require,module,exports){ var global = require('global'); @@ -3525,7 +6053,7 @@ exports.qsParse = function(qs){ return qry; }; -},{"global":27}],23:[function(require,module,exports){ +},{"global":32}],24:[function(require,module,exports){ // browser shim for xmlhttprequest module var hasCORS = require('has-cors'); @@ -3546,18 +6074,31 @@ module.exports = function(opts) { } } -},{"has-cors":28}],24:[function(require,module,exports){ -arguments[4][1][0].apply(exports,arguments) -},{"./lib/":25}],25:[function(require,module,exports){ -/** +},{"has-cors":33}],25:[function(require,module,exports){ +module.exports=require(9) +},{}],26:[function(require,module,exports){ +module.exports=require(10) +},{"indexof":34}],27:[function(require,module,exports){ +var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/** * Module dependencies. */ var keys = require('./keys'); +var sliceBuffer = require('arraybuffer.slice'); +var base64encoder = require('base64-arraybuffer'); +var after = require('after'); + +/** + * A utility for doing slicing, even when ArrayBuffer.prototype.slice doesn't + * exist + * + * @api private + */ /** * Current protocol version. */ + exports.protocol = 2; /** @@ -3582,21 +6123,69 @@ var packetslist = keys(packets); var err = { type: 'error', data: 'parser error' }; +/** + * Create a blob builder even when vendor prefixes exist + */ + +var BlobBuilder = global.BlobBuilder + || global.WebKitBlobBuilder + || global.MSBlobBuilder + || global.MozBlobBuilder; + +/** + * Check if Blob constructor is supported + */ + +var blobSupported = (function() { + try { + var b = new Blob(['hi']); + return b.size == 2; + } catch(e) { + return false; + } +})(); + +/** + * Check if BlobBuilder is supported + */ + +var blobBuilderSupported = !!BlobBuilder + && !!BlobBuilder.prototype.append + && !!BlobBuilder.prototype.getBlob; + /** * Encodes a packet. * - * [ `:` ] + * [ ] * * Example: * - * 5:hello world + * 5hello world * 3 * 4 * + * Binary is encoded in an identical principle + * * @api private */ -exports.encodePacket = function (packet) { +exports.encodePacket = function (packet, supportsBinary, callback) { + if (typeof supportsBinary == 'function') { + callback = supportsBinary; + supportsBinary = false; + } + + var data = (packet.data === undefined) + ? undefined + : packet.data.buffer || packet.data; + + if (global.ArrayBuffer && data instanceof ArrayBuffer) { + return encodeArrayBuffer(packet, supportsBinary, callback); + } else if (blobSupported && data instanceof Blob) { + return encodeBlob(packet, supportsBinary, callback); + } + + // Sending data as a utf-8 string var encoded = packets[packet.type]; // data fragment is optional @@ -3604,28 +6193,162 @@ exports.encodePacket = function (packet) { encoded += String(packet.data); } - return '' + encoded; + return callback('' + encoded); + +}; + +/** + * 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, callback); + }; + return fr.readAsArrayBuffer(packet.data); +}; + +function encodeBlob(packet, supportsBinary, callback) { + if (!supportsBinary) { + return exports.encodeBase64Packet(packet, callback); + } + + var length = new Uint8Array(1); + length[0] = packets[packet.type]; + var blob; + if (blobSupported) { + blob = new Blob([length.buffer, packet.data]); + } else { + var bb = new BlobBuilder(); + bb.append(length); + bb.append(packet.data); + blob = bb.getBlob(); + } + + return callback(blob); }; /** - * Decodes a packet. + * 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 (blobSupported && 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) { - var type = data.charAt(0); +exports.decodePacket = function (data, binaryType) { + // 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 (Number(type) != type || !packetslist[type]) { + 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] }; + } + } + + var asArray = new Uint8Array(data); + var type = asArray[0]; + var rest = sliceBuffer(data, 1); + if (blobSupported && binaryType === 'blob') { + rest = new Blob([rest]); + } else if (blobBuilderSupported && binaryType === 'blob') { + var bb = new BlobBuilder(); + bb.append(rest); + rest = bb.getBlob(); } + return { type: packetslist[type], data: rest }; +}; - if (data.length > 1) { - return { type: packetslist[type], data: data.substring(1) }; - } else { - return { type: packetslist[type] }; +/** + * 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' && blobSupported) { + data = new Blob([data]); + } else if (binaryType === 'blob' && blobBuilderSupported) { + var bb = new BlobBuilder(); + bb.append(data); + data = bb.getBlob(); } + + return { type: type, data: data }; }; /** @@ -3637,34 +6360,83 @@ exports.decodePacket = function (data) { * * 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) { - if (!packets.length) { - return '0:'; +exports.encodePayload = function (packets, supportsBinary, callback) { + if (typeof supportsBinary == 'function') { + callback = supportsBinary; + supportsBinary = null; } - var encoded = ''; - var message; + if (supportsBinary) { + if (blobSupported || blobBuilderSupported) { + return exports.encodePayloadAsBlob(packets, callback); + } + return exports.encodePayloadAsArrayBuffer(packets, callback); + } - for (var i = 0, l = packets.length; i < l; i++) { - message = exports.encodePacket(packets[i]); - encoded += message.length + ':' + message; + if (!packets.length) { + return callback('0:'); } - return encoded; + function setLengthHeader(message) { + return message.length + ':' + message; + }; + + function encodeOne(packet, doneCallback) { + exports.encodePacket(packet, supportsBinary, 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. + * 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, callback) { +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 @@ -3676,7 +6448,7 @@ exports.decodePayload = function (data, callback) { for (var i = 0, l = data.length; i < l; i++) { var chr = data.charAt(i); - + if (':' != chr) { length += chr; } else { @@ -3693,7 +6465,7 @@ exports.decodePayload = function (data, callback) { } if (msg.length) { - packet = exports.decodePacket(msg); + packet = exports.decodePacket(msg, binaryType); if (err.type == packet.type && err.data == packet.data) { // parser error in individual packet - ignoring payload @@ -3710,14 +6482,195 @@ exports.decodePayload = function (data, callback) { } } - if (length != '') { - // parser error - ignoring payload - return callback(err, 0, 1); + if (length != '') { + // parser error - ignoring payload + return callback(err, 0, 1); + } + +}; + +/** + * Encodes multiple messages (payload) as binary. + * + * <1 = binary, 0 = string>[...] + * + * 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, 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 + (new String(len)).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 = new String(ab.byteLength); + 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, 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 = new String(len); + 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 (blobSupported) { + var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); + doneCallback(null, blob); + } else { + var bb = new BlobBuilder(); + bb.append(binaryIdentifier); + bb.append(lengthAry.buffer); + bb.append(encoded); + var blob = bb.getBlob(); + doneCallback(null, blob); + } + }); + }; + + map(packets, encodeOne, function(err, results) { + if (blobSupported) { + return callback(new Blob(results)); + } + + var bb = new BlobBuilder(); + results.forEach(function(encoding) { + bb.append(encoding); + }); + return callback(bb.getBlob()); + }); +}; + +/* + * 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 = []; + + 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; + msgLength += tailArray[i]; + } + 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); + var basic = new Array(typed.length); + for (var i = 0; i < typed.length; i++) { + basic[i] = typed[i]; + } + msg = String.fromCharCode.apply(null, basic); + } + } + buffers.push(msg); + bufferTail = sliceBuffer(bufferTail, msgLength); } + var total = buffers.length; + buffers.forEach(function(buffer, i) { + callback(exports.decodePacket(buffer, binaryType), i, total); + }); }; -},{"./keys":26}],26:[function(require,module,exports){ +},{"./keys":28,"after":29,"arraybuffer.slice":30,"base64-arraybuffer":31}],28:[function(require,module,exports){ /** * Gets the keys for an object. @@ -3738,7 +6691,128 @@ module.exports = Object.keys || function keys (obj){ return arr; }; -},{}],27:[function(require,module,exports){ +},{}],29:[function(require,module,exports){ +module.exports = after + +function after(count, callback, err_cb) { + var bail = false + err_cb = err_cb || noop + proxy.count = count + + return (count === 0) ? callback() : proxy + + function proxy(err, result) { + if (proxy.count <= 0) { + throw new Error('after called too many times') + } + --proxy.count + + // after first error, rest are passed to err_cb + if (err) { + bail = true + callback(err) + // future error callbacks will go to error handler + callback = err_cb + } else if (proxy.count === 0 && !bail) { + callback(null, result) + } + } +} + +function noop() {} + +},{}],30:[function(require,module,exports){ +/** + * An abstraction for slicing an arraybuffer even when + * ArrayBuffer.prototype.slice is not supported + * + * @api private + */ + +module.exports = function(arraybuffer, start, end) { + var bytes = arraybuffer.byteLength; + start = start || 0; + end = end || bytes; + + if (arraybuffer.slice) { return arraybuffer.slice(start, end); } + + if (start < 0) { start += bytes; } + if (end < 0) { end += bytes; } + if (end > bytes) { end = bytes; } + + if (start >= bytes || start >= end || bytes == 0) { + return new ArrayBuffer(0); + } + + var abv = new Uint8Array(arraybuffer); + var result = new Uint8Array(end - start); + for (var i = start, ii = 0; i < end; i++, ii++) { + result[ii] = abv[i]; + } + return result.buffer; +}; + +},{}],31:[function(require,module,exports){ +/* + * base64-arraybuffer + * https://github.com/niklasvh/base64-arraybuffer + * + * Copyright (c) 2012 Niklas von Hertzen + * Licensed under the MIT license. + */ +(function(chars){ + "use strict"; + + exports.encode = function(arraybuffer) { + var bytes = new Uint8Array(arraybuffer), + i, len = bytes.buffer.byteLength, base64 = ""; + + for (i = 0; i < len; i+=3) { + base64 += chars[bytes.buffer[i] >> 2]; + base64 += chars[((bytes.buffer[i] & 3) << 4) | (bytes.buffer[i + 1] >> 4)]; + base64 += chars[((bytes.buffer[i + 1] & 15) << 2) | (bytes.buffer[i + 2] >> 6)]; + base64 += chars[bytes.buffer[i + 2] & 63]; + } + + if ((len % 3) === 2) { + base64 = base64.substring(0, base64.length - 1) + "="; + } else if (len % 3 === 1) { + base64 = base64.substring(0, base64.length - 2) + "=="; + } + + return base64; + }; + + exports.decode = function(base64) { + var bufferLength = base64.length * 0.75, + len = base64.length, i, p = 0, + encoded1, encoded2, encoded3, encoded4; + + if (base64[base64.length - 1] === "=") { + bufferLength--; + if (base64[base64.length - 2] === "=") { + bufferLength--; + } + } + + var arraybuffer = new ArrayBuffer(bufferLength), + bytes = new Uint8Array(arraybuffer); + + for (i = 0; i < len; i+=4) { + encoded1 = chars.indexOf(base64[i]); + encoded2 = chars.indexOf(base64[i+1]); + encoded3 = chars.indexOf(base64[i+2]); + encoded4 = chars.indexOf(base64[i+3]); + + bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); + bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); + bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); + } + + return arraybuffer; + }; +})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); +},{}],32:[function(require,module,exports){ /** * Returns `this`. Execute this without a "context" (i.e. without it being @@ -3748,7 +6822,7 @@ module.exports = Object.keys || function keys (obj){ module.exports = (function () { return this; })(); -},{}],28:[function(require,module,exports){ +},{}],33:[function(require,module,exports){ /** * Module dependencies. @@ -3773,9 +6847,9 @@ try { module.exports = false; } -},{"global":27}],29:[function(require,module,exports){ -module.exports=require(10) -},{}],30:[function(require,module,exports){ +},{"global":32}],34:[function(require,module,exports){ +module.exports=require(11) +},{}],35:[function(require,module,exports){ /** * Module dependencies. @@ -3820,7 +6894,69 @@ function ws(uri, protocols, opts) { if (WebSocket) ws.prototype = WebSocket.prototype; -},{}],31:[function(require,module,exports){ +},{}],36:[function(require,module,exports){ +var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},Buffer=require("__browserify_Buffer").Buffer;/* + * Module requirements. + */ + +var isArray = require('isarray'); + +/** + * Module exports. + */ + +module.exports = hasBinary; + +/** + * Checks for binary data. + * + * Right now only Buffer and ArrayBuffer are supported.. + * + * @param {Object} anything + * @api public + */ + +function hasBinary(data) { + + function recursiveCheckForBinary(obj) { + if (!obj) return false; + + if ( (global.Buffer && Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File) + ) { + return true; + } + + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + if (recursiveCheckForBinary(obj[i])) { + return true; + } + } + } else if (obj && 'object' == typeof obj) { + for (var key in obj) { + if (recursiveCheckForBinary(obj[key])) { + return true; + } + } + } + + return false; + } + + return recursiveCheckForBinary(data); +} + +},{"__browserify_Buffer":8,"isarray":37}],37:[function(require,module,exports){ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; + +},{}],38:[function(require,module,exports){ +module.exports=require(11) +},{}],39:[function(require,module,exports){ /** * HOP ref. @@ -3905,7 +7041,7 @@ exports.length = function(obj){ exports.isEmpty = function(obj){ return 0 == exports.length(obj); }; -},{}],32:[function(require,module,exports){ +},{}],40:[function(require,module,exports){ /** * Parses an URI * @@ -3932,14 +7068,18 @@ module.exports = function parseuri(str) { return uri; }; -},{}],33:[function(require,module,exports){ - +},{}],41:[function(require,module,exports){ +var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},Buffer=require("__browserify_Buffer").Buffer; /** * Module dependencies. */ var debug = require('debug')('socket.io-parser'); var json = require('json3'); +var msgpack = require('msgpack-js'); +var isArray = require('isarray'); +var base64 = require('base64-js'); + /** * Protocol version. @@ -3959,6 +7099,7 @@ exports.types = [ 'CONNECT', 'DISCONNECT', 'EVENT', + 'BINARY_EVENT', 'ACK', 'ERROR' ]; @@ -4004,14 +7145,41 @@ exports.ACK = 3; exports.ERROR = 4; /** - * Encode. + * Packet type 'binary event' + * + * @api public + */ + + exports.BINARY_EVENT = 5; + +/** + * Encode a packet as a string or buffer, depending on packet type. * * @param {Object} packet - * @return {String} encoded + * @return {String | Buffer} encoded * @api public */ -exports.encode = function(obj){ +exports.encode = function(obj, callback){ + debug('encoding packet %j', obj); + if (obj.type === exports.BINARY_EVENT) { + encodeAsBinary(obj, callback); + } + else { + var encoding = encodeAsString(obj); + callback(encoding); + } +}; + +/** + * Encode packet as string (used for anything that is not a binary event). + * + * @param {Object} packet + * @return {String} encoded + * @api private + */ + +function encodeAsString(obj) { var str = ''; var nsp = false; @@ -4042,17 +7210,119 @@ exports.encode = function(obj){ debug('encoded %j as %s', obj, str); return str; -}; +} /** - * Decode. + * Encode packet as Buffer (used for binary events). * - * @param {String} str + * @param {Object} packet + * @return {Buffer} encoded + * @api private + */ + +function encodeAsBinary(obj, callback) { + if (global.Blob || global.File) { + removeBlobs(obj, callback); + } else { + var encoding = msgpack.encode(obj); + callback(encoding); + } +} + +/** + * Asynchronously removes Blobs or Files from data via + * FileReaders readAsArrayBuffer method. Used before encoding + * data as msgpack. Calls callback with the blobless data. + * + * @param {Object} data + * @param {Function} callback + * @api private + */ + +function removeBlobs(data, callback) { + + function removeBlobsRecursive(obj, curKey, containingObject) { + if (!obj) return obj; + + // convert any blob + if ((global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File)) { + pendingBlobs++; + + // async filereader + var fileReader = new FileReader(); + fileReader.onload = function() { // this.result == arraybuffer + if (containingObject) { + containingObject[curKey] = this.result; + } + else { + bloblessData = this.result; + } + + // if nothing pending its callback time + if(! --pendingBlobs) { + callback(msgpack.encode(bloblessData)); + } + }; + + fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer + } + + // handle array + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + removeBlobsRecursive(obj[i], i, obj); + } + } else if (obj && 'object' == typeof obj) { // and object + for (var key in obj) { + removeBlobsRecursive(obj[key], key, obj); + } + } + } + + var pendingBlobs = 0; + var bloblessData = data; + removeBlobsRecursive(bloblessData); + if (!pendingBlobs) { + callback(msgpack.encode(bloblessData)); + } +} + +/** + * Decodes a packet Object (msgpack or string) into + * packet JSON. + * + * @param {Object} obj * @return {Object} packet * @api public */ -exports.decode = function(str){ +exports.decode = function(obj) { + if ('string' == typeof obj) { + return decodeString(obj); + } + else if ((global.Buffer && Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob)) { + return decodeBuffer(obj); + } + else if (obj.base64) { + return decodeBase64(obj.data); + } + else { + throw new Error('Unknown type: ' + obj); + } +} + +/** + * Decode a packet String (JSON data) + * + * @param {String} str + * @return {Object} packet + * @api private + */ + +function decodeString(str) { var p = {}; var i = 0; @@ -4102,6 +7372,73 @@ exports.decode = function(str){ return p; }; +/** + * Decode binary data packet into JSON packet + * + * @param {Buffer | ArrayBuffer | Blob} buf + * @return {Object} packet + * @api private + */ + +function decodeBuffer(buf) { + return msgpack.decode(buf); +}; + +/** + * Decode base64 msgpack string into a packet + * + * @param {String} b64 + * @return {Object} packet + * @api private + */ + +var NSP_SEP = 163; +var EVENT_SEP = 146; +var EVENT_STOP = 216; + +function decodeBase64(b64) { + var packet = {type: exports.BINARY_EVENT}; + var bytes = base64.toByteArray(b64); + + var nsp = ''; + var eventName = ''; + var data = []; + var currentThing; + + for (var i = 0; i < bytes.length; i++) { + var b = bytes[i]; + if (!currentThing) { + if (b == EVENT_SEP && !eventName) { + currentThing = 'ev'; + i += 1; // skip the next thing which is another seperator + } + } + else if (currentThing == 'nsp') { + nsp += String.fromCharCode(b); + } + else if (currentThing == 'ev') { + if (b != EVENT_STOP) { + eventName += String.fromCharCode(b); + } else { + currentThing = 'data'; + i += 2; // next two bytes are 0 and another seperator + } + } + else if (currentThing == 'data') { + if (b != NSP_SEP) { + data.push(b); + } else { + currentThing = 'nsp'; + i += 4; // next three chars are 'nsp', then another seperator + } + } + } + + packet.nsp = nsp; + packet.data = [eventName, {base64: true, data: base64.fromByteArray(data)}]; + return packet; +}; + function error(data){ return { type: exports.ERROR, @@ -4109,895 +7446,986 @@ function error(data){ }; } -},{"debug":8,"json3":34}],34:[function(require,module,exports){ -var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/*! JSON v3.3.0 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ -;(function (root) { +},{"__browserify_Buffer":8,"base64-js":42,"debug":43,"isarray":44,"json3":45,"msgpack-js":46}],42:[function(require,module,exports){ +var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +;(function (exports) { + 'use strict'; + + var Arr = (typeof Uint8Array !== 'undefined') + ? Uint8Array + : Array + + var ZERO = '0'.charCodeAt(0) + var PLUS = '+'.charCodeAt(0) + var SLASH = '/'.charCodeAt(0) + var NUMBER = '0'.charCodeAt(0) + var LOWER = 'a'.charCodeAt(0) + var UPPER = 'A'.charCodeAt(0) + + function decode (elt) { + var code = elt.charCodeAt(0) + if (code === PLUS) + return 62 // '+' + if (code === SLASH) + return 63 // '/' + if (code < NUMBER) + return -1 //no match + if (code < NUMBER + 10) + return code - NUMBER + 26 + 26 + if (code < UPPER + 26) + return code - UPPER + if (code < LOWER + 26) + return code - LOWER + 26 + } + + function b64ToByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + + if (b64.length % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + var len = b64.length + placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(b64.length * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length + + var L = 0 + + function push (v) { + arr[L++] = v + } + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) + push((tmp & 0xFF0000) >> 16) + push((tmp & 0xFF00) >> 8) + push(tmp & 0xFF) + } + + if (placeHolders === 2) { + tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) + push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) + push((tmp >> 8) & 0xFF) + push(tmp & 0xFF) + } + + return arr + } + + function uint8ToBase64 (uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length + + function encode (num) { + return lookup.charAt(num) + } + + function tripletToBase64 (num) { + return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) + } + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output += tripletToBase64(temp) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1] + output += encode(temp >> 2) + output += encode((temp << 4) & 0x3F) + output += '==' + break + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) + output += encode(temp >> 10) + output += encode((temp >> 4) & 0x3F) + output += encode((temp << 2) & 0x3F) + output += '=' + break + } + + return output + } + + module.exports.toByteArray = b64ToByteArray + module.exports.fromByteArray = uint8ToBase64 +}()) + +},{}],43:[function(require,module,exports){ +module.exports=require(9) +},{}],44:[function(require,module,exports){ +module.exports=require(37) +},{}],45:[function(require,module,exports){ +/*! JSON v3.2.6 | http://bestiejs.github.io/json3 | Copyright 2012-2013, Kit Cambridge | http://kit.mit-license.org */ +;(function (window) { + // Convenience aliases. + var getClass = {}.toString, isProperty, forEach, undef; + // Detect the `define` function exposed by asynchronous module loaders. The // strict `define` check is necessary for compatibility with `r.js`. var isLoader = typeof define === "function" && define.amd; - // Use the `global` object exposed by Node (including Browserify via - // `insert-module-globals`), Narwhal, and Ringo as the default context. - // Rhino exports a `global` function instead. - var freeGlobal = typeof global == "object" && global; - if (freeGlobal && (freeGlobal["global"] === freeGlobal || freeGlobal["window"] === freeGlobal)) { - root = freeGlobal; - } - - // Public: Initializes JSON 3 using the given `context` object, attaching the - // `stringify` and `parse` functions to the specified `exports` object. - function runInContext(context, exports) { - context || (context = root["Object"]()); - exports || (exports = root["Object"]()); - - // Native constructor aliases. - var Number = context["Number"] || root["Number"], - String = context["String"] || root["String"], - Object = context["Object"] || root["Object"], - Date = context["Date"] || root["Date"], - SyntaxError = context["SyntaxError"] || root["SyntaxError"], - TypeError = context["TypeError"] || root["TypeError"], - Math = context["Math"] || root["Math"], - nativeJSON = context["JSON"] || root["JSON"]; - - // Delegate to the native `stringify` and `parse` implementations. - if (typeof nativeJSON == "object" && nativeJSON) { - exports.stringify = nativeJSON.stringify; - exports.parse = nativeJSON.parse; - } - - // Convenience aliases. - var objectProto = Object.prototype, - getClass = objectProto.toString, - isProperty, forEach, undef; - - // Test the `Date#getUTC*` methods. Based on work by @Yaffle. - var isExtended = new Date(-3509827334573292); - try { - // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical - // results for certain dates in Opera >= 10.53. - isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && - // Safari < 2.0.2 stores the internal millisecond time value correctly, - // but clips the values returned by the date methods to the range of - // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). - isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; - } catch (exception) {} - - // Internal: Determines whether the native `JSON.stringify` and `parse` - // implementations are spec-compliant. Based on work by Ken Snyder. - function has(name) { - if (has[name] !== undef) { - // Return cached feature test result. - return has[name]; - } - var isSupported; - if (name == "bug-string-char-index") { - // IE <= 7 doesn't support accessing string characters using square - // bracket notation. IE 8 only supports this for primitives. - isSupported = "a"[0] != "a"; - } else if (name == "json") { - // Indicates whether both `JSON.stringify` and `JSON.parse` are - // supported. - isSupported = has("json-stringify") && has("json-parse"); - } else { - var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; - // Test `JSON.stringify`. - if (name == "json-stringify") { - var stringify = exports.stringify, stringifySupported = typeof stringify == "function" && isExtended; - if (stringifySupported) { - // A test function object with a custom `toJSON` method. - (value = function () { - return 1; - }).toJSON = value; - try { - stringifySupported = - // Firefox 3.1b1 and b2 serialize string, number, and boolean - // primitives as object literals. - stringify(0) === "0" && - // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object - // literals. - stringify(new Number()) === "0" && - stringify(new String()) == '""' && - // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or - // does not define a canonical JSON representation (this applies to - // objects with `toJSON` properties as well, *unless* they are nested - // within an object or array). - stringify(getClass) === undef && - // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and - // FF 3.1b3 pass this test. - stringify(undef) === undef && - // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, - // respectively, if the value is omitted entirely. - stringify() === undef && - // FF 3.1b1, 2 throw an error if the given value is not a number, - // string, array, object, Boolean, or `null` literal. This applies to - // objects with custom `toJSON` methods as well, unless they are nested - // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` - // methods entirely. - stringify(value) === "1" && - stringify([value]) == "[1]" && - // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of - // `"[null]"`. - stringify([undef]) == "[null]" && - // YUI 3.0.0b1 fails to serialize `null` literals. - stringify(null) == "null" && - // FF 3.1b1, 2 halts serialization if an array contains a function: - // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 - // elides non-JSON values from objects and arrays, unless they - // define custom `toJSON` methods. - stringify([undef, getClass, null]) == "[null,null,null]" && - // Simple serialization test. FF 3.1b1 uses Unicode escape sequences - // where character escape codes are expected (e.g., `\b` => `\u0008`). - stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && - // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. - stringify(null, value) === "1" && - stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && - // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly - // serialize extended years. - stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && - // The milliseconds are optional in ES 5, but required in 5.1. - stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && - // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative - // four-digit years instead of six-digit years. Credits: @Yaffle. - stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && - // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond - // values less than 1000. Credits: @Yaffle. - stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; - } catch (exception) { - stringifySupported = false; - } + // Detect native implementations. + var nativeJSON = typeof JSON == "object" && JSON; + + // Set up the JSON 3 namespace, preferring the CommonJS `exports` object if + // available. + var JSON3 = typeof exports == "object" && exports && !exports.nodeType && exports; + + if (JSON3 && nativeJSON) { + // Explicitly delegate to the native `stringify` and `parse` + // implementations in CommonJS environments. + JSON3.stringify = nativeJSON.stringify; + JSON3.parse = nativeJSON.parse; + } else { + // Export for web browsers, JavaScript engines, and asynchronous module + // loaders, using the global `JSON` object if available. + JSON3 = window.JSON = nativeJSON || {}; + } + + // Test the `Date#getUTC*` methods. Based on work by @Yaffle. + var isExtended = new Date(-3509827334573292); + try { + // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical + // results for certain dates in Opera >= 10.53. + isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && + // Safari < 2.0.2 stores the internal millisecond time value correctly, + // but clips the values returned by the date methods to the range of + // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). + isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; + } catch (exception) {} + + // Internal: Determines whether the native `JSON.stringify` and `parse` + // implementations are spec-compliant. Based on work by Ken Snyder. + function has(name) { + if (has[name] !== undef) { + // Return cached feature test result. + return has[name]; + } + + var isSupported; + if (name == "bug-string-char-index") { + // IE <= 7 doesn't support accessing string characters using square + // bracket notation. IE 8 only supports this for primitives. + isSupported = "a"[0] != "a"; + } else if (name == "json") { + // Indicates whether both `JSON.stringify` and `JSON.parse` are + // supported. + isSupported = has("json-stringify") && has("json-parse"); + } else { + var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; + // Test `JSON.stringify`. + if (name == "json-stringify") { + var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended; + if (stringifySupported) { + // A test function object with a custom `toJSON` method. + (value = function () { + return 1; + }).toJSON = value; + try { + stringifySupported = + // Firefox 3.1b1 and b2 serialize string, number, and boolean + // primitives as object literals. + stringify(0) === "0" && + // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object + // literals. + stringify(new Number()) === "0" && + stringify(new String()) == '""' && + // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or + // does not define a canonical JSON representation (this applies to + // objects with `toJSON` properties as well, *unless* they are nested + // within an object or array). + stringify(getClass) === undef && + // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and + // FF 3.1b3 pass this test. + stringify(undef) === undef && + // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, + // respectively, if the value is omitted entirely. + stringify() === undef && + // FF 3.1b1, 2 throw an error if the given value is not a number, + // string, array, object, Boolean, or `null` literal. This applies to + // objects with custom `toJSON` methods as well, unless they are nested + // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` + // methods entirely. + stringify(value) === "1" && + stringify([value]) == "[1]" && + // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of + // `"[null]"`. + stringify([undef]) == "[null]" && + // YUI 3.0.0b1 fails to serialize `null` literals. + stringify(null) == "null" && + // FF 3.1b1, 2 halts serialization if an array contains a function: + // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 + // elides non-JSON values from objects and arrays, unless they + // define custom `toJSON` methods. + stringify([undef, getClass, null]) == "[null,null,null]" && + // Simple serialization test. FF 3.1b1 uses Unicode escape sequences + // where character escape codes are expected (e.g., `\b` => `\u0008`). + stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && + // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. + stringify(null, value) === "1" && + stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && + // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly + // serialize extended years. + stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && + // The milliseconds are optional in ES 5, but required in 5.1. + stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && + // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative + // four-digit years instead of six-digit years. Credits: @Yaffle. + stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && + // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond + // values less than 1000. Credits: @Yaffle. + stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; + } catch (exception) { + stringifySupported = false; } - isSupported = stringifySupported; } - // Test `JSON.parse`. - if (name == "json-parse") { - var parse = exports.parse; - if (typeof parse == "function") { - try { - // FF 3.1b1, b2 will throw an exception if a bare literal is provided. - // Conforming implementations should also coerce the initial argument to - // a string prior to parsing. - if (parse("0") === 0 && !parse(false)) { - // Simple parsing test. - value = parse(serialized); - var parseSupported = value["a"].length == 5 && value["a"][0] === 1; + isSupported = stringifySupported; + } + // Test `JSON.parse`. + if (name == "json-parse") { + var parse = JSON3.parse; + if (typeof parse == "function") { + try { + // FF 3.1b1, b2 will throw an exception if a bare literal is provided. + // Conforming implementations should also coerce the initial argument to + // a string prior to parsing. + if (parse("0") === 0 && !parse(false)) { + // Simple parsing test. + value = parse(serialized); + var parseSupported = value["a"].length == 5 && value["a"][0] === 1; + if (parseSupported) { + try { + // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. + parseSupported = !parse('"\t"'); + } catch (exception) {} if (parseSupported) { try { - // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. - parseSupported = !parse('"\t"'); + // FF 4.0 and 4.0.1 allow leading `+` signs and leading + // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow + // certain octal literals. + parseSupported = parse("01") !== 1; + } catch (exception) {} + } + if (parseSupported) { + try { + // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal + // points. These environments, along with FF 3.1b1 and 2, + // also allow trailing commas in JSON objects and arrays. + parseSupported = parse("1.") !== 1; } catch (exception) {} - if (parseSupported) { - try { - // FF 4.0 and 4.0.1 allow leading `+` signs and leading - // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow - // certain octal literals. - parseSupported = parse("01") !== 1; - } catch (exception) {} - } - if (parseSupported) { - try { - // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal - // points. These environments, along with FF 3.1b1 and 2, - // also allow trailing commas in JSON objects and arrays. - parseSupported = parse("1.") !== 1; - } catch (exception) {} - } } } - } catch (exception) { - parseSupported = false; } + } catch (exception) { + parseSupported = false; } - isSupported = parseSupported; } + isSupported = parseSupported; } - return has[name] = !!isSupported; - } - - if (!has("json")) { - // Common `[[Class]]` name aliases. - var functionClass = "[object Function]", - dateClass = "[object Date]", - numberClass = "[object Number]", - stringClass = "[object String]", - arrayClass = "[object Array]", - booleanClass = "[object Boolean]"; - - // Detect incomplete support for accessing string characters by index. - var charIndexBuggy = has("bug-string-char-index"); - - // Define additional utility methods if the `Date` methods are buggy. - if (!isExtended) { - var floor = Math.floor; - // A mapping between the months of the year and the number of days between - // January 1st and the first of the respective month. - var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; - // Internal: Calculates the number of days between the Unix epoch and the - // first day of the given month. - var getDay = function (year, month) { - return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); - }; - } - - // Internal: Determines if a property is a direct property of the given - // object. Delegates to the native `Object#hasOwnProperty` method. - if (!(isProperty = objectProto.hasOwnProperty)) { - isProperty = function (property) { - var members = {}, constructor; - if ((members.__proto__ = null, members.__proto__ = { - // The *proto* property cannot be set multiple times in recent - // versions of Firefox and SeaMonkey. - "toString": 1 - }, members).toString != getClass) { - // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but - // supports the mutable *proto* property. - isProperty = function (property) { - // Capture and break the objectgs prototype chain (see section 8.6.2 - // of the ES 5.1 spec). The parenthesized expression prevents an - // unsafe transformation by the Closure Compiler. - var original = this.__proto__, result = property in (this.__proto__ = null, this); - // Restore the original prototype chain. - this.__proto__ = original; - return result; - }; - } else { - // Capture a reference to the top-level `Object` constructor. - constructor = members.constructor; - // Use the `constructor` property to simulate `Object#hasOwnProperty` in - // other environments. - isProperty = function (property) { - var parent = (this.constructor || constructor).prototype; - return property in this && !(property in parent && this[property] === parent[property]); - }; - } - members = null; - return isProperty.call(this, property); - }; - } + } + return has[name] = !!isSupported; + } - // Internal: A set of primitive types used by `isHostType`. - var PrimitiveTypes = { - "boolean": 1, - "number": 1, - "string": 1, - "undefined": 1 + if (!has("json")) { + // Common `[[Class]]` name aliases. + var functionClass = "[object Function]"; + var dateClass = "[object Date]"; + var numberClass = "[object Number]"; + var stringClass = "[object String]"; + var arrayClass = "[object Array]"; + var booleanClass = "[object Boolean]"; + + // Detect incomplete support for accessing string characters by index. + var charIndexBuggy = has("bug-string-char-index"); + + // Define additional utility methods if the `Date` methods are buggy. + if (!isExtended) { + var floor = Math.floor; + // A mapping between the months of the year and the number of days between + // January 1st and the first of the respective month. + var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + // Internal: Calculates the number of days between the Unix epoch and the + // first day of the given month. + var getDay = function (year, month) { + return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); }; + } - // Internal: Determines if the given object `property` value is a - // non-primitive. - var isHostType = function (object, property) { - var type = typeof object[property]; - return type == "object" ? !!object[property] : !PrimitiveTypes[type]; + // Internal: Determines if a property is a direct property of the given + // object. Delegates to the native `Object#hasOwnProperty` method. + if (!(isProperty = {}.hasOwnProperty)) { + isProperty = function (property) { + var members = {}, constructor; + if ((members.__proto__ = null, members.__proto__ = { + // The *proto* property cannot be set multiple times in recent + // versions of Firefox and SeaMonkey. + "toString": 1 + }, members).toString != getClass) { + // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but + // supports the mutable *proto* property. + isProperty = function (property) { + // Capture and break the object's prototype chain (see section 8.6.2 + // of the ES 5.1 spec). The parenthesized expression prevents an + // unsafe transformation by the Closure Compiler. + var original = this.__proto__, result = property in (this.__proto__ = null, this); + // Restore the original prototype chain. + this.__proto__ = original; + return result; + }; + } else { + // Capture a reference to the top-level `Object` constructor. + constructor = members.constructor; + // Use the `constructor` property to simulate `Object#hasOwnProperty` in + // other environments. + isProperty = function (property) { + var parent = (this.constructor || constructor).prototype; + return property in this && !(property in parent && this[property] === parent[property]); + }; + } + members = null; + return isProperty.call(this, property); }; + } - // Internal: Normalizes the `for...in` iteration algorithm across - // environments. Each enumerated key is yielded to a `callback` function. - forEach = function (object, callback) { - var size = 0, Properties, members, property; - - // Tests for bugs in the current environment's `for...in` algorithm. The - // `valueOf` property inherits the non-enumerable flag from - // `Object.prototype` in older versions of IE, Netscape, and Mozilla. - (Properties = function () { - this.valueOf = 0; - }).prototype.valueOf = 0; - - // Iterate over a new instance of the `Properties` class. - members = new Properties(); - for (property in members) { - // Ignore all properties inherited from `Object.prototype`. - if (isProperty.call(members, property)) { - size++; - } + // Internal: A set of primitive types used by `isHostType`. + var PrimitiveTypes = { + 'boolean': 1, + 'number': 1, + 'string': 1, + 'undefined': 1 + }; + + // Internal: Determines if the given object `property` value is a + // non-primitive. + var isHostType = function (object, property) { + var type = typeof object[property]; + return type == 'object' ? !!object[property] : !PrimitiveTypes[type]; + }; + + // Internal: Normalizes the `for...in` iteration algorithm across + // environments. Each enumerated key is yielded to a `callback` function. + forEach = function (object, callback) { + var size = 0, Properties, members, property; + + // Tests for bugs in the current environment's `for...in` algorithm. The + // `valueOf` property inherits the non-enumerable flag from + // `Object.prototype` in older versions of IE, Netscape, and Mozilla. + (Properties = function () { + this.valueOf = 0; + }).prototype.valueOf = 0; + + // Iterate over a new instance of the `Properties` class. + members = new Properties(); + for (property in members) { + // Ignore all properties inherited from `Object.prototype`. + if (isProperty.call(members, property)) { + size++; } - Properties = members = null; - - // Normalize the iteration algorithm. - if (!size) { - // A list of non-enumerable properties inherited from `Object.prototype`. - members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; - // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable - // properties. - forEach = function (object, callback) { - var isFunction = getClass.call(object) == functionClass, property, length; - var hasProperty = !isFunction && typeof object.constructor != "function" && isHostType(object, "hasOwnProperty") ? object.hasOwnProperty : isProperty; - for (property in object) { - // Gecko <= 1.0 enumerates the `prototype` property of functions under - // certain conditions; IE does not. - if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { - callback(property); - } - } - // Manually invoke the callback for each non-enumerable property. - for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); - }; - } else if (size == 2) { - // Safari <= 2.0.4 enumerates shadowed properties twice. - forEach = function (object, callback) { - // Create a set of iterated properties. - var members = {}, isFunction = getClass.call(object) == functionClass, property; - for (property in object) { - // Store each property name to prevent double enumeration. The - // `prototype` property of functions is not enumerated due to cross- - // environment inconsistencies. - if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { - callback(property); - } + } + Properties = members = null; + + // Normalize the iteration algorithm. + if (!size) { + // A list of non-enumerable properties inherited from `Object.prototype`. + members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; + // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable + // properties. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, length; + var hasProperty = !isFunction && typeof object.constructor != 'function' && isHostType(object, 'hasOwnProperty') ? object.hasOwnProperty : isProperty; + for (property in object) { + // Gecko <= 1.0 enumerates the `prototype` property of functions under + // certain conditions; IE does not. + if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { + callback(property); } - }; - } else { - // No bugs detected; use the standard `for...in` algorithm. - forEach = function (object, callback) { - var isFunction = getClass.call(object) == functionClass, property, isConstructor; - for (property in object) { - if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { - callback(property); - } + } + // Manually invoke the callback for each non-enumerable property. + for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); + }; + } else if (size == 2) { + // Safari <= 2.0.4 enumerates shadowed properties twice. + forEach = function (object, callback) { + // Create a set of iterated properties. + var members = {}, isFunction = getClass.call(object) == functionClass, property; + for (property in object) { + // Store each property name to prevent double enumeration. The + // `prototype` property of functions is not enumerated due to cross- + // environment inconsistencies. + if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { + callback(property); } - // Manually invoke the callback for the `constructor` property due to - // cross-environment inconsistencies. - if (isConstructor || isProperty.call(object, (property = "constructor"))) { + } + }; + } else { + // No bugs detected; use the standard `for...in` algorithm. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, isConstructor; + for (property in object) { + if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { callback(property); } - }; - } - return forEach(object, callback); - }; - - // Public: Serializes a JavaScript `value` as a JSON string. The optional - // `filter` argument may specify either a function that alters how object and - // array members are serialized, or an array of strings and numbers that - // indicates which properties should be serialized. The optional `width` - // argument may be either a string or number that specifies the indentation - // level of the output. - if (!has("json-stringify")) { - // Internal: A map of control characters and their escaped equivalents. - var Escapes = { - 92: "\\\\", - 34: '\\"', - 8: "\\b", - 12: "\\f", - 10: "\\n", - 13: "\\r", - 9: "\\t" + } + // Manually invoke the callback for the `constructor` property due to + // cross-environment inconsistencies. + if (isConstructor || isProperty.call(object, (property = "constructor"))) { + callback(property); + } }; + } + return forEach(object, callback); + }; - // Internal: Converts `value` into a zero-padded string such that its - // length is at least equal to `width`. The `width` must be <= 6. - var leadingZeroes = "000000"; - var toPaddedString = function (width, value) { - // The `|| 0` expression is necessary to work around a bug in - // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. - return (leadingZeroes + (value || 0)).slice(-width); - }; + // Public: Serializes a JavaScript `value` as a JSON string. The optional + // `filter` argument may specify either a function that alters how object and + // array members are serialized, or an array of strings and numbers that + // indicates which properties should be serialized. The optional `width` + // argument may be either a string or number that specifies the indentation + // level of the output. + if (!has("json-stringify")) { + // Internal: A map of control characters and their escaped equivalents. + var Escapes = { + 92: "\\\\", + 34: '\\"', + 8: "\\b", + 12: "\\f", + 10: "\\n", + 13: "\\r", + 9: "\\t" + }; - // Internal: Double-quotes a string `value`, replacing all ASCII control - // characters (characters with code unit values between 0 and 31) with - // their escaped equivalents. This is an implementation of the - // `Quote(value)` operation defined in ES 5.1 section 15.12.3. - var unicodePrefix = "\\u00"; - var quote = function (value) { - var result = '"', index = 0, length = value.length, useCharIndex = !charIndexBuggy || length > 10; - var symbols = useCharIndex && (charIndexBuggy ? value.split("") : value); - for (; index < length; index++) { - var charCode = value.charCodeAt(index); - // If the character is a control character, append its Unicode or - // shorthand escape sequence; otherwise, append the character as-is. - switch (charCode) { - case 8: case 9: case 10: case 12: case 13: case 34: case 92: - result += Escapes[charCode]; - break; - default: - if (charCode < 32) { - result += unicodePrefix + toPaddedString(2, charCode.toString(16)); - break; - } - result += useCharIndex ? symbols[index] : value.charAt(index); - } + // Internal: Converts `value` into a zero-padded string such that its + // length is at least equal to `width`. The `width` must be <= 6. + var leadingZeroes = "000000"; + var toPaddedString = function (width, value) { + // The `|| 0` expression is necessary to work around a bug in + // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. + return (leadingZeroes + (value || 0)).slice(-width); + }; + + // Internal: Double-quotes a string `value`, replacing all ASCII control + // characters (characters with code unit values between 0 and 31) with + // their escaped equivalents. This is an implementation of the + // `Quote(value)` operation defined in ES 5.1 section 15.12.3. + var unicodePrefix = "\\u00"; + var quote = function (value) { + var result = '"', index = 0, length = value.length, isLarge = length > 10 && charIndexBuggy, symbols; + if (isLarge) { + symbols = value.split(""); + } + for (; index < length; index++) { + var charCode = value.charCodeAt(index); + // If the character is a control character, append its Unicode or + // shorthand escape sequence; otherwise, append the character as-is. + switch (charCode) { + case 8: case 9: case 10: case 12: case 13: case 34: case 92: + result += Escapes[charCode]; + break; + default: + if (charCode < 32) { + result += unicodePrefix + toPaddedString(2, charCode.toString(16)); + break; + } + result += isLarge ? symbols[index] : charIndexBuggy ? value.charAt(index) : value[index]; } - return result + '"'; - }; + } + return result + '"'; + }; - // Internal: Recursively serializes an object. Implements the - // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. - var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { - var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; - try { - // Necessary for host object support. - value = object[property]; - } catch (exception) {} - if (typeof value == "object" && value) { - className = getClass.call(value); - if (className == dateClass && !isProperty.call(value, "toJSON")) { - if (value > -1 / 0 && value < 1 / 0) { - // Dates are serialized according to the `Date#toJSON` method - // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 - // for the ISO 8601 date time string format. - if (getDay) { - // Manually compute the year, month, date, hours, minutes, - // seconds, and milliseconds if the `getUTC*` methods are - // buggy. Adapted from @Yaffle's `date-shim` project. - date = floor(value / 864e5); - for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); - for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); - date = 1 + date - getDay(year, month); - // The `time` value specifies the time within the day (see ES - // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used - // to compute `A modulo B`, as the `%` operator does not - // correspond to the `modulo` operation for negative numbers. - time = (value % 864e5 + 864e5) % 864e5; - // The hours, minutes, seconds, and milliseconds are obtained by - // decomposing the time within the day. See section 15.9.1.10. - hours = floor(time / 36e5) % 24; - minutes = floor(time / 6e4) % 60; - seconds = floor(time / 1e3) % 60; - milliseconds = time % 1e3; - } else { - year = value.getUTCFullYear(); - month = value.getUTCMonth(); - date = value.getUTCDate(); - hours = value.getUTCHours(); - minutes = value.getUTCMinutes(); - seconds = value.getUTCSeconds(); - milliseconds = value.getUTCMilliseconds(); - } - // Serialize extended years correctly. - value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + - "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + - // Months, dates, hours, minutes, and seconds should have two - // digits; milliseconds should have three. - "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + - // Milliseconds are optional in ES 5.0, but required in 5.1. - "." + toPaddedString(3, milliseconds) + "Z"; + // Internal: Recursively serializes an object. Implements the + // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. + var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { + var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; + try { + // Necessary for host object support. + value = object[property]; + } catch (exception) {} + if (typeof value == "object" && value) { + className = getClass.call(value); + if (className == dateClass && !isProperty.call(value, "toJSON")) { + if (value > -1 / 0 && value < 1 / 0) { + // Dates are serialized according to the `Date#toJSON` method + // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 + // for the ISO 8601 date time string format. + if (getDay) { + // Manually compute the year, month, date, hours, minutes, + // seconds, and milliseconds if the `getUTC*` methods are + // buggy. Adapted from @Yaffle's `date-shim` project. + date = floor(value / 864e5); + for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); + for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); + date = 1 + date - getDay(year, month); + // The `time` value specifies the time within the day (see ES + // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used + // to compute `A modulo B`, as the `%` operator does not + // correspond to the `modulo` operation for negative numbers. + time = (value % 864e5 + 864e5) % 864e5; + // The hours, minutes, seconds, and milliseconds are obtained by + // decomposing the time within the day. See section 15.9.1.10. + hours = floor(time / 36e5) % 24; + minutes = floor(time / 6e4) % 60; + seconds = floor(time / 1e3) % 60; + milliseconds = time % 1e3; } else { - value = null; + year = value.getUTCFullYear(); + month = value.getUTCMonth(); + date = value.getUTCDate(); + hours = value.getUTCHours(); + minutes = value.getUTCMinutes(); + seconds = value.getUTCSeconds(); + milliseconds = value.getUTCMilliseconds(); } - } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { - // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the - // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 - // ignores all `toJSON` methods on these objects unless they are - // defined directly on an instance. - value = value.toJSON(property); + // Serialize extended years correctly. + value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + + // Months, dates, hours, minutes, and seconds should have two + // digits; milliseconds should have three. + "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + + // Milliseconds are optional in ES 5.0, but required in 5.1. + "." + toPaddedString(3, milliseconds) + "Z"; + } else { + value = null; } + } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { + // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the + // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 + // ignores all `toJSON` methods on these objects unless they are + // defined directly on an instance. + value = value.toJSON(property); } - if (callback) { - // If a replacement function was provided, call it to obtain the value - // for serialization. - value = callback.call(object, property, value); - } - if (value === null) { - return "null"; - } - className = getClass.call(value); - if (className == booleanClass) { - // Booleans are represented literally. - return "" + value; - } else if (className == numberClass) { - // JSON numbers must be finite. `Infinity` and `NaN` are serialized as - // `"null"`. - return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; - } else if (className == stringClass) { - // Strings are double-quoted and escaped. - return quote("" + value); + } + if (callback) { + // If a replacement function was provided, call it to obtain the value + // for serialization. + value = callback.call(object, property, value); + } + if (value === null) { + return "null"; + } + className = getClass.call(value); + if (className == booleanClass) { + // Booleans are represented literally. + return "" + value; + } else if (className == numberClass) { + // JSON numbers must be finite. `Infinity` and `NaN` are serialized as + // `"null"`. + return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; + } else if (className == stringClass) { + // Strings are double-quoted and escaped. + return quote("" + value); + } + // Recursively serialize objects and arrays. + if (typeof value == "object") { + // Check for cyclic structures. This is a linear search; performance + // is inversely proportional to the number of unique nested objects. + for (length = stack.length; length--;) { + if (stack[length] === value) { + // Cyclic structures cannot be serialized by `JSON.stringify`. + throw TypeError(); + } } - // Recursively serialize objects and arrays. - if (typeof value == "object") { - // Check for cyclic structures. This is a linear search; performance - // is inversely proportional to the number of unique nested objects. - for (length = stack.length; length--;) { - if (stack[length] === value) { - // Cyclic structures cannot be serialized by `JSON.stringify`. - throw TypeError(); - } + // Add the object to the stack of traversed objects. + stack.push(value); + results = []; + // Save the current indentation level and indent one additional level. + prefix = indentation; + indentation += whitespace; + if (className == arrayClass) { + // Recursively serialize array elements. + for (index = 0, length = value.length; index < length; index++) { + element = serialize(index, value, callback, properties, whitespace, indentation, stack); + results.push(element === undef ? "null" : element); } - // Add the object to the stack of traversed objects. - stack.push(value); - results = []; - // Save the current indentation level and indent one additional level. - prefix = indentation; - indentation += whitespace; - if (className == arrayClass) { - // Recursively serialize array elements. - for (index = 0, length = value.length; index < length; index++) { - element = serialize(index, value, callback, properties, whitespace, indentation, stack); - results.push(element === undef ? "null" : element); + result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; + } else { + // Recursively serialize object members. Members are selected from + // either a user-specified list of property names, or the object + // itself. + forEach(properties || value, function (property) { + var element = serialize(property, value, callback, properties, whitespace, indentation, stack); + if (element !== undef) { + // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} + // is not the empty string, let `member` {quote(property) + ":"} + // be the concatenation of `member` and the `space` character." + // The "`space` character" refers to the literal space + // character, not the `space` {width} argument provided to + // `JSON.stringify`. + results.push(quote(property) + ":" + (whitespace ? " " : "") + element); } - result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; - } else { - // Recursively serialize object members. Members are selected from - // either a user-specified list of property names, or the object - // itself. - forEach(properties || value, function (property) { - var element = serialize(property, value, callback, properties, whitespace, indentation, stack); - if (element !== undef) { - // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} - // is not the empty string, let `member` {quote(property) + ":"} - // be the concatenation of `member` and the `space` character." - // The "`space` character" refers to the literal space - // character, not the `space` {width} argument provided to - // `JSON.stringify`. - results.push(quote(property) + ":" + (whitespace ? " " : "") + element); - } - }); - result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; - } - // Remove the object from the traversed object stack. - stack.pop(); - return result; + }); + result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; } - }; + // Remove the object from the traversed object stack. + stack.pop(); + return result; + } + }; - // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. - exports.stringify = function (source, filter, width) { - var whitespace, callback, properties, className; - if (typeof filter == "function" || typeof filter == "object" && filter) { - if ((className = getClass.call(filter)) == functionClass) { - callback = filter; - } else if (className == arrayClass) { - // Convert the property names array into a makeshift set. - properties = {}; - for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); - } + // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. + JSON3.stringify = function (source, filter, width) { + var whitespace, callback, properties, className; + if (typeof filter == "function" || typeof filter == "object" && filter) { + if ((className = getClass.call(filter)) == functionClass) { + callback = filter; + } else if (className == arrayClass) { + // Convert the property names array into a makeshift set. + properties = {}; + for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); } - if (width) { - if ((className = getClass.call(width)) == numberClass) { - // Convert the `width` to an integer and create a string containing - // `width` number of space characters. - if ((width -= width % 1) > 0) { - for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); - } - } else if (className == stringClass) { - whitespace = width.length <= 10 ? width : width.slice(0, 10); + } + if (width) { + if ((className = getClass.call(width)) == numberClass) { + // Convert the `width` to an integer and create a string containing + // `width` number of space characters. + if ((width -= width % 1) > 0) { + for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); } + } else if (className == stringClass) { + whitespace = width.length <= 10 ? width : width.slice(0, 10); } - // Opera <= 7.54u2 discards the values associated with empty string keys - // (`""`) only if they are used directly within an object member list - // (e.g., `!("" in { "": 1})`). - return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); - }; - } + } + // Opera <= 7.54u2 discards the values associated with empty string keys + // (`""`) only if they are used directly within an object member list + // (e.g., `!("" in { "": 1})`). + return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); + }; + } - // Public: Parses a JSON source string. - if (!has("json-parse")) { - var fromCharCode = String.fromCharCode; - - // Internal: A map of escaped control characters and their unescaped - // equivalents. - var Unescapes = { - 92: "\\", - 34: '"', - 47: "/", - 98: "\b", - 116: "\t", - 110: "\n", - 102: "\f", - 114: "\r" - }; + // Public: Parses a JSON source string. + if (!has("json-parse")) { + var fromCharCode = String.fromCharCode; + + // Internal: A map of escaped control characters and their unescaped + // equivalents. + var Unescapes = { + 92: "\\", + 34: '"', + 47: "/", + 98: "\b", + 116: "\t", + 110: "\n", + 102: "\f", + 114: "\r" + }; - // Internal: Stores the parser state. - var Index, Source; + // Internal: Stores the parser state. + var Index, Source; - // Internal: Resets the parser state and throws a `SyntaxError`. - var abort = function () { - Index = Source = null; - throw SyntaxError(); - }; + // Internal: Resets the parser state and throws a `SyntaxError`. + var abort = function() { + Index = Source = null; + throw SyntaxError(); + }; - // Internal: Returns the next token, or `"$"` if the parser has reached - // the end of the source string. A token may be a string, number, `null` - // literal, or Boolean literal. - var lex = function () { - var source = Source, length = source.length, value, begin, position, isSigned, charCode; - while (Index < length) { - charCode = source.charCodeAt(Index); - switch (charCode) { - case 9: case 10: case 13: case 32: - // Skip whitespace tokens, including tabs, carriage returns, line - // feeds, and space characters. - Index++; - break; - case 123: case 125: case 91: case 93: case 58: case 44: - // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at - // the current position. - value = charIndexBuggy ? source.charAt(Index) : source[Index]; - Index++; - return value; - case 34: - // `"` delimits a JSON string; advance to the next character and - // begin parsing the string. String tokens are prefixed with the - // sentinel `@` character to distinguish them from punctuators and - // end-of-string tokens. - for (value = "@", Index++; Index < length;) { - charCode = source.charCodeAt(Index); - if (charCode < 32) { - // Unescaped ASCII control characters (those with a code unit - // less than the space character) are not permitted. - abort(); - } else if (charCode == 92) { - // A reverse solidus (`\`) marks the beginning of an escaped - // control character (including `"`, `\`, and `/`) or Unicode - // escape sequence. - charCode = source.charCodeAt(++Index); - switch (charCode) { - case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: - // Revive escaped control characters. - value += Unescapes[charCode]; - Index++; - break; - case 117: - // `\u` marks the beginning of a Unicode escape sequence. - // Advance to the first character and validate the - // four-digit code point. - begin = ++Index; - for (position = Index + 4; Index < position; Index++) { - charCode = source.charCodeAt(Index); - // A valid sequence comprises four hexdigits (case- - // insensitive) that form a single hexadecimal value. - if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { - // Invalid Unicode escape sequence. - abort(); - } + // Internal: Returns the next token, or `"$"` if the parser has reached + // the end of the source string. A token may be a string, number, `null` + // literal, or Boolean literal. + var lex = function () { + var source = Source, length = source.length, value, begin, position, isSigned, charCode; + while (Index < length) { + charCode = source.charCodeAt(Index); + switch (charCode) { + case 9: case 10: case 13: case 32: + // Skip whitespace tokens, including tabs, carriage returns, line + // feeds, and space characters. + Index++; + break; + case 123: case 125: case 91: case 93: case 58: case 44: + // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at + // the current position. + value = charIndexBuggy ? source.charAt(Index) : source[Index]; + Index++; + return value; + case 34: + // `"` delimits a JSON string; advance to the next character and + // begin parsing the string. String tokens are prefixed with the + // sentinel `@` character to distinguish them from punctuators and + // end-of-string tokens. + for (value = "@", Index++; Index < length;) { + charCode = source.charCodeAt(Index); + if (charCode < 32) { + // Unescaped ASCII control characters (those with a code unit + // less than the space character) are not permitted. + abort(); + } else if (charCode == 92) { + // A reverse solidus (`\`) marks the beginning of an escaped + // control character (including `"`, `\`, and `/`) or Unicode + // escape sequence. + charCode = source.charCodeAt(++Index); + switch (charCode) { + case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: + // Revive escaped control characters. + value += Unescapes[charCode]; + Index++; + break; + case 117: + // `\u` marks the beginning of a Unicode escape sequence. + // Advance to the first character and validate the + // four-digit code point. + begin = ++Index; + for (position = Index + 4; Index < position; Index++) { + charCode = source.charCodeAt(Index); + // A valid sequence comprises four hexdigits (case- + // insensitive) that form a single hexadecimal value. + if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { + // Invalid Unicode escape sequence. + abort(); } - // Revive the escaped character. - value += fromCharCode("0x" + source.slice(begin, Index)); - break; - default: - // Invalid escape sequence. - abort(); - } - } else { - if (charCode == 34) { - // An unescaped double-quote character marks the end of the - // string. + } + // Revive the escaped character. + value += fromCharCode("0x" + source.slice(begin, Index)); break; - } - charCode = source.charCodeAt(Index); - begin = Index; - // Optimize for the common case where a string is valid. - while (charCode >= 32 && charCode != 92 && charCode != 34) { - charCode = source.charCodeAt(++Index); - } - // Append the string as-is. - value += source.slice(begin, Index); - } - } - if (source.charCodeAt(Index) == 34) { - // Advance to the next character and return the revived string. - Index++; - return value; - } - // Unterminated string. - abort(); - default: - // Parse numbers and literals. - begin = Index; - // Advance past the negative sign, if one is specified. - if (charCode == 45) { - isSigned = true; - charCode = source.charCodeAt(++Index); - } - // Parse an integer or floating-point value. - if (charCode >= 48 && charCode <= 57) { - // Leading zeroes are interpreted as octal literals. - if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { - // Illegal octal literal. - abort(); - } - isSigned = false; - // Parse the integer component. - for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); - // Floats cannot contain a leading decimal point; however, this - // case is already accounted for by the parser. - if (source.charCodeAt(Index) == 46) { - position = ++Index; - // Parse the decimal component. - for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); - if (position == Index) { - // Illegal trailing decimal. + default: + // Invalid escape sequence. abort(); - } - Index = position; } - // Parse exponents. The `e` denoting the exponent is - // case-insensitive. + } else { + if (charCode == 34) { + // An unescaped double-quote character marks the end of the + // string. + break; + } charCode = source.charCodeAt(Index); - if (charCode == 101 || charCode == 69) { + begin = Index; + // Optimize for the common case where a string is valid. + while (charCode >= 32 && charCode != 92 && charCode != 34) { charCode = source.charCodeAt(++Index); - // Skip past the sign following the exponent, if one is - // specified. - if (charCode == 43 || charCode == 45) { - Index++; - } - // Parse the exponential component. - for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); - if (position == Index) { - // Illegal empty exponent. - abort(); - } - Index = position; } - // Coerce the parsed value to a JavaScript number. - return +source.slice(begin, Index); + // Append the string as-is. + value += source.slice(begin, Index); } - // A negative sign may only precede numbers. - if (isSigned) { + } + if (source.charCodeAt(Index) == 34) { + // Advance to the next character and return the revived string. + Index++; + return value; + } + // Unterminated string. + abort(); + default: + // Parse numbers and literals. + begin = Index; + // Advance past the negative sign, if one is specified. + if (charCode == 45) { + isSigned = true; + charCode = source.charCodeAt(++Index); + } + // Parse an integer or floating-point value. + if (charCode >= 48 && charCode <= 57) { + // Leading zeroes are interpreted as octal literals. + if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { + // Illegal octal literal. abort(); } - // `true`, `false`, and `null` literals. - if (source.slice(Index, Index + 4) == "true") { - Index += 4; - return true; - } else if (source.slice(Index, Index + 5) == "false") { - Index += 5; - return false; - } else if (source.slice(Index, Index + 4) == "null") { - Index += 4; - return null; + isSigned = false; + // Parse the integer component. + for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); + // Floats cannot contain a leading decimal point; however, this + // case is already accounted for by the parser. + if (source.charCodeAt(Index) == 46) { + position = ++Index; + // Parse the decimal component. + for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal trailing decimal. + abort(); + } + Index = position; + } + // Parse exponents. The `e` denoting the exponent is + // case-insensitive. + charCode = source.charCodeAt(Index); + if (charCode == 101 || charCode == 69) { + charCode = source.charCodeAt(++Index); + // Skip past the sign following the exponent, if one is + // specified. + if (charCode == 43 || charCode == 45) { + Index++; + } + // Parse the exponential component. + for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal empty exponent. + abort(); + } + Index = position; } - // Unrecognized token. + // Coerce the parsed value to a JavaScript number. + return +source.slice(begin, Index); + } + // A negative sign may only precede numbers. + if (isSigned) { abort(); - } + } + // `true`, `false`, and `null` literals. + if (source.slice(Index, Index + 4) == "true") { + Index += 4; + return true; + } else if (source.slice(Index, Index + 5) == "false") { + Index += 5; + return false; + } else if (source.slice(Index, Index + 4) == "null") { + Index += 4; + return null; + } + // Unrecognized token. + abort(); } - // Return the sentinel `$` character if the parser has reached the end - // of the source string. - return "$"; - }; + } + // Return the sentinel `$` character if the parser has reached the end + // of the source string. + return "$"; + }; - // Internal: Parses a JSON `value` token. - var get = function (value) { - var results, hasMembers; - if (value == "$") { - // Unexpected end of input. - abort(); + // Internal: Parses a JSON `value` token. + var get = function (value) { + var results, hasMembers; + if (value == "$") { + // Unexpected end of input. + abort(); + } + if (typeof value == "string") { + if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { + // Remove the sentinel `@` character. + return value.slice(1); } - if (typeof value == "string") { - if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { - // Remove the sentinel `@` character. - return value.slice(1); - } - // Parse object and array literals. - if (value == "[") { - // Parses a JSON array, returning a new JavaScript array. - results = []; - for (;; hasMembers || (hasMembers = true)) { - value = lex(); - // A closing square bracket marks the end of the array literal. - if (value == "]") { - break; - } - // If the array literal contains elements, the current token - // should be a comma separating the previous element from the - // next. - if (hasMembers) { - if (value == ",") { - value = lex(); - if (value == "]") { - // Unexpected trailing `,` in array literal. - abort(); - } - } else { - // A `,` must separate each array element. + // Parse object and array literals. + if (value == "[") { + // Parses a JSON array, returning a new JavaScript array. + results = []; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing square bracket marks the end of the array literal. + if (value == "]") { + break; + } + // If the array literal contains elements, the current token + // should be a comma separating the previous element from the + // next. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "]") { + // Unexpected trailing `,` in array literal. abort(); } - } - // Elisions and leading commas are not permitted. - if (value == ",") { + } else { + // A `,` must separate each array element. abort(); } - results.push(get(value)); } - return results; - } else if (value == "{") { - // Parses a JSON object, returning a new JavaScript object. - results = {}; - for (;; hasMembers || (hasMembers = true)) { - value = lex(); - // A closing curly brace marks the end of the object literal. - if (value == "}") { - break; - } - // If the object literal contains members, the current token - // should be a comma separator. - if (hasMembers) { - if (value == ",") { - value = lex(); - if (value == "}") { - // Unexpected trailing `,` in object literal. - abort(); - } - } else { - // A `,` must separate each object member. + // Elisions and leading commas are not permitted. + if (value == ",") { + abort(); + } + results.push(get(value)); + } + return results; + } else if (value == "{") { + // Parses a JSON object, returning a new JavaScript object. + results = {}; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing curly brace marks the end of the object literal. + if (value == "}") { + break; + } + // If the object literal contains members, the current token + // should be a comma separator. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "}") { + // Unexpected trailing `,` in object literal. abort(); } - } - // Leading commas are not permitted, object property names must be - // double-quoted strings, and a `:` must separate each property - // name and value. - if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { + } else { + // A `,` must separate each object member. abort(); } - results[value.slice(1)] = get(lex()); } - return results; + // Leading commas are not permitted, object property names must be + // double-quoted strings, and a `:` must separate each property + // name and value. + if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { + abort(); + } + results[value.slice(1)] = get(lex()); } - // Unexpected token encountered. - abort(); + return results; } - return value; - }; + // Unexpected token encountered. + abort(); + } + return value; + }; - // Internal: Updates a traversed object member. - var update = function (source, property, callback) { - var element = walk(source, property, callback); - if (element === undef) { - delete source[property]; - } else { - source[property] = element; - } - }; + // Internal: Updates a traversed object member. + var update = function(source, property, callback) { + var element = walk(source, property, callback); + if (element === undef) { + delete source[property]; + } else { + source[property] = element; + } + }; - // Internal: Recursively traverses a parsed JSON object, invoking the - // `callback` function for each value. This is an implementation of the - // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. - var walk = function (source, property, callback) { - var value = source[property], length; - if (typeof value == "object" && value) { - // `forEach` can't be used to traverse an array in Opera <= 8.54 - // because its `Object#hasOwnProperty` implementation returns `false` - // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). - if (getClass.call(value) == arrayClass) { - for (length = value.length; length--;) { - update(value, length, callback); - } - } else { - forEach(value, function (property) { - update(value, property, callback); - }); + // Internal: Recursively traverses a parsed JSON object, invoking the + // `callback` function for each value. This is an implementation of the + // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. + var walk = function (source, property, callback) { + var value = source[property], length; + if (typeof value == "object" && value) { + // `forEach` can't be used to traverse an array in Opera <= 8.54 + // because its `Object#hasOwnProperty` implementation returns `false` + // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). + if (getClass.call(value) == arrayClass) { + for (length = value.length; length--;) { + update(value, length, callback); } + } else { + forEach(value, function (property) { + update(value, property, callback); + }); } - return callback.call(source, property, value); - }; + } + return callback.call(source, property, value); + }; - // Public: `JSON.parse`. See ES 5.1 section 15.12.2. - exports.parse = function (source, callback) { - var result, value; - Index = 0; - Source = "" + source; - result = get(lex()); - // If a JSON string contains multiple tokens, it is invalid. - if (lex() != "$") { - abort(); - } - // Reset the parser state. - Index = Source = null; - return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; - }; - } + // Public: `JSON.parse`. See ES 5.1 section 15.12.2. + JSON3.parse = function (source, callback) { + var result, value; + Index = 0; + Source = "" + source; + result = get(lex()); + // If a JSON string contains multiple tokens, it is invalid. + if (lex() != "$") { + abort(); + } + // Reset the parser state. + Index = Source = null; + return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; + }; } - - exports["runInContext"] = runInContext; - return exports; - } - - if (typeof exports == "object" && exports && !exports.nodeType && !isLoader) { - // Export for CommonJS environments. - runInContext(root, exports); - } else { - // Export for web browsers and JavaScript engines. - var nativeJSON = root.JSON; - var JSON3 = runInContext(root, (root["JSON3"] = { - // Public: Restores the original value of the global `JSON` object and - // returns a reference to the `JSON3` object. - "noConflict": function () { - root.JSON = nativeJSON; - return JSON3; - } - })); - - root.JSON = { - "parse": JSON3.parse, - "stringify": JSON3.stringify - }; } // Export for asynchronous module loaders. @@ -5008,7 +8436,1381 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? } }(this)); -},{}],35:[function(require,module,exports){ +},{}],46:[function(require,module,exports){ +var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};var msgpack = require('msgpack-js-browser'); + +if (!global.DataView) { + global.DataView = require('jdataview'); +} + +module.exports = msgpack; +},{"jdataview":47,"msgpack-js-browser":48}],47:[function(require,module,exports){ +var Buffer=require("__browserify_Buffer").Buffer;// +// jDataView by Vjeux - Jan 2010 +// Continued by RReverser - Feb 2013 +// +// A unique way to work with a binary file in the browser +// http://github.com/jDataView/jDataView +// http://jDataView.github.io/ + +(function (global) { + +'use strict'; + +var compatibility = { + // NodeJS Buffer in v0.5.5 and newer + NodeBuffer: 'Buffer' in global && 'readInt16LE' in Buffer.prototype, + DataView: 'DataView' in global && ( + 'getFloat64' in DataView.prototype || // Chrome + 'getFloat64' in new DataView(new ArrayBuffer(1)) // Node + ), + ArrayBuffer: 'ArrayBuffer' in global, + PixelData: 'CanvasPixelArray' in global && 'ImageData' in global && 'document' in global +}; + +// we don't want to bother with old Buffer implementation +if (compatibility.NodeBuffer) { + (function (buffer) { + try { + buffer.writeFloatLE(Infinity, 0); + } catch (e) { + compatibility.NodeBuffer = false; + } + })(new Buffer(4)); +} + +if (compatibility.PixelData) { + var createPixelData = function (byteLength, buffer) { + var data = createPixelData.context2d.createImageData((byteLength + 3) / 4, 1).data; + data.byteLength = byteLength; + if (buffer !== undefined) { + for (var i = 0; i < byteLength; i++) { + data[i] = buffer[i]; + } + } + return data; + }; + createPixelData.context2d = document.createElement('canvas').getContext('2d'); +} + +var dataTypes = { + 'Int8': 1, + 'Int16': 2, + 'Int32': 4, + 'Uint8': 1, + 'Uint16': 2, + 'Uint32': 4, + 'Float32': 4, + 'Float64': 8 +}; + +var nodeNaming = { + 'Int8': 'Int8', + 'Int16': 'Int16', + 'Int32': 'Int32', + 'Uint8': 'UInt8', + 'Uint16': 'UInt16', + 'Uint32': 'UInt32', + 'Float32': 'Float', + 'Float64': 'Double' +}; + +function arrayFrom(arrayLike, forceCopy) { + return (!forceCopy && (arrayLike instanceof Array)) ? arrayLike : Array.prototype.slice.call(arrayLike); +} + +function defined(value, defaultValue) { + return value !== undefined ? value : defaultValue; +} + +function jDataView(buffer, byteOffset, byteLength, littleEndian) { + /* jshint validthis:true */ + + if (buffer instanceof jDataView) { + var result = buffer.slice(byteOffset, byteOffset + byteLength); + result._littleEndian = defined(littleEndian, result._littleEndian); + return result; + } + + if (!(this instanceof jDataView)) { + return new jDataView(buffer, byteOffset, byteLength, littleEndian); + } + + this.buffer = buffer = jDataView.wrapBuffer(buffer); + + // Check parameters and existing functionnalities + this._isArrayBuffer = compatibility.ArrayBuffer && buffer instanceof ArrayBuffer; + this._isPixelData = compatibility.PixelData && buffer instanceof CanvasPixelArray; + this._isDataView = compatibility.DataView && this._isArrayBuffer; + this._isNodeBuffer = compatibility.NodeBuffer && buffer instanceof Buffer; + + // Handle Type Errors + if (!this._isNodeBuffer && !this._isArrayBuffer && !this._isPixelData && !(buffer instanceof Array)) { + throw new TypeError('jDataView buffer has an incompatible type'); + } + + // Default Values + this._littleEndian = !!littleEndian; + + var bufferLength = 'byteLength' in buffer ? buffer.byteLength : buffer.length; + this.byteOffset = byteOffset = defined(byteOffset, 0); + this.byteLength = byteLength = defined(byteLength, bufferLength - byteOffset); + + if (!this._isDataView) { + this._checkBounds(byteOffset, byteLength, bufferLength); + } else { + this._view = new DataView(buffer, byteOffset, byteLength); + } + + // Create uniform methods (action wrappers) for the following data types + + this._engineAction = + this._isDataView + ? this._dataViewAction + : this._isNodeBuffer + ? this._nodeBufferAction + : this._isArrayBuffer + ? this._arrayBufferAction + : this._arrayAction; +} + +function getCharCodes(string) { + if (compatibility.NodeBuffer) { + return new Buffer(string, 'binary'); + } + + var Type = compatibility.ArrayBuffer ? Uint8Array : Array, + codes = new Type(string.length); + + for (var i = 0, length = string.length; i < length; i++) { + codes[i] = string.charCodeAt(i) & 0xff; + } + return codes; +} + +// mostly internal function for wrapping any supported input (String or Array-like) to best suitable buffer format +jDataView.wrapBuffer = function (buffer) { + switch (typeof buffer) { + case 'number': + if (compatibility.NodeBuffer) { + buffer = new Buffer(buffer); + buffer.fill(0); + } else + if (compatibility.ArrayBuffer) { + buffer = new Uint8Array(buffer).buffer; + } else + if (compatibility.PixelData) { + buffer = createPixelData(buffer); + } else { + buffer = new Array(buffer); + for (var i = 0; i < buffer.length; i++) { + buffer[i] = 0; + } + } + return buffer; + + case 'string': + buffer = getCharCodes(buffer); + /* falls through */ + default: + if ('length' in buffer && !((compatibility.NodeBuffer && buffer instanceof Buffer) || (compatibility.ArrayBuffer && buffer instanceof ArrayBuffer) || (compatibility.PixelData && buffer instanceof CanvasPixelArray))) { + if (compatibility.NodeBuffer) { + buffer = new Buffer(buffer); + } else + if (compatibility.ArrayBuffer) { + if (!(buffer instanceof ArrayBuffer)) { + buffer = new Uint8Array(buffer).buffer; + // bug in Node.js <= 0.8: + if (!(buffer instanceof ArrayBuffer)) { + buffer = new Uint8Array(arrayFrom(buffer, true)).buffer; + } + } + } else + if (compatibility.PixelData) { + buffer = createPixelData(buffer.length, buffer); + } else { + buffer = arrayFrom(buffer); + } + } + return buffer; + } +}; + +function pow2(n) { + return (n >= 0 && n < 31) ? (1 << n) : (pow2[n] || (pow2[n] = Math.pow(2, n))); +} + +// left for backward compatibility +jDataView.createBuffer = function () { + return jDataView.wrapBuffer(arguments); +}; + +function Uint64(lo, hi) { + this.lo = lo; + this.hi = hi; +} + +jDataView.Uint64 = Uint64; + +Uint64.prototype = { + valueOf: function () { + return this.lo + pow2(32) * this.hi; + }, + + toString: function () { + return Number.prototype.toString.apply(this.valueOf(), arguments); + } +}; + +Uint64.fromNumber = function (number) { + var hi = Math.floor(number / pow2(32)), + lo = number - hi * pow2(32); + + return new Uint64(lo, hi); +}; + +function Int64(lo, hi) { + Uint64.apply(this, arguments); +} + +jDataView.Int64 = Int64; + +Int64.prototype = 'create' in Object ? Object.create(Uint64.prototype) : new Uint64(); + +Int64.prototype.valueOf = function () { + if (this.hi < pow2(31)) { + return Uint64.prototype.valueOf.apply(this, arguments); + } + return -((pow2(32) - this.lo) + pow2(32) * (pow2(32) - 1 - this.hi)); +}; + +Int64.fromNumber = function (number) { + var lo, hi; + if (number >= 0) { + var unsigned = Uint64.fromNumber(number); + lo = unsigned.lo; + hi = unsigned.hi; + } else { + hi = Math.floor(number / pow2(32)); + lo = number - hi * pow2(32); + hi += pow2(32); + } + return new Int64(lo, hi); +}; + +jDataView.prototype = { + _offset: 0, + _bitOffset: 0, + + compatibility: compatibility, + + _checkBounds: function (byteOffset, byteLength, maxLength) { + // Do additional checks to simulate DataView + if (typeof byteOffset !== 'number') { + throw new TypeError('Offset is not a number.'); + } + if (typeof byteLength !== 'number') { + throw new TypeError('Size is not a number.'); + } + if (byteLength < 0) { + throw new RangeError('Length is negative.'); + } + if (byteOffset < 0 || byteOffset + byteLength > defined(maxLength, this.byteLength)) { + throw new RangeError('Offsets are out of bounds.'); + } + }, + + _action: function (type, isReadAction, byteOffset, littleEndian, value) { + return this._engineAction( + type, + isReadAction, + defined(byteOffset, this._offset), + defined(littleEndian, this._littleEndian), + value + ); + }, + + _dataViewAction: function (type, isReadAction, byteOffset, littleEndian, value) { + // Move the internal offset forward + this._offset = byteOffset + dataTypes[type]; + return isReadAction ? this._view['get' + type](byteOffset, littleEndian) : this._view['set' + type](byteOffset, value, littleEndian); + }, + + _nodeBufferAction: function (type, isReadAction, byteOffset, littleEndian, value) { + // Move the internal offset forward + this._offset = byteOffset + dataTypes[type]; + var nodeName = nodeNaming[type] + ((type === 'Int8' || type === 'Uint8') ? '' : littleEndian ? 'LE' : 'BE'); + byteOffset += this.byteOffset; + return isReadAction ? this.buffer['read' + nodeName](byteOffset) : this.buffer['write' + nodeName](value, byteOffset); + }, + + _arrayBufferAction: function (type, isReadAction, byteOffset, littleEndian, value) { + var size = dataTypes[type], TypedArray = global[type + 'Array'], typedArray; + + littleEndian = defined(littleEndian, this._littleEndian); + + // ArrayBuffer: we use a typed array of size 1 from original buffer if alignment is good and from slice when it's not + if (size === 1 || ((this.byteOffset + byteOffset) % size === 0 && littleEndian)) { + typedArray = new TypedArray(this.buffer, this.byteOffset + byteOffset, 1); + this._offset = byteOffset + size; + return isReadAction ? typedArray[0] : (typedArray[0] = value); + } else { + var bytes = new Uint8Array(isReadAction ? this.getBytes(size, byteOffset, littleEndian, true) : size); + typedArray = new TypedArray(bytes.buffer, 0, 1); + + if (isReadAction) { + return typedArray[0]; + } else { + typedArray[0] = value; + this._setBytes(byteOffset, bytes, littleEndian); + } + } + }, + + _arrayAction: function (type, isReadAction, byteOffset, littleEndian, value) { + return isReadAction ? this['_get' + type](byteOffset, littleEndian) : this['_set' + type](byteOffset, value, littleEndian); + }, + + // Helpers + + _getBytes: function (length, byteOffset, littleEndian) { + littleEndian = defined(littleEndian, this._littleEndian); + byteOffset = defined(byteOffset, this._offset); + length = defined(length, this.byteLength - byteOffset); + + this._checkBounds(byteOffset, length); + + byteOffset += this.byteOffset; + + this._offset = byteOffset - this.byteOffset + length; + + var result = this._isArrayBuffer + ? new Uint8Array(this.buffer, byteOffset, length) + : (this.buffer.slice || Array.prototype.slice).call(this.buffer, byteOffset, byteOffset + length); + + return littleEndian || length <= 1 ? result : arrayFrom(result).reverse(); + }, + + // wrapper for external calls (do not return inner buffer directly to prevent it's modifying) + getBytes: function (length, byteOffset, littleEndian, toArray) { + var result = this._getBytes(length, byteOffset, defined(littleEndian, true)); + return toArray ? arrayFrom(result) : result; + }, + + _setBytes: function (byteOffset, bytes, littleEndian) { + var length = bytes.length; + + // needed for Opera + if (length === 0) { + return; + } + + littleEndian = defined(littleEndian, this._littleEndian); + byteOffset = defined(byteOffset, this._offset); + + this._checkBounds(byteOffset, length); + + if (!littleEndian && length > 1) { + bytes = arrayFrom(bytes, true).reverse(); + } + + byteOffset += this.byteOffset; + + if (this._isArrayBuffer) { + new Uint8Array(this.buffer, byteOffset, length).set(bytes); + } + else { + if (this._isNodeBuffer) { + new Buffer(bytes).copy(this.buffer, byteOffset); + } else { + for (var i = 0; i < length; i++) { + this.buffer[byteOffset + i] = bytes[i]; + } + } + } + + this._offset = byteOffset - this.byteOffset + length; + }, + + setBytes: function (byteOffset, bytes, littleEndian) { + this._setBytes(byteOffset, bytes, defined(littleEndian, true)); + }, + + getString: function (byteLength, byteOffset, encoding) { + if (this._isNodeBuffer) { + byteOffset = defined(byteOffset, this._offset); + byteLength = defined(byteLength, this.byteLength - byteOffset); + + this._checkBounds(byteOffset, byteLength); + + this._offset = byteOffset + byteLength; + return this.buffer.toString(encoding || 'binary', this.byteOffset + byteOffset, this.byteOffset + this._offset); + } + var bytes = this._getBytes(byteLength, byteOffset, true), string = ''; + byteLength = bytes.length; + for (var i = 0; i < byteLength; i++) { + string += String.fromCharCode(bytes[i]); + } + if (encoding === 'utf8') { + string = decodeURIComponent(escape(string)); + } + return string; + }, + + setString: function (byteOffset, subString, encoding) { + if (this._isNodeBuffer) { + byteOffset = defined(byteOffset, this._offset); + this._checkBounds(byteOffset, subString.length); + this._offset = byteOffset + this.buffer.write(subString, this.byteOffset + byteOffset, encoding || 'binary'); + return; + } + if (encoding === 'utf8') { + subString = unescape(encodeURIComponent(subString)); + } + this._setBytes(byteOffset, getCharCodes(subString), true); + }, + + getChar: function (byteOffset) { + return this.getString(1, byteOffset); + }, + + setChar: function (byteOffset, character) { + this.setString(byteOffset, character); + }, + + tell: function () { + return this._offset; + }, + + seek: function (byteOffset) { + this._checkBounds(byteOffset, 0); + /* jshint boss: true */ + return this._offset = byteOffset; + }, + + skip: function (byteLength) { + return this.seek(this._offset + byteLength); + }, + + slice: function (start, end, forceCopy) { + function normalizeOffset(offset, byteLength) { + return offset < 0 ? offset + byteLength : offset; + } + + start = normalizeOffset(start, this.byteLength); + end = normalizeOffset(defined(end, this.byteLength), this.byteLength); + + return forceCopy + ? new jDataView(this.getBytes(end - start, start, true, true), undefined, undefined, this._littleEndian) + : new jDataView(this.buffer, this.byteOffset + start, end - start, this._littleEndian); + }, + + alignBy: function (byteCount) { + this._bitOffset = 0; + if (defined(byteCount, 1) !== 1) { + return this.skip(byteCount - (this._offset % byteCount || byteCount)); + } else { + return this._offset; + } + }, + + // Compatibility functions + + _getFloat64: function (byteOffset, littleEndian) { + var b = this._getBytes(8, byteOffset, littleEndian), + + sign = 1 - (2 * (b[7] >> 7)), + exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1), + + // Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead + mantissa = ((b[6] & 0x0f) * pow2(48)) + (b[5] * pow2(40)) + (b[4] * pow2(32)) + + (b[3] * pow2(24)) + (b[2] * pow2(16)) + (b[1] * pow2(8)) + b[0]; + + if (exponent === 1024) { + if (mantissa !== 0) { + return NaN; + } else { + return sign * Infinity; + } + } + + if (exponent === -1023) { // Denormalized + return sign * mantissa * pow2(-1022 - 52); + } + + return sign * (1 + mantissa * pow2(-52)) * pow2(exponent); + }, + + _getFloat32: function (byteOffset, littleEndian) { + var b = this._getBytes(4, byteOffset, littleEndian), + + sign = 1 - (2 * (b[3] >> 7)), + exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127, + mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0]; + + if (exponent === 128) { + if (mantissa !== 0) { + return NaN; + } else { + return sign * Infinity; + } + } + + if (exponent === -127) { // Denormalized + return sign * mantissa * pow2(-126 - 23); + } + + return sign * (1 + mantissa * pow2(-23)) * pow2(exponent); + }, + + _get64: function (Type, byteOffset, littleEndian) { + littleEndian = defined(littleEndian, this._littleEndian); + byteOffset = defined(byteOffset, this._offset); + + var parts = littleEndian ? [0, 4] : [4, 0]; + + for (var i = 0; i < 2; i++) { + parts[i] = this.getUint32(byteOffset + parts[i], littleEndian); + } + + this._offset = byteOffset + 8; + + return new Type(parts[0], parts[1]); + }, + + getInt64: function (byteOffset, littleEndian) { + return this._get64(Int64, byteOffset, littleEndian); + }, + + getUint64: function (byteOffset, littleEndian) { + return this._get64(Uint64, byteOffset, littleEndian); + }, + + _getInt32: function (byteOffset, littleEndian) { + var b = this._getBytes(4, byteOffset, littleEndian); + return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; + }, + + _getUint32: function (byteOffset, littleEndian) { + return this._getInt32(byteOffset, littleEndian) >>> 0; + }, + + _getInt16: function (byteOffset, littleEndian) { + return (this._getUint16(byteOffset, littleEndian) << 16) >> 16; + }, + + _getUint16: function (byteOffset, littleEndian) { + var b = this._getBytes(2, byteOffset, littleEndian); + return (b[1] << 8) | b[0]; + }, + + _getInt8: function (byteOffset) { + return (this._getUint8(byteOffset) << 24) >> 24; + }, + + _getUint8: function (byteOffset) { + return this._getBytes(1, byteOffset)[0]; + }, + + _getBitRangeData: function (bitLength, byteOffset) { + var startBit = (defined(byteOffset, this._offset) << 3) + this._bitOffset, + endBit = startBit + bitLength, + start = startBit >>> 3, + end = (endBit + 7) >>> 3, + b = this._getBytes(end - start, start, true), + wideValue = 0; + + /* jshint boss: true */ + if (this._bitOffset = endBit & 7) { + this._bitOffset -= 8; + } + + for (var i = 0, length = b.length; i < length; i++) { + wideValue = (wideValue << 8) | b[i]; + } + + return { + start: start, + bytes: b, + wideValue: wideValue + }; + }, + + getSigned: function (bitLength, byteOffset) { + var shift = 32 - bitLength; + return (this.getUnsigned(bitLength, byteOffset) << shift) >> shift; + }, + + getUnsigned: function (bitLength, byteOffset) { + var value = this._getBitRangeData(bitLength, byteOffset).wideValue >>> -this._bitOffset; + return bitLength < 32 ? (value & ~(-1 << bitLength)) : value; + }, + + _setBinaryFloat: function (byteOffset, value, mantSize, expSize, littleEndian) { + var signBit = value < 0 ? 1 : 0, + exponent, + mantissa, + eMax = ~(-1 << (expSize - 1)), + eMin = 1 - eMax; + + if (value < 0) { + value = -value; + } + + if (value === 0) { + exponent = 0; + mantissa = 0; + } else if (isNaN(value)) { + exponent = 2 * eMax + 1; + mantissa = 1; + } else if (value === Infinity) { + exponent = 2 * eMax + 1; + mantissa = 0; + } else { + exponent = Math.floor(Math.log(value) / Math.LN2); + if (exponent >= eMin && exponent <= eMax) { + mantissa = Math.floor((value * pow2(-exponent) - 1) * pow2(mantSize)); + exponent += eMax; + } else { + mantissa = Math.floor(value / pow2(eMin - mantSize)); + exponent = 0; + } + } + + var b = []; + while (mantSize >= 8) { + b.push(mantissa % 256); + mantissa = Math.floor(mantissa / 256); + mantSize -= 8; + } + exponent = (exponent << mantSize) | mantissa; + expSize += mantSize; + while (expSize >= 8) { + b.push(exponent & 0xff); + exponent >>>= 8; + expSize -= 8; + } + b.push((signBit << expSize) | exponent); + + this._setBytes(byteOffset, b, littleEndian); + }, + + _setFloat32: function (byteOffset, value, littleEndian) { + this._setBinaryFloat(byteOffset, value, 23, 8, littleEndian); + }, + + _setFloat64: function (byteOffset, value, littleEndian) { + this._setBinaryFloat(byteOffset, value, 52, 11, littleEndian); + }, + + _set64: function (Type, byteOffset, value, littleEndian) { + if (!(value instanceof Type)) { + value = Type.fromNumber(value); + } + + littleEndian = defined(littleEndian, this._littleEndian); + byteOffset = defined(byteOffset, this._offset); + + var parts = littleEndian ? {lo: 0, hi: 4} : {lo: 4, hi: 0}; + + for (var partName in parts) { + this.setUint32(byteOffset + parts[partName], value[partName], littleEndian); + } + + this._offset = byteOffset + 8; + }, + + setInt64: function (byteOffset, value, littleEndian) { + this._set64(Int64, byteOffset, value, littleEndian); + }, + + setUint64: function (byteOffset, value, littleEndian) { + this._set64(Uint64, byteOffset, value, littleEndian); + }, + + _setUint32: function (byteOffset, value, littleEndian) { + this._setBytes(byteOffset, [ + value & 0xff, + (value >>> 8) & 0xff, + (value >>> 16) & 0xff, + value >>> 24 + ], littleEndian); + }, + + _setUint16: function (byteOffset, value, littleEndian) { + this._setBytes(byteOffset, [ + value & 0xff, + (value >>> 8) & 0xff + ], littleEndian); + }, + + _setUint8: function (byteOffset, value) { + this._setBytes(byteOffset, [value & 0xff]); + }, + + setUnsigned: function (byteOffset, value, bitLength) { + var data = this._getBitRangeData(bitLength, byteOffset), + wideValue = data.wideValue, + b = data.bytes; + + wideValue &= ~(~(-1 << bitLength) << -this._bitOffset); // clearing bit range before binary "or" + wideValue |= (bitLength < 32 ? (value & ~(-1 << bitLength)) : value) << -this._bitOffset; // setting bits + + for (var i = b.length - 1; i >= 0; i--) { + b[i] = wideValue & 0xff; + wideValue >>>= 8; + } + + this._setBytes(data.start, b, true); + } +}; + +var proto = jDataView.prototype; + +for (var type in dataTypes) { + (function (type) { + proto['get' + type] = function (byteOffset, littleEndian) { + return this._action(type, true, byteOffset, littleEndian); + }; + proto['set' + type] = function (byteOffset, value, littleEndian) { + this._action(type, false, byteOffset, littleEndian, value); + }; + })(type); +} + +proto._setInt32 = proto._setUint32; +proto._setInt16 = proto._setUint16; +proto._setInt8 = proto._setUint8; +proto.setSigned = proto.setUnsigned; + +for (var method in proto) { + if (method.slice(0, 3) === 'set') { + (function (type) { + proto['write' + type] = function () { + Array.prototype.unshift.call(arguments, undefined); + this['set' + type].apply(this, arguments); + }; + })(method.slice(3)); + } +} + +if (typeof module !== 'undefined' && typeof module.exports === 'object') { + module.exports = jDataView; +} else +if (typeof define === 'function' && define.amd) { + define([], function () { return jDataView }); +} else { + var oldGlobal = global.jDataView; + (global.jDataView = jDataView).noConflict = function () { + global.jDataView = oldGlobal; + return this; + }; +} + +})((function () { /* jshint strict: false */ return this })()); +},{"__browserify_Buffer":8}],48:[function(require,module,exports){ +( // Module boilerplate to support browser globals and browserify and AMD. + typeof define === "function" ? function (m) { define("msgpack-js", m); } : + typeof exports === "object" ? function (m) { module.exports = m(); } : + function(m){ this.msgpack = m(); } +)(function () { +"use strict"; + +var exports = {}; + +exports.inspect = inspect; +function inspect(buffer) { + if (buffer === undefined) return "undefined"; + var view; + var type; + if (buffer instanceof ArrayBuffer) { + type = "ArrayBuffer"; + view = new DataView(buffer); + } + else if (buffer instanceof DataView) { + type = "DataView"; + view = buffer; + } + if (!view) return JSON.stringify(buffer); + var bytes = []; + for (var i = 0; i < buffer.byteLength; i++) { + if (i > 20) { + bytes.push("..."); + break; + } + var byte = view.getUint8(i).toString(16); + if (byte.length === 1) byte = "0" + byte; + bytes.push(byte); + } + return "<" + type + " " + bytes.join(" ") + ">"; +} + +// Encode string as utf8 into dataview at offset +exports.utf8Write = utf8Write; +function utf8Write(view, offset, string) { + var byteLength = view.byteLength; + for(var i = 0, l = string.length; i < l; i++) { + var codePoint = string.charCodeAt(i); + + // One byte of UTF-8 + if (codePoint < 0x80) { + view.setUint8(offset++, codePoint >>> 0 & 0x7f | 0x00); + continue; + } + + // Two bytes of UTF-8 + if (codePoint < 0x800) { + view.setUint8(offset++, codePoint >>> 6 & 0x1f | 0xc0); + view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); + continue; + } + + // Three bytes of UTF-8. + if (codePoint < 0x10000) { + view.setUint8(offset++, codePoint >>> 12 & 0x0f | 0xe0); + view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80); + view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); + continue; + } + + // Four bytes of UTF-8 + if (codePoint < 0x110000) { + view.setUint8(offset++, codePoint >>> 18 & 0x07 | 0xf0); + view.setUint8(offset++, codePoint >>> 12 & 0x3f | 0x80); + view.setUint8(offset++, codePoint >>> 6 & 0x3f | 0x80); + view.setUint8(offset++, codePoint >>> 0 & 0x3f | 0x80); + continue; + } + throw new Error("bad codepoint " + codePoint); + } +} + +exports.utf8Read = utf8Read; +function utf8Read(view, offset, length) { + var string = ""; + for (var i = offset, end = offset + length; i < end; i++) { + var byte = view.getUint8(i); + // One byte character + if ((byte & 0x80) === 0x00) { + string += String.fromCharCode(byte); + continue; + } + // Two byte character + if ((byte & 0xe0) === 0xc0) { + string += String.fromCharCode( + ((byte & 0x0f) << 6) | + (view.getUint8(++i) & 0x3f) + ); + continue; + } + // Three byte character + if ((byte & 0xf0) === 0xe0) { + string += String.fromCharCode( + ((byte & 0x0f) << 12) | + ((view.getUint8(++i) & 0x3f) << 6) | + ((view.getUint8(++i) & 0x3f) << 0) + ); + continue; + } + // Four byte character + if ((byte & 0xf8) === 0xf0) { + string += String.fromCharCode( + ((byte & 0x07) << 18) | + ((view.getUint8(++i) & 0x3f) << 12) | + ((view.getUint8(++i) & 0x3f) << 6) | + ((view.getUint8(++i) & 0x3f) << 0) + ); + continue; + } + throw new Error("Invalid byte " + byte.toString(16)); + } + return string; +} + +exports.utf8ByteCount = utf8ByteCount; +function utf8ByteCount(string) { + var count = 0; + for(var i = 0, l = string.length; i < l; i++) { + var codePoint = string.charCodeAt(i); + if (codePoint < 0x80) { + count += 1; + continue; + } + if (codePoint < 0x800) { + count += 2; + continue; + } + if (codePoint < 0x10000) { + count += 3; + continue; + } + if (codePoint < 0x110000) { + count += 4; + continue; + } + throw new Error("bad codepoint " + codePoint); + } + return count; +} + +exports.encode = function (value) { + var buffer = new ArrayBuffer(sizeof(value)); + var view = new DataView(buffer); + encode(value, view, 0); + return buffer; +} + +exports.decode = decode; + +// http://wiki.msgpack.org/display/MSGPACK/Format+specification +// I've extended the protocol to have two new types that were previously reserved. +// buffer 16 11011000 0xd8 +// buffer 32 11011001 0xd9 +// These work just like raw16 and raw32 except they are node buffers instead of strings. +// +// Also I've added a type for `undefined` +// undefined 11000100 0xc4 + +function Decoder(view, offset) { + this.offset = offset || 0; + this.view = view; +} +Decoder.prototype.map = function (length) { + var value = {}; + for (var i = 0; i < length; i++) { + var key = this.parse(); + value[key] = this.parse(); + } + return value; +}; +Decoder.prototype.buf = function (length) { + var value = new ArrayBuffer(length); + (new Uint8Array(value)).set(new Uint8Array(this.view.buffer, this.offset, length), 0); + this.offset += length; + return value; +}; +Decoder.prototype.raw = function (length) { + var value = utf8Read(this.view, this.offset, length); + this.offset += length; + return value; +}; +Decoder.prototype.array = function (length) { + var value = new Array(length); + for (var i = 0; i < length; i++) { + value[i] = this.parse(); + } + return value; +}; +Decoder.prototype.parse = function () { + var type = this.view.getUint8(this.offset); + var value, length; + // FixRaw + if ((type & 0xe0) === 0xa0) { + length = type & 0x1f; + this.offset++; + return this.raw(length); + } + // FixMap + if ((type & 0xf0) === 0x80) { + length = type & 0x0f; + this.offset++; + return this.map(length); + } + // FixArray + if ((type & 0xf0) === 0x90) { + length = type & 0x0f; + this.offset++; + return this.array(length); + } + // Positive FixNum + if ((type & 0x80) === 0x00) { + this.offset++; + return type; + } + // Negative Fixnum + if ((type & 0xe0) === 0xe0) { + value = this.view.getInt8(this.offset); + this.offset++; + return value; + } + switch (type) { + // raw 16 + case 0xda: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.raw(length); + // raw 32 + case 0xdb: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.raw(length); + // nil + case 0xc0: + this.offset++; + return null; + // false + case 0xc2: + this.offset++; + return false; + // true + case 0xc3: + this.offset++; + return true; + // undefined + case 0xc4: + this.offset++; + return undefined; + // uint8 + case 0xcc: + value = this.view.getUint8(this.offset + 1); + this.offset += 2; + return value; + // uint 16 + case 0xcd: + value = this.view.getUint16(this.offset + 1); + this.offset += 3; + return value; + // uint 32 + case 0xce: + value = this.view.getUint32(this.offset + 1); + this.offset += 5; + return value; + // int 8 + case 0xd0: + value = this.view.getInt8(this.offset + 1); + this.offset += 2; + return value; + // int 16 + case 0xd1: + value = this.view.getInt16(this.offset + 1); + this.offset += 3; + return value; + // int 32 + case 0xd2: + value = this.view.getInt32(this.offset + 1); + this.offset += 5; + return value; + // map 16 + case 0xde: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.map(length); + // map 32 + case 0xdf: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.map(length); + // array 16 + case 0xdc: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.array(length); + // array 32 + case 0xdd: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.array(length); + // buffer 16 + case 0xd8: + length = this.view.getUint16(this.offset + 1); + this.offset += 3; + return this.buf(length); + // buffer 32 + case 0xd9: + length = this.view.getUint32(this.offset + 1); + this.offset += 5; + return this.buf(length); + // float + case 0xca: + value = this.view.getFloat32(this.offset + 1); + this.offset += 5; + return value; + // double + case 0xcb: + value = this.view.getFloat64(this.offset + 1); + this.offset += 9; + return value; + } + throw new Error("Unknown type 0x" + type.toString(16)); +}; +function decode(buffer) { + var view = new DataView(buffer); + var decoder = new Decoder(view); + var value = decoder.parse(); + if (decoder.offset !== buffer.byteLength) throw new Error((buffer.byteLength - decoder.offset) + " trailing bytes"); + return value; +} + +function encode(value, view, offset) { + var type = typeof value; + + // Strings Bytes + if (type === "string") { + var length = utf8ByteCount(value); + // fix raw + if (length < 0x20) { + view.setUint8(offset, length | 0xa0); + utf8Write(view, offset + 1, value); + return 1 + length; + } + // raw 16 + if (length < 0x10000) { + view.setUint8(offset, 0xda); + view.setUint16(offset + 1, length); + utf8Write(view, offset + 3, value); + return 3 + length; + } + // raw 32 + if (length < 0x100000000) { + view.setUint8(offset, 0xdb); + view.setUint32(offset + 1, length); + utf8Write(view, offset + 5, value); + return 5 + length; + } + } + + if (value instanceof ArrayBuffer) { + var length = value.byteLength; + // buffer 16 + if (length < 0x10000) { + view.setUint8(offset, 0xd8); + view.setUint16(offset + 1, length); + (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 3); + return 3 + length; + } + // buffer 32 + if (length < 0x100000000) { + view.setUint8(offset, 0xd9); + view.setUint32(offset + 1, length); + (new Uint8Array(view.buffer)).set(new Uint8Array(value), offset + 5); + return 5 + length; + } + } + + if (type === "number") { + // Floating Point + if ((value << 0) !== value) { + view.setUint8(offset, 0xcb); + view.setFloat64(offset + 1, value); + return 9; + } + + // Integers + if (value >=0) { + // positive fixnum + if (value < 0x80) { + view.setUint8(offset, value); + return 1; + } + // uint 8 + if (value < 0x100) { + view.setUint8(offset, 0xcc); + view.setUint8(offset + 1, value); + return 2; + } + // uint 16 + if (value < 0x10000) { + view.setUint8(offset, 0xcd); + view.setUint16(offset + 1, value); + return 3; + } + // uint 32 + if (value < 0x100000000) { + view.setUint8(offset, 0xce); + view.setUint32(offset + 1, value); + return 5; + } + throw new Error("Number too big 0x" + value.toString(16)); + } + // negative fixnum + if (value >= -0x20) { + view.setInt8(offset, value); + return 1; + } + // int 8 + if (value >= -0x80) { + view.setUint8(offset, 0xd0); + view.setInt8(offset + 1, value); + return 2; + } + // int 16 + if (value >= -0x8000) { + view.setUint8(offset, 0xd1); + view.setInt16(offset + 1, value); + return 3; + } + // int 32 + if (value >= -0x80000000) { + view.setUint8(offset, 0xd2); + view.setInt32(offset + 1, value); + return 5; + } + throw new Error("Number too small -0x" + (-value).toString(16).substr(1)); + } + + // undefined + if (type === "undefined") { + view.setUint8(offset, 0xc4); + return 1; + } + + // null + if (value === null) { + view.setUint8(offset, 0xc0); + return 1; + } + + // Boolean + if (type === "boolean") { + view.setUint8(offset, value ? 0xc3 : 0xc2); + return 1; + } + + // Container Types + if (type === "object") { + var length, size = 0; + var isArray = Array.isArray(value); + + if (isArray) { + length = value.length; + } + else { + var keys = Object.keys(value); + length = keys.length; + } + + var size; + if (length < 0x10) { + view.setUint8(offset, length | (isArray ? 0x90 : 0x80)); + size = 1; + } + else if (length < 0x10000) { + view.setUint8(offset, isArray ? 0xdc : 0xde); + view.setUint16(offset + 1, length); + size = 3; + } + else if (length < 0x100000000) { + view.setUint8(offset, isArray ? 0xdd : 0xdf); + view.setUint32(offset + 1, length); + size = 5; + } + + if (isArray) { + for (var i = 0; i < length; i++) { + size += encode(value[i], view, offset + size); + } + } + else { + for (var i = 0; i < length; i++) { + var key = keys[i]; + size += encode(key, view, offset + size); + size += encode(value[key], view, offset + size); + } + } + + return size; + } + throw new Error("Unknown type " + type); +} + +function sizeof(value) { + var type = typeof value; + + // Raw Bytes + if (type === "string") { + var length = utf8ByteCount(value); + if (length < 0x20) { + return 1 + length; + } + if (length < 0x10000) { + return 3 + length; + } + if (length < 0x100000000) { + return 5 + length; + } + } + + if (value instanceof ArrayBuffer) { + var length = value.byteLength; + if (length < 0x10000) { + return 3 + length; + } + if (length < 0x100000000) { + return 5 + length; + } + } + + if (type === "number") { + // Floating Point + // double + if (value << 0 !== value) return 9; + + // Integers + if (value >=0) { + // positive fixnum + if (value < 0x80) return 1; + // uint 8 + if (value < 0x100) return 2; + // uint 16 + if (value < 0x10000) return 3; + // uint 32 + if (value < 0x100000000) return 5; + // uint 64 + if (value < 0x10000000000000000) return 9; + throw new Error("Number too big 0x" + value.toString(16)); + } + // negative fixnum + if (value >= -0x20) return 1; + // int 8 + if (value >= -0x80) return 2; + // int 16 + if (value >= -0x8000) return 3; + // int 32 + if (value >= -0x80000000) return 5; + // int 64 + if (value >= -0x8000000000000000) return 9; + throw new Error("Number too small -0x" + value.toString(16).substr(1)); + } + + // Boolean, null, undefined + if (type === "boolean" || type === "undefined" || value === null) return 1; + + // Container Types + if (type === "object") { + var length, size = 0; + if (Array.isArray(value)) { + length = value.length; + for (var i = 0; i < length; i++) { + size += sizeof(value[i]); + } + } + else { + var keys = Object.keys(value); + length = keys.length; + for (var i = 0; i < length; i++) { + var key = keys[i]; + size += sizeof(key) + sizeof(value[key]); + } + } + if (length < 0x10) { + return 1 + size; + } + if (length < 0x10000) { + return 3 + size; + } + if (length < 0x100000000) { + return 5 + size; + } + throw new Error("Array or object too long 0x" + length.toString(16)); + } + throw new Error("Unknown type " + type); +} + +return exports; + +}); + +},{}],49:[function(require,module,exports){ module.exports = toArray function toArray(list, index) { diff --git a/test/connection.js b/test/connection.js index 8e88ae522..a6e9bf2a1 100644 --- a/test/connection.js +++ b/test/connection.js @@ -1,5 +1,8 @@ var expect = require('expect.js'); var io = require('../'); +var hasCORS = require('has-cors'); +var b64 = require('base64-js'); +var textBlobBuilder = require('text-blob-builder'); describe('connection', function() { this.timeout(10000); @@ -27,4 +30,96 @@ describe('connection', function() { done(); }); }); + +if (!global.Blob && !global.ArrayBuffer) { + it('should get base64 data as a last resort', function(done) { + socket.on('takebin', function(a) { + expect(a.base64).to.be(true); + var bytes = b64.toByteArray(a.data); + var dataString = String.fromCharCode.apply(String, bytes); + expect(dataString).to.eql('asdfasdf'); + done(); + }); + socket.emit('getbin'); + }); +} + +if (global.ArrayBuffer) { + var base64 = require('base64-arraybuffer'); + + it('should get binary data (as an ArrayBuffer)', function(done){ + socket.emit('doge'); + socket.on('doge', function(buffer){ + expect(buffer instanceof ArrayBuffer).to.be(true); + done(); + }); + }); + + it('should send binary data (as an ArrayBuffer)', function(done){ + socket.on('buffack', function(){ + done(); + }); + var buf = base64.decode("asdfasdf"); + socket.emit('buffa', buf); + }); + + it('should send binary data (as an ArrayBuffer) mixed with json', function(done) { + socket.on('jsonbuff-ack', function() { + done(); + }); + var buf = base64.decode("howdy"); + socket.emit('jsonbuff', {hello: 'lol', message: buf, goodbye: 'gotcha'}); + }); + + it('should send events with ArrayBuffers in the correct order', function(done) { + socket.on('abuff2-ack', function() { + done(); + }); + var buf = base64.decode("abuff1"); + socket.emit('abuff1', buf); + socket.emit('abuff2', 'please arrive second'); + }); +} + +if (global.Blob && null != textBlobBuilder('xxx')) { + it('should send binary data (as a Blob)', function(done){ + socket.on('back', function(){ + done(); + }); + var blob = textBlobBuilder('hello world'); + socket.emit('blob', blob); + }); + + it('should send binary data (as a Blob) mixed with json', function(done) { + socket.on('jsonblob-ack', function() { + done(); + }); + var blob = textBlobBuilder('EEEEEEEEE'); + socket.emit('jsonblob', {hello: 'lol', message: blob, goodbye: 'gotcha'}); + }); + + it('should send events with Blobs in the correct order', function(done) { + socket.on('blob3-ack', function() { + done(); + }); + var blob = textBlobBuilder('BLOBBLOB'); + socket.emit('blob1', blob); + socket.emit('blob2', 'second'); + socket.emit('blob3', blob); + }); + + it('should clear its packet buffer in case of disconnect', function(done) { + var blob = textBlobBuilder('BLOBBLOB'); + for (var i=0; i < 10; i++) { // fill the buffer + socket.emit('asdf', blob); + } + expect(socket.io.packetBuffer.length).to.not.be(0); + expect(socket.io.encoding).to.be(true); + socket.io.disconnect(); + expect(socket.io.packetBuffer.length).to.be(0); + expect(socket.io.encoding).to.be(false); + done(); + }); +} + }); diff --git a/test/support/server.js b/test/support/server.js index 2a188d51b..df77ffe07 100644 --- a/test/support/server.js +++ b/test/support/server.js @@ -3,6 +3,7 @@ var io = require('socket.io'); var server = io(process.env.ZUUL_PORT); +var expect = require('expect.js'); server.on('connection', function(socket){ // simple test @@ -23,4 +24,72 @@ server.on('connection', function(socket){ socket.on('false', function(){ socket.emit('false', false); }); + + // binary test + socket.on('doge', function(){ + buf = new Buffer('asdfasdf', 'utf8'); + socket.emit('doge', buf); + }); + + // expect receiving binary to be buffer + socket.on('buffa', function(a){ + if (Buffer.isBuffer(a)) socket.emit('buffack'); + }); + + // expect receiving binary with mixed JSON + socket.on('jsonbuff', function(a) { + expect(a.hello).to.eql('lol'); + expect(Buffer.isBuffer(a.message)).to.be(true); + expect(a.goodbye).to.eql('gotcha'); + socket.emit('jsonbuff-ack'); + }); + + // expect receiving buffers in order + var receivedAbuff1 = false; + socket.on('abuff1', function(a) { + expect(Buffer.isBuffer(a)).to.be(true); + receivedAbuff1 = true; + }); + socket.on('abuff2', function(a) { + expect(receivedAbuff1).to.be(true); + socket.emit('abuff2-ack'); + }); + + // expect sent blob to be buffer + socket.on('blob', function(a){ + if (Buffer.isBuffer(a)) socket.emit('back'); + }); + + // expect sent blob mixed with json to be buffer + socket.on('jsonblob', function(a) { + expect(a.hello).to.eql('lol'); + expect(Buffer.isBuffer(a.message)).to.be(true); + expect(a.goodbye).to.eql('gotcha'); + socket.emit('jsonblob-ack'); + }); + + // expect blobs sent in order to arrive in correct order + var receivedblob1 = false; + var receivedblob2 = false; + socket.on('blob1', function(a) { + expect(Buffer.isBuffer(a)).to.be(true); + receivedblob1 = true; + }); + socket.on('blob2', function(a) { + expect(receivedblob1).to.be(true); + expect(a).to.eql('second'); + receivedblob2 = true; + }); + socket.on('blob3', function(a) { + expect(Buffer.isBuffer(a)).to.be(true); + expect(receivedblob1).to.be(true); + expect(receivedblob2).to.be(true); + socket.emit('blob3-ack'); + }); + + // emit buffer to base64 receiving browsers + socket.on('getbin', function() { + buf = new Buffer('asdfasdf', 'utf8'); + socket.emit('takebin', buf); + }); });