-
-
Save ndob/4742514cd23eec7a81a3 to your computer and use it in GitHub Desktop.
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.io=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);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<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | |
module.exports = _dereq_('./lib/'); | |
},{"./lib/":2}],2:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var url = _dereq_('./url'); | |
var parser = _dereq_('socket.io-parser'); | |
var Manager = _dereq_('./manager'); | |
var debug = _dereq_('debug')('socket.io-client'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = exports = lookup; | |
/** | |
* Managers cache. | |
*/ | |
var cache = exports.managers = {}; | |
/** | |
* Looks up an existing `Manager` for multiplexing. | |
* If the user summons: | |
* | |
* `io('http://localhost/a');` | |
* `io('http://localhost/b');` | |
* | |
* We reuse the existing instance based on same scheme/port/host, | |
* and we initialize sockets for each namespace. | |
* | |
* @api public | |
*/ | |
function lookup(uri, opts) { | |
if (typeof uri == 'object') { | |
opts = uri; | |
uri = undefined; | |
} | |
opts = opts || {}; | |
var parsed = url(uri); | |
var source = parsed.source; | |
var id = parsed.id; | |
var io; | |
if (opts.forceNew || opts['force new connection'] || false === opts.multiplex) { | |
debug('ignoring socket cache for %s', source); | |
io = Manager(source, opts); | |
} else { | |
if (!cache[id]) { | |
debug('new io instance for %s', source); | |
cache[id] = Manager(source, opts); | |
} | |
io = cache[id]; | |
} | |
return io.socket(parsed.path); | |
} | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
exports.protocol = parser.protocol; | |
/** | |
* `connect`. | |
* | |
* @param {String} uri | |
* @api public | |
*/ | |
exports.connect = lookup; | |
/** | |
* Expose constructors for standalone build. | |
* | |
* @api public | |
*/ | |
exports.Manager = _dereq_('./manager'); | |
exports.Socket = _dereq_('./socket'); | |
},{"./manager":3,"./socket":5,"./url":6,"debug":9,"socket.io-parser":40}],3:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var url = _dereq_('./url'); | |
var eio = _dereq_('engine.io-client'); | |
var Socket = _dereq_('./socket'); | |
var Emitter = _dereq_('component-emitter'); | |
var parser = _dereq_('socket.io-parser'); | |
var on = _dereq_('./on'); | |
var bind = _dereq_('component-bind'); | |
var object = _dereq_('object-component'); | |
var debug = _dereq_('debug')('socket.io-client:manager'); | |
var indexOf = _dereq_('indexof'); | |
/** | |
* Module exports | |
*/ | |
module.exports = Manager; | |
/** | |
* `Manager` constructor. | |
* | |
* @param {String} engine instance or engine uri/opts | |
* @param {Object} options | |
* @api public | |
*/ | |
function Manager(uri, opts){ | |
if (!(this instanceof Manager)) return new Manager(uri, opts); | |
if (uri && ('object' == typeof uri)) { | |
opts = uri; | |
uri = undefined; | |
} | |
opts = opts || {}; | |
opts.path = opts.path || '/socket.io'; | |
this.nsps = {}; | |
this.subs = []; | |
this.opts = opts; | |
this.reconnection(opts.reconnection !== false); | |
this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); | |
this.reconnectionDelay(opts.reconnectionDelay || 1000); | |
this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); | |
this.timeout(null == opts.timeout ? 20000 : opts.timeout); | |
this.readyState = 'closed'; | |
this.uri = uri; | |
this.connected = []; | |
this.attempts = 0; | |
this.encoding = false; | |
this.packetBuffer = []; | |
this.encoder = new parser.Encoder(); | |
this.decoder = new parser.Decoder(); | |
this.autoConnect = opts.autoConnect !== false; | |
if (this.autoConnect) this.open(); | |
} | |
/** | |
* Propagate given event to sockets and emit on `this` | |
* | |
* @api private | |
*/ | |
Manager.prototype.emitAll = function() { | |
this.emit.apply(this, arguments); | |
for (var nsp in this.nsps) { | |
this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); | |
} | |
}; | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Manager.prototype); | |
/** | |
* Sets the `reconnection` config. | |
* | |
* @param {Boolean} true/false if it should automatically reconnect | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnection = function(v){ | |
if (!arguments.length) return this._reconnection; | |
this._reconnection = !!v; | |
return this; | |
}; | |
/** | |
* Sets the reconnection attempts config. | |
* | |
* @param {Number} max reconnection attempts before giving up | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionAttempts = function(v){ | |
if (!arguments.length) return this._reconnectionAttempts; | |
this._reconnectionAttempts = v; | |
return this; | |
}; | |
/** | |
* Sets the delay between reconnections. | |
* | |
* @param {Number} delay | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionDelay = function(v){ | |
if (!arguments.length) return this._reconnectionDelay; | |
this._reconnectionDelay = v; | |
return this; | |
}; | |
/** | |
* Sets the maximum delay between reconnections. | |
* | |
* @param {Number} delay | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.reconnectionDelayMax = function(v){ | |
if (!arguments.length) return this._reconnectionDelayMax; | |
this._reconnectionDelayMax = v; | |
return this; | |
}; | |
/** | |
* Sets the connection timeout. `false` to disable | |
* | |
* @return {Manager} self or value | |
* @api public | |
*/ | |
Manager.prototype.timeout = function(v){ | |
if (!arguments.length) return this._timeout; | |
this._timeout = v; | |
return this; | |
}; | |
/** | |
* Starts trying to reconnect if reconnection is enabled and we have not | |
* started reconnecting yet | |
* | |
* @api private | |
*/ | |
Manager.prototype.maybeReconnectOnOpen = function() { | |
// Only try to reconnect if it's the first time we're connecting | |
if (!this.openReconnect && !this.reconnecting && this._reconnection && this.attempts === 0) { | |
// keeps reconnection from firing twice for the same reconnection loop | |
this.openReconnect = true; | |
this.reconnect(); | |
} | |
}; | |
/** | |
* Sets the current transport `socket`. | |
* | |
* @param {Function} optional, callback | |
* @return {Manager} self | |
* @api public | |
*/ | |
Manager.prototype.open = | |
Manager.prototype.connect = function(fn){ | |
debug('readyState %s', this.readyState); | |
if (~this.readyState.indexOf('open')) return this; | |
debug('opening %s', this.uri); | |
this.engine = eio(this.uri, this.opts); | |
var socket = this.engine; | |
var self = this; | |
this.readyState = 'opening'; | |
this.skipReconnect = false; | |
// emit `open` | |
var openSub = on(socket, 'open', function() { | |
self.onopen(); | |
fn && fn(); | |
}); | |
// emit `connect_error` | |
var errorSub = on(socket, 'error', function(data){ | |
debug('connect_error'); | |
self.cleanup(); | |
self.readyState = 'closed'; | |
self.emitAll('connect_error', data); | |
if (fn) { | |
var err = new Error('Connection error'); | |
err.data = data; | |
fn(err); | |
} | |
self.maybeReconnectOnOpen(); | |
}); | |
// emit `connect_timeout` | |
if (false !== this._timeout) { | |
var timeout = this._timeout; | |
debug('connect attempt will timeout after %d', timeout); | |
// set timer | |
var timer = setTimeout(function(){ | |
debug('connect attempt timed out after %d', timeout); | |
openSub.destroy(); | |
socket.close(); | |
socket.emit('error', 'timeout'); | |
self.emitAll('connect_timeout', timeout); | |
}, timeout); | |
this.subs.push({ | |
destroy: function(){ | |
clearTimeout(timer); | |
} | |
}); | |
} | |
this.subs.push(openSub); | |
this.subs.push(errorSub); | |
return this; | |
}; | |
/** | |
* Called upon transport open. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onopen = function(){ | |
debug('open'); | |
// clear old subs | |
this.cleanup(); | |
// mark as open | |
this.readyState = 'open'; | |
this.emit('open'); | |
// add new subs | |
var socket = this.engine; | |
this.subs.push(on(socket, 'data', bind(this, 'ondata'))); | |
this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); | |
this.subs.push(on(socket, 'error', bind(this, 'onerror'))); | |
this.subs.push(on(socket, 'close', bind(this, 'onclose'))); | |
}; | |
/** | |
* Called with data. | |
* | |
* @api private | |
*/ | |
Manager.prototype.ondata = function(data){ | |
this.decoder.add(data); | |
}; | |
/** | |
* Called when parser fully decodes a packet. | |
* | |
* @api private | |
*/ | |
Manager.prototype.ondecoded = function(packet) { | |
this.emit('packet', packet); | |
}; | |
/** | |
* Called upon socket error. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onerror = function(err){ | |
debug('error', err); | |
this.emitAll('error', err); | |
}; | |
/** | |
* Creates a new socket for the given `nsp`. | |
* | |
* @return {Socket} | |
* @api public | |
*/ | |
Manager.prototype.socket = function(nsp){ | |
var socket = this.nsps[nsp]; | |
if (!socket) { | |
socket = new Socket(this, nsp); | |
this.nsps[nsp] = socket; | |
var self = this; | |
socket.on('connect', function(){ | |
if (!~indexOf(self.connected, socket)) { | |
self.connected.push(socket); | |
} | |
}); | |
} | |
return socket; | |
}; | |
/** | |
* Called upon a socket close. | |
* | |
* @param {Socket} socket | |
*/ | |
Manager.prototype.destroy = function(socket){ | |
var index = indexOf(this.connected, socket); | |
if (~index) this.connected.splice(index, 1); | |
if (this.connected.length) return; | |
this.close(); | |
}; | |
/** | |
* Writes a packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Manager.prototype.packet = function(packet){ | |
debug('writing packet %j', packet); | |
var self = this; | |
if (!self.encoding) { | |
// encode, then write to engine with result | |
self.encoding = true; | |
this.encoder.encode(packet, function(encodedPackets) { | |
for (var i = 0; i < encodedPackets.length; i++) { | |
self.engine.write(encodedPackets[i]); | |
} | |
self.encoding = false; | |
self.processPacketQueue(); | |
}); | |
} else { // add packet to the queue | |
self.packetBuffer.push(packet); | |
} | |
}; | |
/** | |
* 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 | |
*/ | |
Manager.prototype.cleanup = function(){ | |
var sub; | |
while (sub = this.subs.shift()) sub.destroy(); | |
this.packetBuffer = []; | |
this.encoding = false; | |
this.decoder.destroy(); | |
}; | |
/** | |
* Close the current socket. | |
* | |
* @api private | |
*/ | |
Manager.prototype.close = | |
Manager.prototype.disconnect = function(){ | |
this.skipReconnect = true; | |
this.readyState = 'closed'; | |
this.engine && this.engine.close(); | |
}; | |
/** | |
* Called upon engine close. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onclose = function(reason){ | |
debug('close'); | |
this.cleanup(); | |
this.readyState = 'closed'; | |
this.emit('close', reason); | |
if (this._reconnection && !this.skipReconnect) { | |
this.reconnect(); | |
} | |
}; | |
/** | |
* Attempt a reconnection. | |
* | |
* @api private | |
*/ | |
Manager.prototype.reconnect = function(){ | |
if (this.reconnecting || this.skipReconnect) return this; | |
var self = this; | |
this.attempts++; | |
if (this.attempts > this._reconnectionAttempts) { | |
debug('reconnect failed'); | |
this.emitAll('reconnect_failed'); | |
this.reconnecting = false; | |
} else { | |
var delay = this.attempts * this.reconnectionDelay(); | |
delay = Math.min(delay, this.reconnectionDelayMax()); | |
debug('will wait %dms before reconnect attempt', delay); | |
this.reconnecting = true; | |
var timer = setTimeout(function(){ | |
if (self.skipReconnect) return; | |
debug('attempting reconnect'); | |
self.emitAll('reconnect_attempt', self.attempts); | |
self.emitAll('reconnecting', self.attempts); | |
// check again for the case socket closed in above events | |
if (self.skipReconnect) return; | |
self.open(function(err){ | |
if (err) { | |
debug('reconnect attempt error'); | |
self.reconnecting = false; | |
self.reconnect(); | |
self.emitAll('reconnect_error', err.data); | |
} else { | |
debug('reconnect success'); | |
self.onreconnect(); | |
} | |
}); | |
}, delay); | |
this.subs.push({ | |
destroy: function(){ | |
clearTimeout(timer); | |
} | |
}); | |
} | |
}; | |
/** | |
* Called upon successful reconnect. | |
* | |
* @api private | |
*/ | |
Manager.prototype.onreconnect = function(){ | |
var attempt = this.attempts; | |
this.attempts = 0; | |
this.reconnecting = false; | |
this.emitAll('reconnect', attempt); | |
}; | |
},{"./on":4,"./socket":5,"./url":6,"component-bind":7,"component-emitter":8,"debug":9,"engine.io-client":10,"indexof":36,"object-component":37,"socket.io-parser":40}],4:[function(_dereq_,module,exports){ | |
/** | |
* Module exports. | |
*/ | |
module.exports = on; | |
/** | |
* Helper for subscriptions. | |
* | |
* @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` | |
* @param {String} event name | |
* @param {Function} callback | |
* @api public | |
*/ | |
function on(obj, ev, fn) { | |
obj.on(ev, fn); | |
return { | |
destroy: function(){ | |
obj.removeListener(ev, fn); | |
} | |
}; | |
} | |
},{}],5:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var parser = _dereq_('socket.io-parser'); | |
var Emitter = _dereq_('component-emitter'); | |
var toArray = _dereq_('to-array'); | |
var on = _dereq_('./on'); | |
var bind = _dereq_('component-bind'); | |
var debug = _dereq_('debug')('socket.io-client:socket'); | |
var hasBin = _dereq_('has-binary'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = exports = Socket; | |
/** | |
* Internal events (blacklisted). | |
* These events can't be emitted by the user. | |
* | |
* @api private | |
*/ | |
var events = { | |
connect: 1, | |
connect_error: 1, | |
connect_timeout: 1, | |
disconnect: 1, | |
error: 1, | |
reconnect: 1, | |
reconnect_attempt: 1, | |
reconnect_failed: 1, | |
reconnect_error: 1, | |
reconnecting: 1 | |
}; | |
/** | |
* Shortcut to `Emitter#emit`. | |
*/ | |
var emit = Emitter.prototype.emit; | |
/** | |
* `Socket` constructor. | |
* | |
* @api public | |
*/ | |
function Socket(io, nsp){ | |
this.io = io; | |
this.nsp = nsp; | |
this.json = this; // compat | |
this.ids = 0; | |
this.acks = {}; | |
if (this.io.autoConnect) this.open(); | |
this.receiveBuffer = []; | |
this.sendBuffer = []; | |
this.connected = false; | |
this.disconnected = true; | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Socket.prototype); | |
/** | |
* Subscribe to open, close and packet events | |
* | |
* @api private | |
*/ | |
Socket.prototype.subEvents = function() { | |
if (this.subs) return; | |
var io = this.io; | |
this.subs = [ | |
on(io, 'open', bind(this, 'onopen')), | |
on(io, 'packet', bind(this, 'onpacket')), | |
on(io, 'close', bind(this, 'onclose')) | |
]; | |
}; | |
/** | |
* "Opens" the socket. | |
* | |
* @api public | |
*/ | |
Socket.prototype.open = | |
Socket.prototype.connect = function(){ | |
if (this.connected) return this; | |
this.subEvents(); | |
this.io.open(); // ensure open | |
if ('open' == this.io.readyState) this.onopen(); | |
return this; | |
}; | |
/** | |
* Sends a `message` event. | |
* | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.send = function(){ | |
var args = toArray(arguments); | |
args.unshift('message'); | |
this.emit.apply(this, args); | |
return this; | |
}; | |
/** | |
* Override `emit`. | |
* If the event is in `events`, it's emitted normally. | |
* | |
* @param {String} event name | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.emit = function(ev){ | |
if (events.hasOwnProperty(ev)) { | |
emit.apply(this, arguments); | |
return this; | |
} | |
var args = toArray(arguments); | |
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]) { | |
debug('emitting packet with ack id %d', this.ids); | |
this.acks[this.ids] = args.pop(); | |
packet.id = this.ids++; | |
} | |
if (this.connected) { | |
this.packet(packet); | |
} else { | |
this.sendBuffer.push(packet); | |
} | |
return this; | |
}; | |
/** | |
* Sends a packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.packet = function(packet){ | |
packet.nsp = this.nsp; | |
this.io.packet(packet); | |
}; | |
/** | |
* Called upon engine `open`. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onopen = function(){ | |
debug('transport is open - connecting'); | |
// write connect packet if necessary | |
if ('/' != this.nsp) { | |
this.packet({ type: parser.CONNECT }); | |
} | |
}; | |
/** | |
* Called upon engine `close`. | |
* | |
* @param {String} reason | |
* @api private | |
*/ | |
Socket.prototype.onclose = function(reason){ | |
debug('close (%s)', reason); | |
this.connected = false; | |
this.disconnected = true; | |
this.emit('disconnect', reason); | |
}; | |
/** | |
* Called with socket packet. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onpacket = function(packet){ | |
if (packet.nsp != this.nsp) return; | |
switch (packet.type) { | |
case parser.CONNECT: | |
this.onconnect(); | |
break; | |
case parser.EVENT: | |
this.onevent(packet); | |
break; | |
case parser.BINARY_EVENT: | |
this.onevent(packet); | |
break; | |
case parser.ACK: | |
this.onack(packet); | |
break; | |
case parser.BINARY_ACK: | |
this.onack(packet); | |
break; | |
case parser.DISCONNECT: | |
this.ondisconnect(); | |
break; | |
case parser.ERROR: | |
this.emit('error', packet.data); | |
break; | |
} | |
}; | |
/** | |
* Called upon a server event. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onevent = function(packet){ | |
var args = packet.data || []; | |
debug('emitting event %j', args); | |
if (null != packet.id) { | |
debug('attaching ack callback to event'); | |
args.push(this.ack(packet.id)); | |
} | |
if (this.connected) { | |
emit.apply(this, args); | |
} else { | |
this.receiveBuffer.push(args); | |
} | |
}; | |
/** | |
* Produces an ack callback to emit with an event. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ack = function(id){ | |
var self = this; | |
var sent = false; | |
return function(){ | |
// prevent double callbacks | |
if (sent) return; | |
sent = true; | |
var args = toArray(arguments); | |
debug('sending ack %j', args); | |
var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK; | |
self.packet({ | |
type: type, | |
id: id, | |
data: args | |
}); | |
}; | |
}; | |
/** | |
* Called upon a server acknowlegement. | |
* | |
* @param {Object} packet | |
* @api private | |
*/ | |
Socket.prototype.onack = function(packet){ | |
debug('calling ack %s with %j', packet.id, packet.data); | |
var fn = this.acks[packet.id]; | |
fn.apply(this, packet.data); | |
delete this.acks[packet.id]; | |
}; | |
/** | |
* Called upon server connect. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onconnect = function(){ | |
this.connected = true; | |
this.disconnected = false; | |
this.emit('connect'); | |
this.emitBuffered(); | |
}; | |
/** | |
* Emit buffered events (received and emitted). | |
* | |
* @api private | |
*/ | |
Socket.prototype.emitBuffered = function(){ | |
var i; | |
for (i = 0; i < this.receiveBuffer.length; i++) { | |
emit.apply(this, this.receiveBuffer[i]); | |
} | |
this.receiveBuffer = []; | |
for (i = 0; i < this.sendBuffer.length; i++) { | |
this.packet(this.sendBuffer[i]); | |
} | |
this.sendBuffer = []; | |
}; | |
/** | |
* Called upon server disconnect. | |
* | |
* @api private | |
*/ | |
Socket.prototype.ondisconnect = function(){ | |
debug('server disconnect (%s)', this.nsp); | |
this.destroy(); | |
this.onclose('io server disconnect'); | |
}; | |
/** | |
* Called upon forced client/server side disconnections, | |
* this method ensures the manager stops tracking us and | |
* that reconnections don't get triggered for this. | |
* | |
* @api private. | |
*/ | |
Socket.prototype.destroy = function(){ | |
if (this.subs) { | |
// clean subscriptions to avoid reconnections | |
for (var i = 0; i < this.subs.length; i++) { | |
this.subs[i].destroy(); | |
} | |
this.subs = null; | |
} | |
this.io.destroy(this); | |
}; | |
/** | |
* Disconnects the socket manually. | |
* | |
* @return {Socket} self | |
* @api public | |
*/ | |
Socket.prototype.close = | |
Socket.prototype.disconnect = function(){ | |
if (this.connected) { | |
debug('performing disconnect (%s)', this.nsp); | |
this.packet({ type: parser.DISCONNECT }); | |
} | |
// remove socket from pool | |
this.destroy(); | |
if (this.connected) { | |
// fire events | |
this.onclose('io client disconnect'); | |
} | |
return this; | |
}; | |
},{"./on":4,"component-bind":7,"component-emitter":8,"debug":9,"has-binary":32,"socket.io-parser":40,"to-array":44}],6:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var parseuri = _dereq_('parseuri'); | |
var debug = _dereq_('debug')('socket.io-client:url'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = url; | |
/** | |
* URL parser. | |
* | |
* @param {String} url | |
* @param {Object} An object meant to mimic window.location. | |
* Defaults to window.location. | |
* @api public | |
*/ | |
function url(uri, loc){ | |
var obj = uri; | |
// default to window.location | |
var loc = loc || global.location; | |
if (null == uri) uri = loc.protocol + '//' + loc.hostname; | |
// relative path support | |
if ('string' == typeof uri) { | |
if ('/' == uri.charAt(0)) { | |
if ('/' == uri.charAt(1)) { | |
uri = loc.protocol + uri; | |
} else { | |
uri = loc.hostname + uri; | |
} | |
} | |
if (!/^(https?|wss?):\/\//.test(uri)) { | |
debug('protocol-less url %s', uri); | |
if ('undefined' != typeof loc) { | |
uri = loc.protocol + '//' + uri; | |
} else { | |
uri = 'https://' + uri; | |
} | |
} | |
// parse | |
debug('parse %s', uri); | |
obj = parseuri(uri); | |
} | |
// make sure we treat `localhost:80` and `localhost` equally | |
if (!obj.port) { | |
if (/^(http|ws)$/.test(obj.protocol)) { | |
obj.port = '80'; | |
} | |
else if (/^(http|ws)s$/.test(obj.protocol)) { | |
obj.port = '443'; | |
} | |
} | |
obj.path = obj.path || '/'; | |
// define unique id | |
obj.id = obj.protocol + '://' + obj.host + ':' + obj.port; | |
// define href | |
obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); | |
return obj; | |
} | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"debug":9,"parseuri":38}],7:[function(_dereq_,module,exports){ | |
/** | |
* Slice reference. | |
*/ | |
var slice = [].slice; | |
/** | |
* Bind `obj` to `fn`. | |
* | |
* @param {Object} obj | |
* @param {Function|String} fn or string | |
* @return {Function} | |
* @api public | |
*/ | |
module.exports = function(obj, fn){ | |
if ('string' == typeof fn) fn = obj[fn]; | |
if ('function' != typeof fn) throw new Error('bind() requires a function'); | |
var args = slice.call(arguments, 2); | |
return function(){ | |
return fn.apply(obj, args.concat(slice.call(arguments))); | |
} | |
}; | |
},{}],8:[function(_dereq_,module,exports){ | |
/** | |
* Expose `Emitter`. | |
*/ | |
module.exports = Emitter; | |
/** | |
* Initialize a new `Emitter`. | |
* | |
* @api public | |
*/ | |
function Emitter(obj) { | |
if (obj) return mixin(obj); | |
}; | |
/** | |
* Mixin the emitter properties. | |
* | |
* @param {Object} obj | |
* @return {Object} | |
* @api private | |
*/ | |
function mixin(obj) { | |
for (var key in Emitter.prototype) { | |
obj[key] = Emitter.prototype[key]; | |
} | |
return obj; | |
} | |
/** | |
* Listen on the given `event` with `fn`. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.on = | |
Emitter.prototype.addEventListener = function(event, fn){ | |
this._callbacks = this._callbacks || {}; | |
(this._callbacks[event] = this._callbacks[event] || []) | |
.push(fn); | |
return this; | |
}; | |
/** | |
* Adds an `event` listener that will be invoked a single | |
* time then automatically removed. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.once = function(event, fn){ | |
var self = this; | |
this._callbacks = this._callbacks || {}; | |
function on() { | |
self.off(event, on); | |
fn.apply(this, arguments); | |
} | |
on.fn = fn; | |
this.on(event, on); | |
return this; | |
}; | |
/** | |
* Remove the given callback for `event` or all | |
* registered callbacks. | |
* | |
* @param {String} event | |
* @param {Function} fn | |
* @return {Emitter} | |
* @api public | |
*/ | |
Emitter.prototype.off = | |
Emitter.prototype.removeListener = | |
Emitter.prototype.removeAllListeners = | |
Emitter.prototype.removeEventListener = function(event, fn){ | |
this._callbacks = this._callbacks || {}; | |
// all | |
if (0 == arguments.length) { | |
this._callbacks = {}; | |
return this; | |
} | |
// specific event | |
var callbacks = this._callbacks[event]; | |
if (!callbacks) return this; | |
// remove all handlers | |
if (1 == arguments.length) { | |
delete this._callbacks[event]; | |
return this; | |
} | |
// remove specific handler | |
var cb; | |
for (var i = 0; i < callbacks.length; i++) { | |
cb = callbacks[i]; | |
if (cb === fn || cb.fn === fn) { | |
callbacks.splice(i, 1); | |
break; | |
} | |
} | |
return this; | |
}; | |
/** | |
* Emit `event` with the given args. | |
* | |
* @param {String} event | |
* @param {Mixed} ... | |
* @return {Emitter} | |
*/ | |
Emitter.prototype.emit = function(event){ | |
this._callbacks = this._callbacks || {}; | |
var args = [].slice.call(arguments, 1) | |
, callbacks = this._callbacks[event]; | |
if (callbacks) { | |
callbacks = callbacks.slice(0); | |
for (var i = 0, len = callbacks.length; i < len; ++i) { | |
callbacks[i].apply(this, args); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Return array of callbacks for `event`. | |
* | |
* @param {String} event | |
* @return {Array} | |
* @api public | |
*/ | |
Emitter.prototype.listeners = function(event){ | |
this._callbacks = this._callbacks || {}; | |
return this._callbacks[event] || []; | |
}; | |
/** | |
* Check if this emitter has `event` handlers. | |
* | |
* @param {String} event | |
* @return {Boolean} | |
* @api public | |
*/ | |
Emitter.prototype.hasListeners = function(event){ | |
return !! this.listeners(event).length; | |
}; | |
},{}],9:[function(_dereq_,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(_dereq_,module,exports){ | |
module.exports = _dereq_('./lib/'); | |
},{"./lib/":11}],11:[function(_dereq_,module,exports){ | |
module.exports = _dereq_('./socket'); | |
/** | |
* Exports parser | |
* | |
* @api public | |
* | |
*/ | |
module.exports.parser = _dereq_('engine.io-parser'); | |
},{"./socket":12,"engine.io-parser":21}],12:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var transports = _dereq_('./transports'); | |
var Emitter = _dereq_('component-emitter'); | |
var debug = _dereq_('debug')('engine.io-client:socket'); | |
var index = _dereq_('indexof'); | |
var parser = _dereq_('engine.io-parser'); | |
var parseuri = _dereq_('parseuri'); | |
var parsejson = _dereq_('parsejson'); | |
var parseqs = _dereq_('parseqs'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = Socket; | |
/** | |
* Noop function. | |
* | |
* @api private | |
*/ | |
function noop(){} | |
/** | |
* Socket constructor. | |
* | |
* @param {String|Object} uri or options | |
* @param {Object} options | |
* @api public | |
*/ | |
function Socket(uri, opts){ | |
if (!(this instanceof Socket)) return new Socket(uri, opts); | |
opts = opts || {}; | |
if (uri && 'object' == typeof uri) { | |
opts = uri; | |
uri = null; | |
} | |
if (uri) { | |
uri = parseuri(uri); | |
opts.host = uri.host; | |
opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; | |
opts.port = uri.port; | |
if (uri.query) opts.query = uri.query; | |
} | |
this.secure = null != opts.secure ? opts.secure : | |
(global.location && 'https:' == location.protocol); | |
if (opts.host) { | |
var pieces = opts.host.split(':'); | |
opts.hostname = pieces.shift(); | |
if (pieces.length) opts.port = pieces.pop(); | |
} | |
this.agent = opts.agent || false; | |
this.hostname = opts.hostname || | |
(global.location ? location.hostname : 'localhost'); | |
this.port = opts.port || (global.location && location.port ? | |
location.port : | |
(this.secure ? 443 : 80)); | |
this.query = opts.query || {}; | |
if ('string' == typeof this.query) this.query = parseqs.decode(this.query); | |
this.upgrade = false !== opts.upgrade; | |
this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; | |
this.forceJSONP = !!opts.forceJSONP; | |
this.jsonp = false !== opts.jsonp; | |
this.forceBase64 = !!opts.forceBase64; | |
this.enablesXDR = !!opts.enablesXDR; | |
this.timestampParam = opts.timestampParam || 't'; | |
this.timestampRequests = opts.timestampRequests; | |
this.transports = opts.transports || ['polling', 'websocket']; | |
this.readyState = ''; | |
this.writeBuffer = []; | |
this.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`. | |
*/ | |
Emitter(Socket.prototype); | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
Socket.protocol = parser.protocol; // this is an int | |
/** | |
* Expose deps for legacy compatibility | |
* and standalone browser access. | |
*/ | |
Socket.Socket = Socket; | |
Socket.Transport = _dereq_('./transport'); | |
Socket.transports = _dereq_('./transports'); | |
Socket.parser = _dereq_('engine.io-parser'); | |
/** | |
* Creates transport of the given type. | |
* | |
* @param {String} transport name | |
* @return {Transport} | |
* @api private | |
*/ | |
Socket.prototype.createTransport = function (name) { | |
debug('creating transport "%s"', name); | |
var query = clone(this.query); | |
// append engine.io protocol identifier | |
query.EIO = parser.protocol; | |
// transport name | |
query.transport = name; | |
// session id if we already have one | |
if (this.id) query.sid = this.id; | |
var transport = new transports[name]({ | |
agent: this.agent, | |
hostname: this.hostname, | |
port: this.port, | |
secure: this.secure, | |
path: this.path, | |
query: query, | |
forceJSONP: this.forceJSONP, | |
jsonp: this.jsonp, | |
forceBase64: this.forceBase64, | |
enablesXDR: this.enablesXDR, | |
timestampRequests: this.timestampRequests, | |
timestampParam: this.timestampParam, | |
policyPort: this.policyPort, | |
socket: this | |
}); | |
return transport; | |
}; | |
function clone (obj) { | |
var o = {}; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
o[i] = obj[i]; | |
} | |
} | |
return o; | |
} | |
/** | |
* Initializes transport to use and starts probe. | |
* | |
* @api private | |
*/ | |
Socket.prototype.open = function () { | |
var transport; | |
if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { | |
transport = 'websocket'; | |
} else if (0 == this.transports.length) { | |
// Emit error on next tick so it can be listened to | |
var self = this; | |
setTimeout(function() { | |
self.emit('error', 'No transports available'); | |
}, 0); | |
return; | |
} else { | |
transport = this.transports[0]; | |
} | |
this.readyState = 'opening'; | |
// Retry with the next transport if the transport is disabled (jsonp: false) | |
var transport; | |
try { | |
transport = this.createTransport(transport); | |
} catch (e) { | |
this.transports.shift(); | |
this.open(); | |
return; | |
} | |
transport.open(); | |
this.setTransport(transport); | |
}; | |
/** | |
* Sets the current transport. Disables the existing one (if any). | |
* | |
* @api private | |
*/ | |
Socket.prototype.setTransport = function(transport){ | |
debug('setting transport %s', transport.name); | |
var self = this; | |
if (this.transport) { | |
debug('clearing existing transport %s', this.transport.name); | |
this.transport.removeAllListeners(); | |
} | |
// set up transport | |
this.transport = transport; | |
// set up transport listeners | |
transport | |
.on('drain', function(){ | |
self.onDrain(); | |
}) | |
.on('packet', function(packet){ | |
self.onPacket(packet); | |
}) | |
.on('error', function(e){ | |
self.onError(e); | |
}) | |
.on('close', function(){ | |
self.onClose('transport close'); | |
}); | |
}; | |
/** | |
* Probes a transport. | |
* | |
* @param {String} transport name | |
* @api private | |
*/ | |
Socket.prototype.probe = function (name) { | |
debug('probing transport "%s"', name); | |
var transport = this.createTransport(name, { probe: 1 }) | |
, failed = false | |
, self = this; | |
Socket.priorWebsocketSuccess = false; | |
function onTransportOpen(){ | |
if (self.onlyBinaryUpgrades) { | |
var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; | |
failed = failed || upgradeLosesBinary; | |
} | |
if (failed) return; | |
debug('probe transport "%s" opened', name); | |
transport.send([{ type: 'ping', data: 'probe' }]); | |
transport.once('packet', function (msg) { | |
if (failed) return; | |
if ('pong' == msg.type && 'probe' == msg.data) { | |
debug('probe transport "%s" pong', name); | |
self.upgrading = true; | |
self.emit('upgrading', transport); | |
if (!transport) return; | |
Socket.priorWebsocketSuccess = 'websocket' == transport.name; | |
debug('pausing current transport "%s"', self.transport.name); | |
self.transport.pause(function () { | |
if (failed) return; | |
if ('closed' == self.readyState) return; | |
debug('changing transport and sending upgrade packet'); | |
cleanup(); | |
self.setTransport(transport); | |
transport.send([{ type: 'upgrade' }]); | |
self.emit('upgrade', transport); | |
transport = null; | |
self.upgrading = false; | |
self.flush(); | |
}); | |
} else { | |
debug('probe transport "%s" failed', name); | |
var err = new Error('probe error'); | |
err.transport = transport.name; | |
self.emit('upgradeError', err); | |
} | |
}); | |
} | |
function freezeTransport() { | |
if (failed) return; | |
// Any callback called by transport should be ignored since now | |
failed = true; | |
cleanup(); | |
transport.close(); | |
transport = null; | |
} | |
//Handle any error that happens while probing | |
function onerror(err) { | |
var error = new Error('probe error: ' + err); | |
error.transport = transport.name; | |
freezeTransport(); | |
debug('probe transport "%s" failed because of error: %s', name, err); | |
self.emit('upgradeError', error); | |
} | |
function onTransportClose(){ | |
onerror("transport closed"); | |
} | |
//When the socket is closed while we're probing | |
function onclose(){ | |
onerror("socket closed"); | |
} | |
//When the socket is upgraded while we're probing | |
function onupgrade(to){ | |
if (transport && to.name != transport.name) { | |
debug('"%s" works - aborting "%s"', to.name, transport.name); | |
freezeTransport(); | |
} | |
} | |
//Remove all listeners on the transport and on self | |
function cleanup(){ | |
transport.removeListener('open', onTransportOpen); | |
transport.removeListener('error', onerror); | |
transport.removeListener('close', onTransportClose); | |
self.removeListener('close', onclose); | |
self.removeListener('upgrading', onupgrade); | |
} | |
transport.once('open', onTransportOpen); | |
transport.once('error', onerror); | |
transport.once('close', onTransportClose); | |
this.once('close', onclose); | |
this.once('upgrading', onupgrade); | |
transport.open(); | |
}; | |
/** | |
* Called when connection is deemed open. | |
* | |
* @api public | |
*/ | |
Socket.prototype.onOpen = function () { | |
debug('socket open'); | |
this.readyState = 'open'; | |
Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; | |
this.emit('open'); | |
this.flush(); | |
// we check for `readyState` in case an `open` | |
// listener already closed the socket | |
if ('open' == this.readyState && this.upgrade && this.transport.pause) { | |
debug('starting upgrade probes'); | |
for (var i = 0, l = this.upgrades.length; i < l; i++) { | |
this.probe(this.upgrades[i]); | |
} | |
} | |
}; | |
/** | |
* Handles a packet. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onPacket = function (packet) { | |
if ('opening' == this.readyState || 'open' == this.readyState) { | |
debug('socket receive: type "%s", data "%s"', packet.type, packet.data); | |
this.emit('packet', packet); | |
// Socket is live - any packet counts | |
this.emit('heartbeat'); | |
switch (packet.type) { | |
case 'open': | |
this.onHandshake(parsejson(packet.data)); | |
break; | |
case 'pong': | |
this.setPing(); | |
break; | |
case 'error': | |
var err = new Error('server error'); | |
err.code = packet.data; | |
this.emit('error', err); | |
break; | |
case 'message': | |
this.emit('data', packet.data); | |
this.emit('message', packet.data); | |
break; | |
} | |
} else { | |
debug('packet received with socket readyState "%s"', this.readyState); | |
} | |
}; | |
/** | |
* Called upon handshake completion. | |
* | |
* @param {Object} handshake obj | |
* @api private | |
*/ | |
Socket.prototype.onHandshake = function (data) { | |
this.emit('handshake', data); | |
this.id = data.sid; | |
this.transport.query.sid = data.sid; | |
this.upgrades = this.filterUpgrades(data.upgrades); | |
this.pingInterval = data.pingInterval; | |
this.pingTimeout = data.pingTimeout; | |
this.onOpen(); | |
// In case open handler closes socket | |
if ('closed' == this.readyState) return; | |
this.setPing(); | |
// Prolong liveness of socket on heartbeat | |
this.removeListener('heartbeat', this.onHeartbeat); | |
this.on('heartbeat', this.onHeartbeat); | |
}; | |
/** | |
* Resets ping timeout. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onHeartbeat = function (timeout) { | |
clearTimeout(this.pingTimeoutTimer); | |
var self = this; | |
self.pingTimeoutTimer = setTimeout(function () { | |
if ('closed' == self.readyState) return; | |
self.onClose('ping timeout'); | |
}, timeout || (self.pingInterval + self.pingTimeout)); | |
}; | |
/** | |
* Pings server every `this.pingInterval` and expects response | |
* within `this.pingTimeout` or closes connection. | |
* | |
* @api private | |
*/ | |
Socket.prototype.setPing = function () { | |
var self = this; | |
clearTimeout(self.pingIntervalTimer); | |
self.pingIntervalTimer = setTimeout(function () { | |
debug('writing ping packet - expecting pong within %sms', self.pingTimeout); | |
self.ping(); | |
self.onHeartbeat(self.pingTimeout); | |
}, self.pingInterval); | |
}; | |
/** | |
* Sends a ping packet. | |
* | |
* @api public | |
*/ | |
Socket.prototype.ping = function () { | |
this.sendPacket('ping'); | |
}; | |
/** | |
* Called on `drain` event | |
* | |
* @api private | |
*/ | |
Socket.prototype.onDrain = function() { | |
for (var i = 0; i < this.prevBufferLen; i++) { | |
if (this.callbackBuffer[i]) { | |
this.callbackBuffer[i](); | |
} | |
} | |
this.writeBuffer.splice(0, this.prevBufferLen); | |
this.callbackBuffer.splice(0, this.prevBufferLen); | |
// setting prevBufferLen = 0 is very important | |
// for example, when upgrading, upgrade packet is sent over, | |
// and a nonzero prevBufferLen could cause problems on `drain` | |
this.prevBufferLen = 0; | |
if (this.writeBuffer.length == 0) { | |
this.emit('drain'); | |
} else { | |
this.flush(); | |
} | |
}; | |
/** | |
* Flush write buffers. | |
* | |
* @api private | |
*/ | |
Socket.prototype.flush = function () { | |
if ('closed' != this.readyState && this.transport.writable && | |
!this.upgrading && this.writeBuffer.length) { | |
debug('flushing %d packets in socket', this.writeBuffer.length); | |
this.transport.send(this.writeBuffer); | |
// keep track of current length of writeBuffer | |
// splice writeBuffer and callbackBuffer on `drain` | |
this.prevBufferLen = this.writeBuffer.length; | |
this.emit('flush'); | |
} | |
}; | |
/** | |
* Sends a message. | |
* | |
* @param {String} message. | |
* @param {Function} callback function. | |
* @return {Socket} for chaining. | |
* @api public | |
*/ | |
Socket.prototype.write = | |
Socket.prototype.send = function (msg, fn) { | |
this.sendPacket('message', msg, fn); | |
return this; | |
}; | |
/** | |
* Sends a packet. | |
* | |
* @param {String} packet type. | |
* @param {String} data. | |
* @param {Function} callback function. | |
* @api private | |
*/ | |
Socket.prototype.sendPacket = function (type, data, fn) { | |
if ('closing' == this.readyState || 'closed' == this.readyState) { | |
return; | |
} | |
var packet = { type: type, data: data }; | |
this.emit('packetCreate', packet); | |
this.writeBuffer.push(packet); | |
this.callbackBuffer.push(fn); | |
this.flush(); | |
}; | |
/** | |
* Closes the connection. | |
* | |
* @api private | |
*/ | |
Socket.prototype.close = function () { | |
if ('opening' == this.readyState || 'open' == this.readyState) { | |
this.readyState = 'closing'; | |
var self = this; | |
function close() { | |
self.onClose('forced close'); | |
debug('socket closing - telling transport to close'); | |
self.transport.close(); | |
} | |
function cleanupAndClose() { | |
self.removeListener('upgrade', cleanupAndClose); | |
self.removeListener('upgradeError', cleanupAndClose); | |
close(); | |
} | |
function waitForUpgrade() { | |
// wait for upgrade to finish since we can't send packets while pausing a transport | |
self.once('upgrade', cleanupAndClose); | |
self.once('upgradeError', cleanupAndClose); | |
} | |
if (this.writeBuffer.length) { | |
this.once('drain', function() { | |
if (this.upgrading) { | |
waitForUpgrade(); | |
} else { | |
close(); | |
} | |
}); | |
} else if (this.upgrading) { | |
waitForUpgrade(); | |
} else { | |
close(); | |
} | |
} | |
return this; | |
}; | |
/** | |
* Called upon transport error | |
* | |
* @api private | |
*/ | |
Socket.prototype.onError = function (err) { | |
debug('socket error %j', err); | |
Socket.priorWebsocketSuccess = false; | |
this.emit('error', err); | |
this.onClose('transport error', err); | |
}; | |
/** | |
* Called upon transport close. | |
* | |
* @api private | |
*/ | |
Socket.prototype.onClose = function (reason, desc) { | |
if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) { | |
debug('socket close with reason: "%s"', reason); | |
var self = this; | |
// clear timers | |
clearTimeout(this.pingIntervalTimer); | |
clearTimeout(this.pingTimeoutTimer); | |
// clean buffers in next tick, so developers can still | |
// grab the buffers on `close` event | |
setTimeout(function() { | |
self.writeBuffer = []; | |
self.callbackBuffer = []; | |
self.prevBufferLen = 0; | |
}, 0); | |
// stop event from firing again for transport | |
this.transport.removeAllListeners('close'); | |
// ensure transport won't stay open | |
this.transport.close(); | |
// ignore further transport communication | |
this.transport.removeAllListeners(); | |
// set ready state | |
this.readyState = 'closed'; | |
// clear session id | |
this.id = null; | |
// emit close event | |
this.emit('close', reason, desc); | |
} | |
}; | |
/** | |
* Filters upgrades, returning only those matching client transports. | |
* | |
* @param {Array} server upgrades | |
* @api private | |
* | |
*/ | |
Socket.prototype.filterUpgrades = function (upgrades) { | |
var filteredUpgrades = []; | |
for (var i = 0, j = upgrades.length; i<j; i++) { | |
if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]); | |
} | |
return filteredUpgrades; | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./transport":13,"./transports":14,"component-emitter":8,"debug":9,"engine.io-parser":21,"indexof":36,"parsejson":28,"parseqs":29,"parseuri":30}],13:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var parser = _dereq_('engine.io-parser'); | |
var Emitter = _dereq_('component-emitter'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = Transport; | |
/** | |
* Transport abstract constructor. | |
* | |
* @param {Object} options. | |
* @api private | |
*/ | |
function Transport (opts) { | |
this.path = opts.path; | |
this.hostname = opts.hostname; | |
this.port = opts.port; | |
this.secure = opts.secure; | |
this.query = opts.query; | |
this.timestampParam = opts.timestampParam; | |
this.timestampRequests = opts.timestampRequests; | |
this.readyState = ''; | |
this.agent = opts.agent || false; | |
this.socket = opts.socket; | |
this.enablesXDR = opts.enablesXDR; | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Transport.prototype); | |
/** | |
* A counter used to prevent collisions in the timestamps used | |
* for cache busting. | |
*/ | |
Transport.timestamps = 0; | |
/** | |
* Emits an error. | |
* | |
* @param {String} str | |
* @return {Transport} for chaining | |
* @api public | |
*/ | |
Transport.prototype.onError = function (msg, desc) { | |
var err = new Error(msg); | |
err.type = 'TransportError'; | |
err.description = desc; | |
this.emit('error', err); | |
return this; | |
}; | |
/** | |
* Opens the transport. | |
* | |
* @api public | |
*/ | |
Transport.prototype.open = function () { | |
if ('closed' == this.readyState || '' == this.readyState) { | |
this.readyState = 'opening'; | |
this.doOpen(); | |
} | |
return this; | |
}; | |
/** | |
* Closes the transport. | |
* | |
* @api private | |
*/ | |
Transport.prototype.close = function () { | |
if ('opening' == this.readyState || 'open' == this.readyState) { | |
this.doClose(); | |
this.onClose(); | |
} | |
return this; | |
}; | |
/** | |
* Sends multiple packets. | |
* | |
* @param {Array} packets | |
* @api private | |
*/ | |
Transport.prototype.send = function(packets){ | |
if ('open' == this.readyState) { | |
this.write(packets); | |
} else { | |
throw new Error('Transport not open'); | |
} | |
}; | |
/** | |
* Called upon open | |
* | |
* @api private | |
*/ | |
Transport.prototype.onOpen = function () { | |
this.readyState = 'open'; | |
this.writable = true; | |
this.emit('open'); | |
}; | |
/** | |
* Called with data. | |
* | |
* @param {String} data | |
* @api private | |
*/ | |
Transport.prototype.onData = function(data){ | |
var packet = parser.decodePacket(data, this.socket.binaryType); | |
this.onPacket(packet); | |
}; | |
/** | |
* Called with a decoded packet. | |
*/ | |
Transport.prototype.onPacket = function (packet) { | |
this.emit('packet', packet); | |
}; | |
/** | |
* Called upon close. | |
* | |
* @api private | |
*/ | |
Transport.prototype.onClose = function () { | |
this.readyState = 'closed'; | |
this.emit('close'); | |
}; | |
},{"component-emitter":8,"engine.io-parser":21}],14:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies | |
*/ | |
var XMLHttpRequest = _dereq_('xmlhttprequest'); | |
var XHR = _dereq_('./polling-xhr'); | |
var JSONP = _dereq_('./polling-jsonp'); | |
var websocket = _dereq_('./websocket'); | |
/** | |
* Export transports. | |
*/ | |
exports.polling = polling; | |
exports.websocket = websocket; | |
/** | |
* Polling transport polymorphic constructor. | |
* Decides on xhr vs jsonp based on feature detection. | |
* | |
* @api private | |
*/ | |
function polling(opts){ | |
var xhr; | |
var xd = false; | |
var xs = false; | |
var jsonp = false !== opts.jsonp; | |
if (global.location) { | |
var isSSL = 'https:' == location.protocol; | |
var port = location.port; | |
// some user agents have empty `location.port` | |
if (!port) { | |
port = isSSL ? 443 : 80; | |
} | |
xd = opts.hostname != location.hostname || port != opts.port; | |
xs = opts.secure != isSSL; | |
} | |
opts.xdomain = xd; | |
opts.xscheme = xs; | |
xhr = new XMLHttpRequest(opts); | |
if ('open' in xhr && !opts.forceJSONP) { | |
return new XHR(opts); | |
} else { | |
if (!jsonp) throw new Error('JSONP disabled'); | |
return new JSONP(opts); | |
} | |
} | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./polling-jsonp":15,"./polling-xhr":16,"./websocket":18,"xmlhttprequest":19}],15:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Module requirements. | |
*/ | |
var Polling = _dereq_('./polling'); | |
var inherit = _dereq_('component-inherit'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = JSONPPolling; | |
/** | |
* Cached regular expressions. | |
*/ | |
var rNewline = /\n/g; | |
var rEscapedNewline = /\\n/g; | |
/** | |
* Global JSONP callbacks. | |
*/ | |
var callbacks; | |
/** | |
* Callbacks count. | |
*/ | |
var index = 0; | |
/** | |
* Noop. | |
*/ | |
function empty () { } | |
/** | |
* JSONP Polling constructor. | |
* | |
* @param {Object} opts. | |
* @api public | |
*/ | |
function JSONPPolling (opts) { | |
Polling.call(this, opts); | |
this.query = this.query || {}; | |
// define global callbacks array if not present | |
// we do this here (lazily) to avoid unneeded global pollution | |
if (!callbacks) { | |
// we need to consider multiple engines in the same page | |
if (!global.___eio) global.___eio = []; | |
callbacks = global.___eio; | |
} | |
// callback identifier | |
this.index = callbacks.length; | |
// add callback to jsonp global | |
var self = this; | |
callbacks.push(function (msg) { | |
self.onData(msg); | |
}); | |
// append to query string | |
this.query.j = this.index; | |
// prevent spurious errors from being emitted when the window is unloaded | |
if (global.document && global.addEventListener) { | |
global.addEventListener('beforeunload', function () { | |
if (self.script) self.script.onerror = empty; | |
}); | |
} | |
} | |
/** | |
* Inherits from Polling. | |
*/ | |
inherit(JSONPPolling, Polling); | |
/* | |
* JSONP only supports binary as base64 encoded strings | |
*/ | |
JSONPPolling.prototype.supportsBinary = false; | |
/** | |
* Closes the socket. | |
* | |
* @api private | |
*/ | |
JSONPPolling.prototype.doClose = function () { | |
if (this.script) { | |
this.script.parentNode.removeChild(this.script); | |
this.script = null; | |
} | |
if (this.form) { | |
this.form.parentNode.removeChild(this.form); | |
this.form = null; | |
this.iframe = null; | |
} | |
Polling.prototype.doClose.call(this); | |
}; | |
/** | |
* Starts a poll cycle. | |
* | |
* @api private | |
*/ | |
JSONPPolling.prototype.doPoll = function () { | |
var self = this; | |
var script = document.createElement('script'); | |
if (this.script) { | |
this.script.parentNode.removeChild(this.script); | |
this.script = null; | |
} | |
script.async = true; | |
script.src = this.uri(); | |
script.onerror = function(e){ | |
self.onError('jsonp poll error',e); | |
}; | |
var insertAt = document.getElementsByTagName('script')[0]; | |
insertAt.parentNode.insertBefore(script, insertAt); | |
this.script = script; | |
var isUAgecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent); | |
if (isUAgecko) { | |
setTimeout(function () { | |
var iframe = document.createElement('iframe'); | |
document.body.appendChild(iframe); | |
document.body.removeChild(iframe); | |
}, 100); | |
} | |
}; | |
/** | |
* Writes with a hidden iframe. | |
* | |
* @param {String} data to send | |
* @param {Function} called upon flush. | |
* @api private | |
*/ | |
JSONPPolling.prototype.doWrite = function (data, fn) { | |
var self = this; | |
if (!this.form) { | |
var form = document.createElement('form'); | |
var area = document.createElement('textarea'); | |
var id = this.iframeId = 'eio_iframe_' + this.index; | |
var iframe; | |
form.className = 'socketio'; | |
form.style.position = 'absolute'; | |
form.style.top = '-1000px'; | |
form.style.left = '-1000px'; | |
form.target = id; | |
form.method = 'POST'; | |
form.setAttribute('accept-charset', 'utf-8'); | |
area.name = 'd'; | |
form.appendChild(area); | |
document.body.appendChild(form); | |
this.form = form; | |
this.area = area; | |
} | |
this.form.action = this.uri(); | |
function complete () { | |
initIframe(); | |
fn(); | |
} | |
function initIframe () { | |
if (self.iframe) { | |
try { | |
self.form.removeChild(self.iframe); | |
} catch (e) { | |
self.onError('jsonp polling iframe removal error', e); | |
} | |
} | |
try { | |
// ie6 dynamic iframes with target="" support (thanks Chris Lambacher) | |
var html = '<iframe src="javascript:0" name="'+ self.iframeId +'">'; | |
iframe = document.createElement(html); | |
} catch (e) { | |
iframe = document.createElement('iframe'); | |
iframe.name = self.iframeId; | |
iframe.src = 'javascript:0'; | |
} | |
iframe.id = self.iframeId; | |
self.form.appendChild(iframe); | |
self.iframe = iframe; | |
} | |
initIframe(); | |
// escape \n to prevent it from being converted into \r\n by some UAs | |
// double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side | |
data = data.replace(rEscapedNewline, '\\\n'); | |
this.area.value = data.replace(rNewline, '\\n'); | |
try { | |
this.form.submit(); | |
} catch(e) {} | |
if (this.iframe.attachEvent) { | |
this.iframe.onreadystatechange = function(){ | |
if (self.iframe.readyState == 'complete') { | |
complete(); | |
} | |
}; | |
} else { | |
this.iframe.onload = complete; | |
} | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./polling":17,"component-inherit":20}],16:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Module requirements. | |
*/ | |
var XMLHttpRequest = _dereq_('xmlhttprequest'); | |
var Polling = _dereq_('./polling'); | |
var Emitter = _dereq_('component-emitter'); | |
var inherit = _dereq_('component-inherit'); | |
var debug = _dereq_('debug')('engine.io-client:polling-xhr'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = XHR; | |
module.exports.Request = Request; | |
/** | |
* Empty function | |
*/ | |
function empty(){} | |
/** | |
* XHR Polling constructor. | |
* | |
* @param {Object} opts | |
* @api public | |
*/ | |
function XHR(opts){ | |
Polling.call(this, opts); | |
if (global.location) { | |
var isSSL = 'https:' == location.protocol; | |
var port = location.port; | |
// some user agents have empty `location.port` | |
if (!port) { | |
port = isSSL ? 443 : 80; | |
} | |
this.xd = opts.hostname != global.location.hostname || | |
port != opts.port; | |
this.xs = opts.secure != isSSL; | |
} | |
} | |
/** | |
* Inherits from Polling. | |
*/ | |
inherit(XHR, Polling); | |
/** | |
* XHR supports binary | |
*/ | |
XHR.prototype.supportsBinary = true; | |
/** | |
* Creates a request. | |
* | |
* @param {String} method | |
* @api private | |
*/ | |
XHR.prototype.request = function(opts){ | |
opts = opts || {}; | |
opts.uri = this.uri(); | |
opts.xd = this.xd; | |
opts.xs = this.xs; | |
opts.agent = this.agent || false; | |
opts.supportsBinary = this.supportsBinary; | |
opts.enablesXDR = this.enablesXDR; | |
return new Request(opts); | |
}; | |
/** | |
* Sends data. | |
* | |
* @param {String} data to send. | |
* @param {Function} called upon flush. | |
* @api private | |
*/ | |
XHR.prototype.doWrite = function(data, fn){ | |
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){ | |
self.onError('xhr post error', err); | |
}); | |
this.sendXhr = req; | |
}; | |
/** | |
* Starts a poll cycle. | |
* | |
* @api private | |
*/ | |
XHR.prototype.doPoll = function(){ | |
debug('xhr poll'); | |
var req = this.request(); | |
var self = this; | |
req.on('data', function(data){ | |
self.onData(data); | |
}); | |
req.on('error', function(err){ | |
self.onError('xhr poll error', err); | |
}); | |
this.pollXhr = req; | |
}; | |
/** | |
* Request constructor | |
* | |
* @param {Object} options | |
* @api public | |
*/ | |
function Request(opts){ | |
this.method = opts.method || 'GET'; | |
this.uri = opts.uri; | |
this.xd = !!opts.xd; | |
this.xs = !!opts.xs; | |
this.async = false !== opts.async; | |
this.data = undefined != opts.data ? opts.data : null; | |
this.agent = opts.agent; | |
this.isBinary = opts.isBinary; | |
this.supportsBinary = opts.supportsBinary; | |
this.enablesXDR = opts.enablesXDR; | |
this.create(); | |
} | |
/** | |
* Mix in `Emitter`. | |
*/ | |
Emitter(Request.prototype); | |
/** | |
* Creates the XHR object and sends the request. | |
* | |
* @api private | |
*/ | |
Request.prototype.create = function(){ | |
var xhr = this.xhr = new XMLHttpRequest({ agent: this.agent, xdomain: this.xd, xscheme: this.xs, enablesXDR: this.enablesXDR }); | |
var self = this; | |
try { | |
debug('xhr open %s: %s', this.method, this.uri); | |
xhr.open(this.method, this.uri, this.async); | |
if (this.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 { | |
if (this.isBinary) { | |
xhr.setRequestHeader('Content-type', 'application/octet-stream'); | |
} else { | |
xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); | |
} | |
} catch (e) {} | |
} | |
// ie6 check | |
if ('withCredentials' in xhr) { | |
xhr.withCredentials = true; | |
} | |
if (this.hasXDR()) { | |
xhr.onload = function(){ | |
self.onLoad(); | |
}; | |
xhr.onerror = function(){ | |
self.onError(xhr.responseText); | |
}; | |
} else { | |
xhr.onreadystatechange = function(){ | |
if (4 != xhr.readyState) return; | |
if (200 == xhr.status || 1223 == xhr.status) { | |
self.onLoad(); | |
} else { | |
// make sure the `error` event handler that's user-set | |
// does not throw in the same tick and gets caught here | |
setTimeout(function(){ | |
self.onError(xhr.status); | |
}, 0); | |
} | |
}; | |
} | |
debug('xhr data %s', this.data); | |
xhr.send(this.data); | |
} catch (e) { | |
// 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() { | |
self.onError(e); | |
}, 0); | |
return; | |
} | |
if (global.document) { | |
this.index = Request.requestsCount++; | |
Request.requests[this.index] = this; | |
} | |
}; | |
/** | |
* Called upon successful response. | |
* | |
* @api private | |
*/ | |
Request.prototype.onSuccess = function(){ | |
this.emit('success'); | |
this.cleanup(); | |
}; | |
/** | |
* Called if we have data. | |
* | |
* @api private | |
*/ | |
Request.prototype.onData = function(data){ | |
this.emit('data', data); | |
this.onSuccess(); | |
}; | |
/** | |
* Called upon error. | |
* | |
* @api private | |
*/ | |
Request.prototype.onError = function(err){ | |
this.emit('error', err); | |
this.cleanup(); | |
}; | |
/** | |
* Cleans up house. | |
* | |
* @api private | |
*/ | |
Request.prototype.cleanup = function(){ | |
if ('undefined' == typeof this.xhr || null === this.xhr) { | |
return; | |
} | |
// xmlhttprequest | |
if (this.hasXDR()) { | |
this.xhr.onload = this.xhr.onerror = empty; | |
} else { | |
this.xhr.onreadystatechange = empty; | |
} | |
try { | |
this.xhr.abort(); | |
} catch(e) {} | |
if (global.document) { | |
delete Request.requests[this.index]; | |
} | |
this.xhr = null; | |
}; | |
/** | |
* Called upon load. | |
* | |
* @api private | |
*/ | |
Request.prototype.onLoad = function(){ | |
var data; | |
try { | |
var contentType; | |
try { | |
contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0]; | |
} catch (e) {} | |
if (contentType === 'application/octet-stream') { | |
data = this.xhr.response; | |
} else { | |
if (!this.supportsBinary) { | |
data = this.xhr.responseText; | |
} else { | |
data = 'ok'; | |
} | |
} | |
} catch (e) { | |
this.onError(e); | |
} | |
if (null != data) { | |
this.onData(data); | |
} | |
}; | |
/** | |
* Check if it has XDomainRequest. | |
* | |
* @api private | |
*/ | |
Request.prototype.hasXDR = function(){ | |
return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR; | |
}; | |
/** | |
* Aborts the request. | |
* | |
* @api public | |
*/ | |
Request.prototype.abort = function(){ | |
this.cleanup(); | |
}; | |
/** | |
* Aborts pending requests when unloading the window. This is needed to prevent | |
* memory leaks (e.g. when using IE) and to ensure that no spurious error is | |
* emitted. | |
*/ | |
if (global.document) { | |
Request.requestsCount = 0; | |
Request.requests = {}; | |
if (global.attachEvent) { | |
global.attachEvent('onunload', unloadHandler); | |
} else if (global.addEventListener) { | |
global.addEventListener('beforeunload', unloadHandler); | |
} | |
} | |
function unloadHandler() { | |
for (var i in Request.requests) { | |
if (Request.requests.hasOwnProperty(i)) { | |
Request.requests[i].abort(); | |
} | |
} | |
} | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./polling":17,"component-emitter":8,"component-inherit":20,"debug":9,"xmlhttprequest":19}],17:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var Transport = _dereq_('../transport'); | |
var parseqs = _dereq_('parseqs'); | |
var parser = _dereq_('engine.io-parser'); | |
var inherit = _dereq_('component-inherit'); | |
var debug = _dereq_('debug')('engine.io-client:polling'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = Polling; | |
/** | |
* Is XHR2 supported? | |
*/ | |
var hasXHR2 = (function() { | |
var XMLHttpRequest = _dereq_('xmlhttprequest'); | |
var xhr = new XMLHttpRequest({ xdomain: false }); | |
return null != xhr.responseType; | |
})(); | |
/** | |
* Polling interface. | |
* | |
* @param {Object} opts | |
* @api private | |
*/ | |
function Polling(opts){ | |
var forceBase64 = (opts && opts.forceBase64); | |
if (!hasXHR2 || forceBase64) { | |
this.supportsBinary = false; | |
} | |
Transport.call(this, opts); | |
} | |
/** | |
* Inherits from Transport. | |
*/ | |
inherit(Polling, Transport); | |
/** | |
* Transport name. | |
*/ | |
Polling.prototype.name = 'polling'; | |
/** | |
* Opens the socket (triggers polling). We write a PING message to determine | |
* when the transport is open. | |
* | |
* @api private | |
*/ | |
Polling.prototype.doOpen = function(){ | |
this.poll(); | |
}; | |
/** | |
* Pauses polling. | |
* | |
* @param {Function} callback upon buffers are flushed and transport is paused | |
* @api private | |
*/ | |
Polling.prototype.pause = function(onPause){ | |
var pending = 0; | |
var self = this; | |
this.readyState = 'pausing'; | |
function pause(){ | |
debug('paused'); | |
self.readyState = 'paused'; | |
onPause(); | |
} | |
if (this.polling || !this.writable) { | |
var total = 0; | |
if (this.polling) { | |
debug('we are currently polling - waiting to pause'); | |
total++; | |
this.once('pollComplete', function(){ | |
debug('pre-pause polling complete'); | |
--total || pause(); | |
}); | |
} | |
if (!this.writable) { | |
debug('we are currently writing - waiting to pause'); | |
total++; | |
this.once('drain', function(){ | |
debug('pre-pause writing complete'); | |
--total || pause(); | |
}); | |
} | |
} else { | |
pause(); | |
} | |
}; | |
/** | |
* Starts polling cycle. | |
* | |
* @api public | |
*/ | |
Polling.prototype.poll = function(){ | |
debug('polling'); | |
this.polling = true; | |
this.doPoll(); | |
this.emit('poll'); | |
}; | |
/** | |
* Overloads onData to detect payloads. | |
* | |
* @api private | |
*/ | |
Polling.prototype.onData = function(data){ | |
var self = this; | |
debug('polling got data %s', data); | |
var callback = function(packet, index, total) { | |
// if its the first message we consider the transport open | |
if ('opening' == self.readyState) { | |
self.onOpen(); | |
} | |
// if its a close packet, we close the ongoing requests | |
if ('close' == packet.type) { | |
self.onClose(); | |
return false; | |
} | |
// 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) { | |
// if we got data we're not polling | |
this.polling = false; | |
this.emit('pollComplete'); | |
if ('open' == this.readyState) { | |
this.poll(); | |
} else { | |
debug('ignoring poll - transport state "%s"', this.readyState); | |
} | |
} | |
}; | |
/** | |
* For polling, send a close packet. | |
* | |
* @api private | |
*/ | |
Polling.prototype.doClose = function(){ | |
var self = this; | |
function close(){ | |
debug('writing close packet'); | |
self.write([{ type: 'close' }]); | |
} | |
if ('open' == this.readyState) { | |
debug('transport open - closing'); | |
close(); | |
} else { | |
// in case we're trying to close while | |
// handshaking is in progress (GH-164) | |
debug('transport not open - deferring close'); | |
this.once('open', close); | |
} | |
}; | |
/** | |
* Writes a packets payload. | |
* | |
* @param {Array} data packets | |
* @param {Function} drain callback | |
* @api private | |
*/ | |
Polling.prototype.write = function(packets){ | |
var self = this; | |
this.writable = false; | |
var callbackfn = function() { | |
self.writable = true; | |
self.emit('drain'); | |
}; | |
var self = this; | |
parser.encodePayload(packets, this.supportsBinary, function(data) { | |
self.doWrite(data, callbackfn); | |
}); | |
}; | |
/** | |
* Generates uri for connection. | |
* | |
* @api private | |
*/ | |
Polling.prototype.uri = function(){ | |
var query = this.query || {}; | |
var schema = this.secure ? 'https' : 'http'; | |
var port = ''; | |
// cache busting is forced | |
if (false !== this.timestampRequests) { | |
query[this.timestampParam] = +new Date + '-' + Transport.timestamps++; | |
} | |
if (!this.supportsBinary && !query.sid) { | |
query.b64 = 1; | |
} | |
query = parseqs.encode(query); | |
// avoid port if default for schema | |
if (this.port && (('https' == schema && this.port != 443) || | |
('http' == schema && this.port != 80))) { | |
port = ':' + this.port; | |
} | |
// prepend ? to query | |
if (query.length) { | |
query = '?' + query; | |
} | |
return schema + '://' + this.hostname + port + this.path + query; | |
}; | |
},{"../transport":13,"component-inherit":20,"debug":9,"engine.io-parser":21,"parseqs":29,"xmlhttprequest":19}],18:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var Transport = _dereq_('../transport'); | |
var parser = _dereq_('engine.io-parser'); | |
var parseqs = _dereq_('parseqs'); | |
var inherit = _dereq_('component-inherit'); | |
var debug = _dereq_('debug')('engine.io-client:websocket'); | |
/** | |
* `ws` exposes a WebSocket-compatible interface in | |
* Node, or the `WebSocket` or `MozWebSocket` globals | |
* in the browser. | |
*/ | |
var WebSocket = require('net.iamyellow.tiws'); | |
/** | |
* Module exports. | |
*/ | |
module.exports = WS; | |
/** | |
* WebSocket transport constructor. | |
* | |
* @api {Object} connection options | |
* @api public | |
*/ | |
function WS(opts){ | |
var forceBase64 = (opts && opts.forceBase64); | |
if (forceBase64) { | |
this.supportsBinary = false; | |
} | |
Transport.call(this, opts); | |
} | |
/** | |
* Inherits from Transport. | |
*/ | |
inherit(WS, Transport); | |
/** | |
* Transport name. | |
* | |
* @api public | |
*/ | |
WS.prototype.name = 'websocket'; | |
/* | |
* WebSockets support binary | |
*/ | |
WS.prototype.supportsBinary = true; | |
/** | |
* Opens socket. | |
* | |
* @api private | |
*/ | |
WS.prototype.doOpen = function(){ | |
// TODO: Figure out and fix. This causes initial connection to time out on iOS. | |
if (!this.check()) { | |
// let probe timeout | |
return; | |
} | |
var self = this; | |
var uri = this.uri(); | |
var protocols = void(0); | |
var opts = { agent: this.agent }; | |
this.ws = WebSocket.createWS(); | |
if (this.ws.binaryType === undefined) { | |
this.supportsBinary = false; | |
} | |
this.ws.binaryType = 'arraybuffer'; | |
this.addEventListeners(); | |
this.ws.open(uri); | |
}; | |
/** | |
* Adds event listeners to the socket | |
* | |
* @api private | |
*/ | |
WS.prototype.addEventListeners = function(){ | |
var self = this; | |
this.ws.addEventListener('open', function () { | |
self.onOpen(); | |
}); | |
this.ws.addEventListener('close', function (ev) { | |
self.onClose(); | |
}); | |
this.ws.addEventListener('message', function (ev) { | |
self.onData(ev.data); | |
}); | |
this.ws.addEventListener('error', function (e) { | |
self.onError('websocket error', e); | |
}); | |
}; | |
/** | |
* Override `onData` to use a timer on iOS. | |
* See: https://gist.github.com/mloughran/2052006 | |
* | |
* @api private | |
*/ | |
if ('undefined' != typeof navigator | |
&& /iPad|iPhone|iPod/i.test(navigator.userAgent)) { | |
WS.prototype.onData = function(data){ | |
var self = this; | |
setTimeout(function(){ | |
Transport.prototype.onData.call(self, data); | |
}, 0); | |
}; | |
} | |
/** | |
* Writes data to socket. | |
* | |
* @param {Array} array of packets. | |
* @api private | |
*/ | |
WS.prototype.write = function(packets){ | |
var self = this; | |
this.writable = false; | |
// encodePacket efficient as it uses WS framing | |
// no need for encodePayload | |
for (var i = 0, l = packets.length; i < l; i++) { | |
parser.encodePacket(packets[i], this.supportsBinary, function(data) { | |
//Sometimes the websocket has already been closed but the browser didn't | |
//have a chance of informing us about it yet, in that case send will | |
//throw an error | |
try { | |
self.ws.send(data); | |
} catch (e){ | |
debug('websocket closed before onclose event'); | |
} | |
}); | |
} | |
function ondrain() { | |
self.writable = true; | |
self.emit('drain'); | |
} | |
// fake drain | |
// defer to next tick to allow Socket to clear writeBuffer | |
setTimeout(ondrain, 0); | |
}; | |
/** | |
* Called upon close | |
* | |
* @api private | |
*/ | |
WS.prototype.onClose = function(){ | |
Transport.prototype.onClose.call(this); | |
}; | |
/** | |
* Closes socket. | |
* | |
* @api private | |
*/ | |
WS.prototype.doClose = function(){ | |
if (typeof this.ws !== 'undefined') { | |
this.ws.close(); | |
} | |
}; | |
/** | |
* Generates uri for connection. | |
* | |
* @api private | |
*/ | |
WS.prototype.uri = function(){ | |
var query = this.query || {}; | |
var schema = this.secure ? 'wss' : 'ws'; | |
var port = ''; | |
// avoid port if default for schema | |
if (this.port && (('wss' == schema && this.port != 443) | |
|| ('ws' == schema && this.port != 80))) { | |
port = ':' + this.port; | |
} | |
// append timestamp to URI | |
if (this.timestampRequests) { | |
query[this.timestampParam] = +new Date; | |
} | |
// communicate binary support capabilities | |
if (!this.supportsBinary) { | |
query.b64 = 1; | |
} | |
query = parseqs.encode(query); | |
// prepend ? to query | |
if (query.length) { | |
query = '?' + query; | |
} | |
return schema + '://' + this.hostname + port + this.path + query; | |
}; | |
/** | |
* Feature detection for WebSocket. | |
* | |
* @return {Boolean} whether this transport is available. | |
* @api public | |
*/ | |
WS.prototype.check = function(){ | |
return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); | |
}; | |
},{"../transport":13,"component-inherit":20,"debug":9,"engine.io-parser":21,"parseqs":29,"ws":31}],19:[function(_dereq_,module,exports){ | |
// browser shim for xmlhttprequest module | |
var hasCORS = _dereq_('has-cors'); | |
module.exports = function(opts) { | |
var xdomain = opts.xdomain; | |
// scheme must be same when usign XDomainRequest | |
// http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx | |
var xscheme = opts.xscheme; | |
// XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default. | |
// https://github.com/Automattic/engine.io-client/pull/217 | |
var enablesXDR = opts.enablesXDR; | |
// XMLHttpRequest can be disabled on IE | |
try { | |
if ('undefined' != typeof XMLHttpRequest && (!xdomain || hasCORS)) { | |
return new XMLHttpRequest(); | |
} | |
} catch (e) { } | |
// Use XDomainRequest for IE8 if enablesXDR is true | |
// because loading bar keeps flashing when using jsonp-polling | |
// https://github.com/yujiosaka/socke.io-ie8-loading-example | |
try { | |
if ('undefined' != typeof XDomainRequest && !xscheme && enablesXDR) { | |
return new XDomainRequest(); | |
} | |
} catch (e) { } | |
if (!xdomain) { | |
try { | |
return new ActiveXObject('Microsoft.XMLHTTP'); | |
} catch(e) { } | |
} | |
} | |
},{"has-cors":34}],20:[function(_dereq_,module,exports){ | |
module.exports = function(a, b){ | |
var fn = function(){}; | |
fn.prototype = b.prototype; | |
a.prototype = new fn; | |
a.prototype.constructor = a; | |
}; | |
},{}],21:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Module dependencies. | |
*/ | |
var keys = _dereq_('./keys'); | |
var sliceBuffer = _dereq_('arraybuffer.slice'); | |
var base64encoder = _dereq_('base64-arraybuffer'); | |
var after = _dereq_('after'); | |
var utf8 = _dereq_('utf8'); | |
/** | |
* Check if we are running an android browser. That requires us to use | |
* ArrayBuffer with polling transports... | |
* | |
* http://ghinda.net/jpeg-blob-ajax-android/ | |
*/ | |
var isAndroid = false; // navigator.userAgent.match(/Android/i); | |
/** | |
* Current protocol version. | |
*/ | |
exports.protocol = 3; | |
/** | |
* Packet types. | |
*/ | |
var packets = exports.packets = { | |
open: 0 // non-ws | |
, close: 1 // non-ws | |
, ping: 2 | |
, pong: 3 | |
, message: 4 | |
, upgrade: 5 | |
, noop: 6 | |
}; | |
var packetslist = keys(packets); | |
/** | |
* Premade error packet. | |
*/ | |
var err = { type: 'error', data: 'parser error' }; | |
/** | |
* Create a blob api even for blob builder when vendor prefixes exist | |
*/ | |
var Blob = _dereq_('blob'); | |
/** | |
* Encodes a packet. | |
* | |
* <packet type id> [ <data> ] | |
* | |
* Example: | |
* | |
* 5hello world | |
* 3 | |
* 4 | |
* | |
* Binary is encoded in an identical principle | |
* | |
* @api private | |
*/ | |
exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) { | |
if ('function' == typeof supportsBinary) { | |
callback = supportsBinary; | |
supportsBinary = false; | |
} | |
if ('function' == typeof utf8encode) { | |
callback = utf8encode; | |
utf8encode = null; | |
} | |
var data = (packet.data === undefined) | |
? undefined | |
: packet.data.buffer || packet.data; | |
if (global.ArrayBuffer && data instanceof ArrayBuffer) { | |
return encodeArrayBuffer(packet, supportsBinary, callback); | |
} else if (Blob && data instanceof global.Blob) { | |
return encodeBlob(packet, supportsBinary, callback); | |
} | |
// Sending data as a utf-8 string | |
var encoded = packets[packet.type]; | |
// data fragment is optional | |
if (undefined !== packet.data) { | |
encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data); | |
} | |
return callback('' + encoded); | |
}; | |
/** | |
* Encode packet helpers for binary types | |
*/ | |
function encodeArrayBuffer(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return exports.encodeBase64Packet(packet, callback); | |
} | |
var data = packet.data; | |
var contentArray = new Uint8Array(data); | |
var resultBuffer = new Uint8Array(1 + data.byteLength); | |
resultBuffer[0] = packets[packet.type]; | |
for (var i = 0; i < contentArray.length; i++) { | |
resultBuffer[i+1] = contentArray[i]; | |
} | |
return callback(resultBuffer.buffer); | |
} | |
function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return exports.encodeBase64Packet(packet, callback); | |
} | |
var fr = new FileReader(); | |
fr.onload = function() { | |
packet.data = fr.result; | |
exports.encodePacket(packet, supportsBinary, true, callback); | |
}; | |
return fr.readAsArrayBuffer(packet.data); | |
} | |
function encodeBlob(packet, supportsBinary, callback) { | |
if (!supportsBinary) { | |
return exports.encodeBase64Packet(packet, callback); | |
} | |
if (isAndroid) { | |
return encodeBlobAsArrayBuffer(packet, supportsBinary, callback); | |
} | |
var length = new Uint8Array(1); | |
length[0] = packets[packet.type]; | |
var blob = new Blob([length.buffer, packet.data]); | |
return callback(blob); | |
} | |
/** | |
* Encodes a packet with binary data in a base64 string | |
* | |
* @param {Object} packet, has `type` and `data` | |
* @return {String} base64 encoded message | |
*/ | |
exports.encodeBase64Packet = function(packet, callback) { | |
var message = 'b' + exports.packets[packet.type]; | |
if (Blob && packet.data instanceof Blob) { | |
var fr = new FileReader(); | |
fr.onload = function() { | |
var b64 = fr.result.split(',')[1]; | |
callback(message + b64); | |
}; | |
return fr.readAsDataURL(packet.data); | |
} | |
var b64data; | |
try { | |
b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data)); | |
} catch (e) { | |
// iPhone Safari doesn't let you apply with typed arrays | |
var typed = new Uint8Array(packet.data); | |
var basic = new Array(typed.length); | |
for (var i = 0; i < typed.length; i++) { | |
basic[i] = typed[i]; | |
} | |
b64data = String.fromCharCode.apply(null, basic); | |
} | |
message += global.btoa(b64data); | |
return callback(message); | |
}; | |
/** | |
* Decodes a packet. Changes format to Blob if requested. | |
* | |
* @return {Object} with `type` and `data` (if any) | |
* @api private | |
*/ | |
exports.decodePacket = function (data, binaryType, utf8decode) { | |
// String data | |
if (typeof data == 'string' || data === undefined) { | |
if (data.charAt(0) == 'b') { | |
return exports.decodeBase64Packet(data.substr(1), binaryType); | |
} | |
if (utf8decode) { | |
try { | |
data = utf8.decode(data); | |
} catch (e) { | |
return err; | |
} | |
} | |
var type = data.charAt(0); | |
if (Number(type) != type || !packetslist[type]) { | |
return err; | |
} | |
if (data.length > 1) { | |
return { type: packetslist[type], data: data.substring(1) }; | |
} else { | |
return { type: packetslist[type] }; | |
} | |
} | |
var asArray = new Uint8Array(data); | |
var type = asArray[0]; | |
var rest = sliceBuffer(data, 1); | |
if (Blob && binaryType === 'blob') { | |
rest = new Blob([rest]); | |
} | |
return { type: packetslist[type], data: rest }; | |
}; | |
/** | |
* Decodes a packet encoded in a base64 string | |
* | |
* @param {String} base64 encoded message | |
* @return {Object} with `type` and `data` (if any) | |
*/ | |
exports.decodeBase64Packet = function(msg, binaryType) { | |
var type = packetslist[msg.charAt(0)]; | |
if (!global.ArrayBuffer) { | |
return { type: type, data: { base64: true, data: msg.substr(1) } }; | |
} | |
var data = base64encoder.decode(msg.substr(1)); | |
if (binaryType === 'blob' && Blob) { | |
data = new Blob([data]); | |
} | |
return { type: type, data: data }; | |
}; | |
/** | |
* Encodes multiple messages (payload). | |
* | |
* <length>:data | |
* | |
* Example: | |
* | |
* 11:hello world2:hi | |
* | |
* If any contents are binary, they will be encoded as base64 strings. Base64 | |
* encoded strings are marked with a b before the length specifier | |
* | |
* @param {Array} packets | |
* @api private | |
*/ | |
exports.encodePayload = function (packets, supportsBinary, callback) { | |
if (typeof supportsBinary == 'function') { | |
callback = supportsBinary; | |
supportsBinary = null; | |
} | |
if (supportsBinary) { | |
if (Blob && !isAndroid) { | |
return exports.encodePayloadAsBlob(packets, callback); | |
} | |
return exports.encodePayloadAsArrayBuffer(packets, callback); | |
} | |
if (!packets.length) { | |
return callback('0:'); | |
} | |
function setLengthHeader(message) { | |
return message.length + ':' + message; | |
} | |
function encodeOne(packet, doneCallback) { | |
exports.encodePacket(packet, supportsBinary, true, function(message) { | |
doneCallback(null, setLengthHeader(message)); | |
}); | |
} | |
map(packets, encodeOne, function(err, results) { | |
return callback(results.join('')); | |
}); | |
}; | |
/** | |
* Async array map using after | |
*/ | |
function map(ary, each, done) { | |
var result = new Array(ary.length); | |
var next = after(ary.length, done); | |
var eachWithIndex = function(i, el, cb) { | |
each(el, function(error, msg) { | |
result[i] = msg; | |
cb(error, result); | |
}); | |
}; | |
for (var i = 0; i < ary.length; i++) { | |
eachWithIndex(i, ary[i], next); | |
} | |
} | |
/* | |
* Decodes data when a payload is maybe expected. Possible binary contents are | |
* decoded from their base64 representation | |
* | |
* @param {String} data, callback method | |
* @api public | |
*/ | |
exports.decodePayload = function (data, binaryType, callback) { | |
if (typeof data != 'string') { | |
return exports.decodePayloadAsBinary(data, binaryType, callback); | |
} | |
if (typeof binaryType === 'function') { | |
callback = binaryType; | |
binaryType = null; | |
} | |
var packet; | |
if (data == '') { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
var length = '' | |
, n, msg; | |
for (var i = 0, l = data.length; i < l; i++) { | |
var chr = data.charAt(i); | |
if (':' != chr) { | |
length += chr; | |
} else { | |
if ('' == length || (length != (n = Number(length)))) { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
msg = data.substr(i + 1, n); | |
if (length != msg.length) { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
if (msg.length) { | |
packet = exports.decodePacket(msg, binaryType, true); | |
if (err.type == packet.type && err.data == packet.data) { | |
// parser error in individual packet - ignoring payload | |
return callback(err, 0, 1); | |
} | |
var ret = callback(packet, i + n, l); | |
if (false === ret) return; | |
} | |
// advance cursor | |
i += n; | |
length = ''; | |
} | |
} | |
if (length != '') { | |
// parser error - ignoring payload | |
return callback(err, 0, 1); | |
} | |
}; | |
/** | |
* Encodes multiple messages (payload) as binary. | |
* | |
* <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number | |
* 255><data> | |
* | |
* Example: | |
* 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers | |
* | |
* @param {Array} packets | |
* @return {ArrayBuffer} encoded payload | |
* @api private | |
*/ | |
exports.encodePayloadAsArrayBuffer = function(packets, callback) { | |
if (!packets.length) { | |
return callback(new ArrayBuffer(0)); | |
} | |
function encodeOne(packet, doneCallback) { | |
exports.encodePacket(packet, true, true, function(data) { | |
return doneCallback(null, data); | |
}); | |
} | |
map(packets, encodeOne, function(err, encodedPackets) { | |
var totalLength = encodedPackets.reduce(function(acc, p) { | |
var len; | |
if (typeof p === 'string'){ | |
len = p.length; | |
} else { | |
len = p.byteLength; | |
} | |
return acc + len.toString().length + len + 2; // string/binary identifier + separator = 2 | |
}, 0); | |
var resultArray = new Uint8Array(totalLength); | |
var bufferIndex = 0; | |
encodedPackets.forEach(function(p) { | |
var isString = typeof p === 'string'; | |
var ab = p; | |
if (isString) { | |
var view = new Uint8Array(p.length); | |
for (var i = 0; i < p.length; i++) { | |
view[i] = p.charCodeAt(i); | |
} | |
ab = view.buffer; | |
} | |
if (isString) { // not true binary | |
resultArray[bufferIndex++] = 0; | |
} else { // true binary | |
resultArray[bufferIndex++] = 1; | |
} | |
var lenStr = ab.byteLength.toString(); | |
for (var i = 0; i < lenStr.length; i++) { | |
resultArray[bufferIndex++] = parseInt(lenStr[i]); | |
} | |
resultArray[bufferIndex++] = 255; | |
var view = new Uint8Array(ab); | |
for (var i = 0; i < view.length; i++) { | |
resultArray[bufferIndex++] = view[i]; | |
} | |
}); | |
return callback(resultArray.buffer); | |
}); | |
}; | |
/** | |
* Encode as Blob | |
*/ | |
exports.encodePayloadAsBlob = function(packets, callback) { | |
function encodeOne(packet, doneCallback) { | |
exports.encodePacket(packet, true, true, function(encoded) { | |
var binaryIdentifier = new Uint8Array(1); | |
binaryIdentifier[0] = 1; | |
if (typeof encoded === 'string') { | |
var view = new Uint8Array(encoded.length); | |
for (var i = 0; i < encoded.length; i++) { | |
view[i] = encoded.charCodeAt(i); | |
} | |
encoded = view.buffer; | |
binaryIdentifier[0] = 0; | |
} | |
var len = (encoded instanceof ArrayBuffer) | |
? encoded.byteLength | |
: encoded.size; | |
var lenStr = len.toString(); | |
var lengthAry = new Uint8Array(lenStr.length + 1); | |
for (var i = 0; i < lenStr.length; i++) { | |
lengthAry[i] = parseInt(lenStr[i]); | |
} | |
lengthAry[lenStr.length] = 255; | |
if (Blob) { | |
var blob = new Blob([binaryIdentifier.buffer, lengthAry.buffer, encoded]); | |
doneCallback(null, blob); | |
} | |
}); | |
} | |
map(packets, encodeOne, function(err, results) { | |
return callback(new Blob(results)); | |
}); | |
}; | |
/* | |
* Decodes data when a payload is maybe expected. Strings are decoded by | |
* interpreting each byte as a key code for entries marked to start with 0. See | |
* description of encodePayloadAsBinary | |
* | |
* @param {ArrayBuffer} data, callback method | |
* @api public | |
*/ | |
exports.decodePayloadAsBinary = function (data, binaryType, callback) { | |
if (typeof binaryType === 'function') { | |
callback = binaryType; | |
binaryType = null; | |
} | |
var bufferTail = data; | |
var buffers = []; | |
var numberTooLong = false; | |
while (bufferTail.byteLength > 0) { | |
var tailArray = new Uint8Array(bufferTail); | |
var isString = tailArray[0] === 0; | |
var msgLength = ''; | |
for (var i = 1; ; i++) { | |
if (tailArray[i] == 255) break; | |
if (msgLength.length > 310) { | |
numberTooLong = true; | |
break; | |
} | |
msgLength += tailArray[i]; | |
} | |
if(numberTooLong) return callback(err, 0, 1); | |
bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length); | |
msgLength = parseInt(msgLength); | |
var msg = sliceBuffer(bufferTail, 0, msgLength); | |
if (isString) { | |
try { | |
msg = String.fromCharCode.apply(null, new Uint8Array(msg)); | |
} catch (e) { | |
// iPhone Safari doesn't let you apply to typed arrays | |
var typed = new Uint8Array(msg); | |
msg = ''; | |
for (var i = 0; i < typed.length; i++) { | |
msg += String.fromCharCode(typed[i]); | |
} | |
} | |
} | |
buffers.push(msg); | |
bufferTail = sliceBuffer(bufferTail, msgLength); | |
} | |
var total = buffers.length; | |
buffers.forEach(function(buffer, i) { | |
callback(exports.decodePacket(buffer, binaryType, true), i, total); | |
}); | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./keys":22,"after":23,"arraybuffer.slice":24,"base64-arraybuffer":25,"blob":26,"utf8":27}],22:[function(_dereq_,module,exports){ | |
/** | |
* Gets the keys for an object. | |
* | |
* @return {Array} keys | |
* @api private | |
*/ | |
module.exports = Object.keys || function keys (obj){ | |
var arr = []; | |
var has = Object.prototype.hasOwnProperty; | |
for (var i in obj) { | |
if (has.call(obj, i)) { | |
arr.push(i); | |
} | |
} | |
return arr; | |
}; | |
},{}],23:[function(_dereq_,module,exports){ | |
module.exports = after | |
function after(count, callback, err_cb) { | |
var bail = false | |
err_cb = err_cb || noop | |
proxy.count = count | |
return (count === 0) ? callback() : proxy | |
function proxy(err, result) { | |
if (proxy.count <= 0) { | |
throw new Error('after called too many times') | |
} | |
--proxy.count | |
// after first error, rest are passed to err_cb | |
if (err) { | |
bail = true | |
callback(err) | |
// future error callbacks will go to error handler | |
callback = err_cb | |
} else if (proxy.count === 0 && !bail) { | |
callback(null, result) | |
} | |
} | |
} | |
function noop() {} | |
},{}],24:[function(_dereq_,module,exports){ | |
/** | |
* An abstraction for slicing an arraybuffer even when | |
* ArrayBuffer.prototype.slice is not supported | |
* | |
* @api public | |
*/ | |
module.exports = function(arraybuffer, start, end) { | |
var bytes = arraybuffer.byteLength; | |
start = start || 0; | |
end = end || bytes; | |
if (arraybuffer.slice) { return arraybuffer.slice(start, end); } | |
if (start < 0) { start += bytes; } | |
if (end < 0) { end += bytes; } | |
if (end > bytes) { end = bytes; } | |
if (start >= bytes || start >= end || bytes === 0) { | |
return new ArrayBuffer(0); | |
} | |
var abv = new Uint8Array(arraybuffer); | |
var result = new Uint8Array(end - start); | |
for (var i = start, ii = 0; i < end; i++, ii++) { | |
result[ii] = abv[i]; | |
} | |
return result.buffer; | |
}; | |
},{}],25:[function(_dereq_,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.length, base64 = ""; | |
for (i = 0; i < len; i+=3) { | |
base64 += chars[bytes[i] >> 2]; | |
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; | |
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; | |
base64 += chars[bytes[i + 2] & 63]; | |
} | |
if ((len % 3) === 2) { | |
base64 = base64.substring(0, base64.length - 1) + "="; | |
} else if (len % 3 === 1) { | |
base64 = base64.substring(0, base64.length - 2) + "=="; | |
} | |
return base64; | |
}; | |
exports.decode = function(base64) { | |
var bufferLength = base64.length * 0.75, | |
len = base64.length, i, p = 0, | |
encoded1, encoded2, encoded3, encoded4; | |
if (base64[base64.length - 1] === "=") { | |
bufferLength--; | |
if (base64[base64.length - 2] === "=") { | |
bufferLength--; | |
} | |
} | |
var arraybuffer = new ArrayBuffer(bufferLength), | |
bytes = new Uint8Array(arraybuffer); | |
for (i = 0; i < len; i+=4) { | |
encoded1 = 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+/"); | |
},{}],26:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* Create a blob builder even when vendor prefixes exist | |
*/ | |
var BlobBuilder = global.BlobBuilder | |
|| global.WebKitBlobBuilder | |
|| global.MSBlobBuilder | |
|| global.MozBlobBuilder; | |
/** | |
* Check if Blob constructor is supported | |
*/ | |
var blobSupported = (function() { | |
try { | |
var 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; | |
function BlobBuilderConstructor(ary, options) { | |
options = options || {}; | |
var bb = new BlobBuilder(); | |
for (var i = 0; i < ary.length; i++) { | |
bb.append(ary[i]); | |
} | |
return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); | |
}; | |
module.exports = (function() { | |
if (blobSupported) { | |
return global.Blob; | |
} else if (blobBuilderSupported) { | |
return BlobBuilderConstructor; | |
} else { | |
return undefined; | |
} | |
})(); | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],27:[function(_dereq_,module,exports){ | |
(function (global){ | |
/*! http://mths.be/utf8js v2.0.0 by @mathias */ | |
;(function(root) { | |
// Detect free variables `exports` | |
var freeExports = typeof exports == 'object' && exports; | |
// Detect free variable `module` | |
var freeModule = typeof module == 'object' && module && | |
module.exports == freeExports && module; | |
// Detect free variable `global`, from Node.js or Browserified code, | |
// and use it as `root` | |
var freeGlobal = typeof global == 'object' && global; | |
if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { | |
root = freeGlobal; | |
} | |
/*--------------------------------------------------------------------------*/ | |
var stringFromCharCode = String.fromCharCode; | |
// Taken from http://mths.be/punycode | |
function ucs2decode(string) { | |
var output = []; | |
var counter = 0; | |
var length = string.length; | |
var value; | |
var extra; | |
while (counter < length) { | |
value = string.charCodeAt(counter++); | |
if (value >= 0xD800 && value <= 0xDBFF && counter < length) { | |
// high surrogate, and there is a next character | |
extra = string.charCodeAt(counter++); | |
if ((extra & 0xFC00) == 0xDC00) { // low surrogate | |
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); | |
} else { | |
// unmatched surrogate; only append this code unit, in case the next | |
// code unit is the high surrogate of a surrogate pair | |
output.push(value); | |
counter--; | |
} | |
} else { | |
output.push(value); | |
} | |
} | |
return output; | |
} | |
// Taken from http://mths.be/punycode | |
function ucs2encode(array) { | |
var length = array.length; | |
var index = -1; | |
var value; | |
var output = ''; | |
while (++index < length) { | |
value = array[index]; | |
if (value > 0xFFFF) { | |
value -= 0x10000; | |
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); | |
value = 0xDC00 | value & 0x3FF; | |
} | |
output += stringFromCharCode(value); | |
} | |
return output; | |
} | |
/*--------------------------------------------------------------------------*/ | |
function createByte(codePoint, shift) { | |
return stringFromCharCode(((codePoint >> shift) & 0x3F) | 0x80); | |
} | |
function encodeCodePoint(codePoint) { | |
if ((codePoint & 0xFFFFFF80) == 0) { // 1-byte sequence | |
return stringFromCharCode(codePoint); | |
} | |
var symbol = ''; | |
if ((codePoint & 0xFFFFF800) == 0) { // 2-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); | |
} | |
else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); | |
symbol += createByte(codePoint, 6); | |
} | |
else if ((codePoint & 0xFFE00000) == 0) { // 4-byte sequence | |
symbol = stringFromCharCode(((codePoint >> 18) & 0x07) | 0xF0); | |
symbol += createByte(codePoint, 12); | |
symbol += createByte(codePoint, 6); | |
} | |
symbol += stringFromCharCode((codePoint & 0x3F) | 0x80); | |
return symbol; | |
} | |
function utf8encode(string) { | |
var codePoints = ucs2decode(string); | |
// console.log(JSON.stringify(codePoints.map(function(x) { | |
// return 'U+' + x.toString(16).toUpperCase(); | |
// }))); | |
var length = codePoints.length; | |
var index = -1; | |
var codePoint; | |
var byteString = ''; | |
while (++index < length) { | |
codePoint = codePoints[index]; | |
byteString += encodeCodePoint(codePoint); | |
} | |
return byteString; | |
} | |
/*--------------------------------------------------------------------------*/ | |
function readContinuationByte() { | |
if (byteIndex >= byteCount) { | |
throw Error('Invalid byte index'); | |
} | |
var continuationByte = byteArray[byteIndex] & 0xFF; | |
byteIndex++; | |
if ((continuationByte & 0xC0) == 0x80) { | |
return continuationByte & 0x3F; | |
} | |
// If we end up here, it’s not a continuation byte | |
throw Error('Invalid continuation byte'); | |
} | |
function decodeSymbol() { | |
var byte1; | |
var byte2; | |
var byte3; | |
var byte4; | |
var codePoint; | |
if (byteIndex > byteCount) { | |
throw Error('Invalid byte index'); | |
} | |
if (byteIndex == byteCount) { | |
return false; | |
} | |
// Read first byte | |
byte1 = byteArray[byteIndex] & 0xFF; | |
byteIndex++; | |
// 1-byte sequence (no continuation bytes) | |
if ((byte1 & 0x80) == 0) { | |
return byte1; | |
} | |
// 2-byte sequence | |
if ((byte1 & 0xE0) == 0xC0) { | |
var byte2 = readContinuationByte(); | |
codePoint = ((byte1 & 0x1F) << 6) | byte2; | |
if (codePoint >= 0x80) { | |
return codePoint; | |
} else { | |
throw Error('Invalid continuation byte'); | |
} | |
} | |
// 3-byte sequence (may include unpaired surrogates) | |
if ((byte1 & 0xF0) == 0xE0) { | |
byte2 = readContinuationByte(); | |
byte3 = readContinuationByte(); | |
codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; | |
if (codePoint >= 0x0800) { | |
return codePoint; | |
} else { | |
throw Error('Invalid continuation byte'); | |
} | |
} | |
// 4-byte sequence | |
if ((byte1 & 0xF8) == 0xF0) { | |
byte2 = readContinuationByte(); | |
byte3 = readContinuationByte(); | |
byte4 = readContinuationByte(); | |
codePoint = ((byte1 & 0x0F) << 0x12) | (byte2 << 0x0C) | | |
(byte3 << 0x06) | byte4; | |
if (codePoint >= 0x010000 && codePoint <= 0x10FFFF) { | |
return codePoint; | |
} | |
} | |
throw Error('Invalid UTF-8 detected'); | |
} | |
var byteArray; | |
var byteCount; | |
var byteIndex; | |
function utf8decode(byteString) { | |
byteArray = ucs2decode(byteString); | |
byteCount = byteArray.length; | |
byteIndex = 0; | |
var codePoints = []; | |
var tmp; | |
while ((tmp = decodeSymbol()) !== false) { | |
codePoints.push(tmp); | |
} | |
return ucs2encode(codePoints); | |
} | |
/*--------------------------------------------------------------------------*/ | |
var utf8 = { | |
'version': '2.0.0', | |
'encode': utf8encode, | |
'decode': utf8decode | |
}; | |
// Some AMD build optimizers, like r.js, check for specific condition patterns | |
// like the following: | |
if ( | |
typeof define == 'function' && | |
typeof define.amd == 'object' && | |
define.amd | |
) { | |
define(function() { | |
return utf8; | |
}); | |
} else if (freeExports && !freeExports.nodeType) { | |
if (freeModule) { // in Node.js or RingoJS v0.8.0+ | |
freeModule.exports = utf8; | |
} else { // in Narwhal or RingoJS v0.7.0- | |
var object = {}; | |
var hasOwnProperty = object.hasOwnProperty; | |
for (var key in utf8) { | |
hasOwnProperty.call(utf8, key) && (freeExports[key] = utf8[key]); | |
} | |
} | |
} else { // in Rhino or a web browser | |
root.utf8 = utf8; | |
} | |
}(this)); | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],28:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* JSON parse. | |
* | |
* @see Based on jQuery#parseJSON (MIT) and JSON2 | |
* @api private | |
*/ | |
var rvalidchars = /^[\],:{}\s]*$/; | |
var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; | |
var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; | |
var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g; | |
var rtrimLeft = /^\s+/; | |
var rtrimRight = /\s+$/; | |
module.exports = function parsejson(data) { | |
if ('string' != typeof data || !data) { | |
return null; | |
} | |
data = data.replace(rtrimLeft, '').replace(rtrimRight, ''); | |
// Attempt to parse using the native JSON parser first | |
if (global.JSON && JSON.parse) { | |
return JSON.parse(data); | |
} | |
if (rvalidchars.test(data.replace(rvalidescape, '@') | |
.replace(rvalidtokens, ']') | |
.replace(rvalidbraces, ''))) { | |
return (new Function('return ' + data))(); | |
} | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],29:[function(_dereq_,module,exports){ | |
/** | |
* Compiles a querystring | |
* Returns string representation of the object | |
* | |
* @param {Object} | |
* @api private | |
*/ | |
exports.encode = function (obj) { | |
var str = ''; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
if (str.length) str += '&'; | |
str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]); | |
} | |
} | |
return str; | |
}; | |
/** | |
* Parses a simple querystring into an object | |
* | |
* @param {String} qs | |
* @api private | |
*/ | |
exports.decode = function(qs){ | |
var qry = {}; | |
var pairs = qs.split('&'); | |
for (var i = 0, l = pairs.length; i < l; i++) { | |
var pair = pairs[i].split('='); | |
qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); | |
} | |
return qry; | |
}; | |
},{}],30:[function(_dereq_,module,exports){ | |
/** | |
* Parses an URI | |
* | |
* @author Steven Levithan <stevenlevithan.com> (MIT license) | |
* @api private | |
*/ | |
var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; | |
var parts = [ | |
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' | |
]; | |
module.exports = function parseuri(str) { | |
var src = str, | |
b = str.indexOf('['), | |
e = str.indexOf(']'); | |
if (b != -1 && e != -1) { | |
str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); | |
} | |
var m = re.exec(str || ''), | |
uri = {}, | |
i = 14; | |
while (i--) { | |
uri[parts[i]] = m[i] || ''; | |
} | |
if (b != -1 && e != -1) { | |
uri.source = src; | |
uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); | |
uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); | |
uri.ipv6uri = true; | |
} | |
return uri; | |
}; | |
},{}],31:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var global = (function() { return this; })(); | |
/** | |
* WebSocket constructor. | |
*/ | |
var WebSocket = global.WebSocket || global.MozWebSocket; | |
/** | |
* Module exports. | |
*/ | |
module.exports = WebSocket ? ws : null; | |
/** | |
* WebSocket constructor. | |
* | |
* The third `opts` options object gets ignored in web browsers, since it's | |
* non-standard, and throws a TypeError if passed to the constructor. | |
* See: https://github.com/einaros/ws/issues/227 | |
* | |
* @param {String} uri | |
* @param {Array} protocols (optional) | |
* @param {Object) opts (optional) | |
* @api public | |
*/ | |
function ws(uri, protocols, opts) { | |
var instance; | |
if (protocols) { | |
instance = new WebSocket(uri, protocols); | |
} else { | |
instance = new WebSocket(uri); | |
} | |
return instance; | |
} | |
if (WebSocket) ws.prototype = WebSocket.prototype; | |
},{}],32:[function(_dereq_,module,exports){ | |
(function (global){ | |
/* | |
* Module requirements. | |
*/ | |
var isArray = _dereq_('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 _hasBinary(obj) { | |
if (!obj) return false; | |
if ( (global.Buffer && global.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 (_hasBinary(obj[i])) { | |
return true; | |
} | |
} | |
} else if (obj && 'object' == typeof obj) { | |
if (obj.toJSON) { | |
obj = obj.toJSON(); | |
} | |
for (var key in obj) { | |
if (obj.hasOwnProperty(key) && _hasBinary(obj[key])) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
return _hasBinary(data); | |
} | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"isarray":33}],33:[function(_dereq_,module,exports){ | |
module.exports = Array.isArray || function (arr) { | |
return Object.prototype.toString.call(arr) == '[object Array]'; | |
}; | |
},{}],34:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var global = _dereq_('global'); | |
/** | |
* Module exports. | |
* | |
* Logic borrowed from Modernizr: | |
* | |
* - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js | |
*/ | |
try { | |
module.exports = 'XMLHttpRequest' in global && | |
'withCredentials' in new global.XMLHttpRequest(); | |
} catch (err) { | |
// if XMLHttp support is disabled in IE then it will throw | |
// when trying to create | |
module.exports = false; | |
} | |
},{"global":35}],35:[function(_dereq_,module,exports){ | |
/** | |
* Returns `this`. Execute this without a "context" (i.e. without it being | |
* attached to an object of the left-hand side), and `this` points to the | |
* "global" scope of the current JS execution. | |
*/ | |
module.exports = (function () { return this; })(); | |
},{}],36:[function(_dereq_,module,exports){ | |
var indexOf = [].indexOf; | |
module.exports = function(arr, obj){ | |
if (indexOf) return arr.indexOf(obj); | |
for (var i = 0; i < arr.length; ++i) { | |
if (arr[i] === obj) return i; | |
} | |
return -1; | |
}; | |
},{}],37:[function(_dereq_,module,exports){ | |
/** | |
* HOP ref. | |
*/ | |
var has = Object.prototype.hasOwnProperty; | |
/** | |
* Return own keys in `obj`. | |
* | |
* @param {Object} obj | |
* @return {Array} | |
* @api public | |
*/ | |
exports.keys = Object.keys || function(obj){ | |
var keys = []; | |
for (var key in obj) { | |
if (has.call(obj, key)) { | |
keys.push(key); | |
} | |
} | |
return keys; | |
}; | |
/** | |
* Return own values in `obj`. | |
* | |
* @param {Object} obj | |
* @return {Array} | |
* @api public | |
*/ | |
exports.values = function(obj){ | |
var vals = []; | |
for (var key in obj) { | |
if (has.call(obj, key)) { | |
vals.push(obj[key]); | |
} | |
} | |
return vals; | |
}; | |
/** | |
* Merge `b` into `a`. | |
* | |
* @param {Object} a | |
* @param {Object} b | |
* @return {Object} a | |
* @api public | |
*/ | |
exports.merge = function(a, b){ | |
for (var key in b) { | |
if (has.call(b, key)) { | |
a[key] = b[key]; | |
} | |
} | |
return a; | |
}; | |
/** | |
* Return length of `obj`. | |
* | |
* @param {Object} obj | |
* @return {Number} | |
* @api public | |
*/ | |
exports.length = function(obj){ | |
return exports.keys(obj).length; | |
}; | |
/** | |
* Check if `obj` is empty. | |
* | |
* @param {Object} obj | |
* @return {Boolean} | |
* @api public | |
*/ | |
exports.isEmpty = function(obj){ | |
return 0 == exports.length(obj); | |
}; | |
},{}],38:[function(_dereq_,module,exports){ | |
/** | |
* Parses an URI | |
* | |
* @author Steven Levithan <stevenlevithan.com> (MIT license) | |
* @api private | |
*/ | |
var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; | |
var parts = [ | |
'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host' | |
, 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' | |
]; | |
module.exports = function parseuri(str) { | |
var m = re.exec(str || '') | |
, uri = {} | |
, i = 14; | |
while (i--) { | |
uri[parts[i]] = m[i] || ''; | |
} | |
return uri; | |
}; | |
},{}],39:[function(_dereq_,module,exports){ | |
(function (global){ | |
/*global Blob,File*/ | |
/** | |
* Module requirements | |
*/ | |
var isArray = _dereq_('isarray'); | |
var isBuf = _dereq_('./is-buffer'); | |
/** | |
* Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. | |
* Anything with blobs or files should be fed through removeBlobs before coming | |
* here. | |
* | |
* @param {Object} packet - socket.io event packet | |
* @return {Object} with deconstructed packet and list of buffers | |
* @api public | |
*/ | |
exports.deconstructPacket = function(packet){ | |
var buffers = []; | |
var packetData = packet.data; | |
function _deconstructPacket(data) { | |
if (!data) return data; | |
if (isBuf(data)) { | |
var placeholder = { _placeholder: true, num: buffers.length }; | |
buffers.push(data); | |
return placeholder; | |
} else if (isArray(data)) { | |
var newData = new Array(data.length); | |
for (var i = 0; i < data.length; i++) { | |
newData[i] = _deconstructPacket(data[i]); | |
} | |
return newData; | |
} else if ('object' == typeof data && !(data instanceof Date)) { | |
var newData = {}; | |
for (var key in data) { | |
newData[key] = _deconstructPacket(data[key]); | |
} | |
return newData; | |
} | |
return data; | |
} | |
var pack = packet; | |
pack.data = _deconstructPacket(packetData); | |
pack.attachments = buffers.length; // number of binary 'attachments' | |
return {packet: pack, buffers: buffers}; | |
}; | |
/** | |
* Reconstructs a binary packet from its placeholder packet and buffers | |
* | |
* @param {Object} packet - event packet with placeholders | |
* @param {Array} buffers - binary buffers to put in placeholder positions | |
* @return {Object} reconstructed packet | |
* @api public | |
*/ | |
exports.reconstructPacket = function(packet, buffers) { | |
var curPlaceHolder = 0; | |
function _reconstructPacket(data) { | |
if (data && data._placeholder) { | |
var buf = buffers[data.num]; // appropriate buffer (should be natural order anyway) | |
return buf; | |
} else if (isArray(data)) { | |
for (var i = 0; i < data.length; i++) { | |
data[i] = _reconstructPacket(data[i]); | |
} | |
return data; | |
} else if (data && 'object' == typeof data) { | |
for (var key in data) { | |
data[key] = _reconstructPacket(data[key]); | |
} | |
return data; | |
} | |
return data; | |
} | |
packet.data = _reconstructPacket(packet.data); | |
packet.attachments = undefined; // no longer useful | |
return packet; | |
}; | |
/** | |
* Asynchronously removes Blobs or Files from data via | |
* FileReader's readAsArrayBuffer method. Used before encoding | |
* data as msgpack. Calls callback with the blobless data. | |
* | |
* @param {Object} data | |
* @param {Function} callback | |
* @api private | |
*/ | |
exports.removeBlobs = function(data, callback) { | |
function _removeBlobs(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(bloblessData); | |
} | |
}; | |
fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer | |
} else if (isArray(obj)) { // handle array | |
for (var i = 0; i < obj.length; i++) { | |
_removeBlobs(obj[i], i, obj); | |
} | |
} else if (obj && 'object' == typeof obj && !isBuf(obj)) { // and object | |
for (var key in obj) { | |
_removeBlobs(obj[key], key, obj); | |
} | |
} | |
} | |
var pendingBlobs = 0; | |
var bloblessData = data; | |
_removeBlobs(bloblessData); | |
if (!pendingBlobs) { | |
callback(bloblessData); | |
} | |
}; | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./is-buffer":41,"isarray":42}],40:[function(_dereq_,module,exports){ | |
/** | |
* Module dependencies. | |
*/ | |
var debug = _dereq_('debug')('socket.io-parser'); | |
var json = _dereq_('json3'); | |
var isArray = _dereq_('isarray'); | |
var Emitter = _dereq_('component-emitter'); | |
var binary = _dereq_('./binary'); | |
var isBuf = _dereq_('./is-buffer'); | |
/** | |
* Protocol version. | |
* | |
* @api public | |
*/ | |
exports.protocol = 4; | |
/** | |
* Packet types. | |
* | |
* @api public | |
*/ | |
exports.types = [ | |
'CONNECT', | |
'DISCONNECT', | |
'EVENT', | |
'BINARY_EVENT', | |
'ACK', | |
'BINARY_ACK', | |
'ERROR' | |
]; | |
/** | |
* Packet type `connect`. | |
* | |
* @api public | |
*/ | |
exports.CONNECT = 0; | |
/** | |
* Packet type `disconnect`. | |
* | |
* @api public | |
*/ | |
exports.DISCONNECT = 1; | |
/** | |
* Packet type `event`. | |
* | |
* @api public | |
*/ | |
exports.EVENT = 2; | |
/** | |
* Packet type `ack`. | |
* | |
* @api public | |
*/ | |
exports.ACK = 3; | |
/** | |
* Packet type `error`. | |
* | |
* @api public | |
*/ | |
exports.ERROR = 4; | |
/** | |
* Packet type 'binary event' | |
* | |
* @api public | |
*/ | |
exports.BINARY_EVENT = 5; | |
/** | |
* Packet type `binary ack`. For acks with binary arguments. | |
* | |
* @api public | |
*/ | |
exports.BINARY_ACK = 6; | |
/** | |
* Encoder constructor. | |
* | |
* @api public | |
*/ | |
exports.Encoder = Encoder; | |
/** | |
* Decoder constructor. | |
* | |
* @api public | |
*/ | |
exports.Decoder = Decoder; | |
/** | |
* A socket.io Encoder instance | |
* | |
* @api public | |
*/ | |
function Encoder() {} | |
/** | |
* Encode a packet as a single string if non-binary, or as a | |
* buffer sequence, depending on packet type. | |
* | |
* @param {Object} obj - packet object | |
* @param {Function} callback - function to handle encodings (likely engine.write) | |
* @return Calls callback with Array of encodings | |
* @api public | |
*/ | |
Encoder.prototype.encode = function(obj, callback){ | |
debug('encoding packet %j', obj); | |
if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) { | |
encodeAsBinary(obj, callback); | |
} | |
else { | |
var encoding = encodeAsString(obj); | |
callback([encoding]); | |
} | |
}; | |
/** | |
* Encode packet as string. | |
* | |
* @param {Object} packet | |
* @return {String} encoded | |
* @api private | |
*/ | |
function encodeAsString(obj) { | |
var str = ''; | |
var nsp = false; | |
// first is type | |
str += obj.type; | |
// attachments if we have them | |
if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) { | |
str += obj.attachments; | |
str += '-'; | |
} | |
// if we have a namespace other than `/` | |
// we append it followed by a comma `,` | |
if (obj.nsp && '/' != obj.nsp) { | |
nsp = true; | |
str += obj.nsp; | |
} | |
// immediately followed by the id | |
if (null != obj.id) { | |
if (nsp) { | |
str += ','; | |
nsp = false; | |
} | |
str += obj.id; | |
} | |
// json data | |
if (null != obj.data) { | |
if (nsp) str += ','; | |
str += json.stringify(obj.data); | |
} | |
debug('encoded %j as %s', obj, str); | |
return str; | |
} | |
/** | |
* Encode packet as 'buffer sequence' by removing blobs, and | |
* deconstructing packet into object with placeholders and | |
* a list of buffers. | |
* | |
* @param {Object} packet | |
* @return {Buffer} encoded | |
* @api private | |
*/ | |
function encodeAsBinary(obj, callback) { | |
function writeEncoding(bloblessData) { | |
var deconstruction = binary.deconstructPacket(bloblessData); | |
var pack = encodeAsString(deconstruction.packet); | |
var buffers = deconstruction.buffers; | |
buffers.unshift(pack); // add packet info to beginning of data list | |
callback(buffers); // write all the buffers | |
} | |
binary.removeBlobs(obj, writeEncoding); | |
} | |
/** | |
* A socket.io Decoder instance | |
* | |
* @return {Object} decoder | |
* @api public | |
*/ | |
function Decoder() { | |
this.reconstructor = null; | |
} | |
/** | |
* Mix in `Emitter` with Decoder. | |
*/ | |
Emitter(Decoder.prototype); | |
/** | |
* Decodes an ecoded packet string into packet JSON. | |
* | |
* @param {String} obj - encoded packet | |
* @return {Object} packet | |
* @api public | |
*/ | |
Decoder.prototype.add = function(obj) { | |
var packet; | |
if ('string' == typeof obj) { | |
packet = decodeString(obj); | |
if (exports.BINARY_EVENT == packet.type || exports.BINARY_ACK == packet.type) { // binary packet's json | |
this.reconstructor = new BinaryReconstructor(packet); | |
// no attachments, labeled binary but no binary data to follow | |
if (this.reconstructor.reconPack.attachments == 0) { | |
this.emit('decoded', packet); | |
} | |
} else { // non-binary full packet | |
this.emit('decoded', packet); | |
} | |
} | |
else if (isBuf(obj) || obj.base64) { // raw binary data | |
if (!this.reconstructor) { | |
throw new Error('got binary data when not reconstructing a packet'); | |
} else { | |
packet = this.reconstructor.takeBinaryData(obj); | |
if (packet) { // received final buffer | |
this.reconstructor = null; | |
this.emit('decoded', packet); | |
} | |
} | |
} | |
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; | |
// look up type | |
p.type = Number(str.charAt(0)); | |
if (null == exports.types[p.type]) return error(); | |
// look up attachments if type binary | |
if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) { | |
p.attachments = ''; | |
while (str.charAt(++i) != '-') { | |
p.attachments += str.charAt(i); | |
} | |
p.attachments = Number(p.attachments); | |
} | |
// look up namespace (if any) | |
if ('/' == str.charAt(i + 1)) { | |
p.nsp = ''; | |
while (++i) { | |
var c = str.charAt(i); | |
if (',' == c) break; | |
p.nsp += c; | |
if (i + 1 == str.length) break; | |
} | |
} else { | |
p.nsp = '/'; | |
} | |
// look up id | |
var next = str.charAt(i + 1); | |
if ('' != next && Number(next) == next) { | |
p.id = ''; | |
while (++i) { | |
var c = str.charAt(i); | |
if (null == c || Number(c) != c) { | |
--i; | |
break; | |
} | |
p.id += str.charAt(i); | |
if (i + 1 == str.length) break; | |
} | |
p.id = Number(p.id); | |
} | |
// look up json data | |
if (str.charAt(++i)) { | |
try { | |
p.data = json.parse(str.substr(i)); | |
} catch(e){ | |
return error(); | |
} | |
} | |
debug('decoded %s as %j', str, p); | |
return p; | |
} | |
/** | |
* Deallocates a parser's resources | |
* | |
* @api public | |
*/ | |
Decoder.prototype.destroy = function() { | |
if (this.reconstructor) { | |
this.reconstructor.finishedReconstruction(); | |
} | |
}; | |
/** | |
* A manager of a binary event's 'buffer sequence'. Should | |
* be constructed whenever a packet of type BINARY_EVENT is | |
* decoded. | |
* | |
* @param {Object} packet | |
* @return {BinaryReconstructor} initialized reconstructor | |
* @api private | |
*/ | |
function BinaryReconstructor(packet) { | |
this.reconPack = packet; | |
this.buffers = []; | |
} | |
/** | |
* Method to be called when binary data received from connection | |
* after a BINARY_EVENT packet. | |
* | |
* @param {Buffer | ArrayBuffer} binData - the raw binary data received | |
* @return {null | Object} returns null if more binary data is expected or | |
* a reconstructed packet object if all buffers have been received. | |
* @api private | |
*/ | |
BinaryReconstructor.prototype.takeBinaryData = function(binData) { | |
this.buffers.push(binData); | |
if (this.buffers.length == this.reconPack.attachments) { // done with buffer list | |
var packet = binary.reconstructPacket(this.reconPack, this.buffers); | |
this.finishedReconstruction(); | |
return packet; | |
} | |
return null; | |
}; | |
/** | |
* Cleans up binary packet reconstruction variables. | |
* | |
* @api private | |
*/ | |
BinaryReconstructor.prototype.finishedReconstruction = function() { | |
this.reconPack = null; | |
this.buffers = []; | |
}; | |
function error(data){ | |
return { | |
type: exports.ERROR, | |
data: 'parser error' | |
}; | |
} | |
},{"./binary":39,"./is-buffer":41,"component-emitter":8,"debug":9,"isarray":42,"json3":43}],41:[function(_dereq_,module,exports){ | |
(function (global){ | |
module.exports = isBuf; | |
/** | |
* Returns true if obj is a buffer or an arraybuffer. | |
* | |
* @api private | |
*/ | |
function isBuf(obj) { | |
return (global.Buffer && global.Buffer.isBuffer(obj)) || | |
(global.ArrayBuffer && obj instanceof ArrayBuffer); | |
} | |
}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],42:[function(_dereq_,module,exports){ | |
module.exports=_dereq_(33) | |
},{}],43:[function(_dereq_,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; | |
// 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 = 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 { | |
// 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; | |
} | |
} | |
isSupported = parseSupported; | |
} | |
} | |
return has[name] = !!isSupported; | |
} | |
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 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: 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); | |
} | |
} | |
}; | |
} 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 the `constructor` property due to | |
// cross-environment inconsistencies. | |
if (isConstructor || isProperty.call(object, (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" | |
}; | |
// 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 + '"'; | |
}; | |
// 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"; | |
} 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); | |
} | |
// 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); | |
} | |
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; | |
} | |
}; | |
// 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); | |
} | |
} | |
// 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" | |
}; | |
// 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: 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. | |
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. | |
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; | |
} | |
// 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 "$"; | |
}; | |
// 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); | |
} | |
// 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. | |
abort(); | |
} | |
} | |
// 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(); | |
} | |
} else { | |
// A `,` must separate each object member. | |
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() != ":") { | |
abort(); | |
} | |
results[value.slice(1)] = get(lex()); | |
} | |
return results; | |
} | |
// 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: 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); | |
}; | |
// 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; | |
}; | |
} | |
} | |
// Export for asynchronous module loaders. | |
if (isLoader) { | |
define(function () { | |
return JSON3; | |
}); | |
} | |
}(this)); | |
},{}],44:[function(_dereq_,module,exports){ | |
module.exports = toArray | |
function toArray(list, index) { | |
var array = [] | |
index = index || 0 | |
for (var i = index || 0; i < list.length; i++) { | |
array[i - index] = list[i] | |
} | |
return array | |
} | |
},{}]},{},[1]) | |
(1) | |
}); |
diff --git a/0b9bdaf16f82daefc787a36c41a35277b7a29f84:./socket.io.js b/4c05eb5785cdbf1dc78a71a6fb388122cec4ce41:./socket.io.js | |
index 9248e28..6f0ac98 100644 | |
--- a/0b9bdaf16f82daefc787a36c41a35277b7a29f84:./socket.io.js | |
+++ b/4c05eb5785cdbf1dc78a71a6fb388122cec4ce41:./socket.io.js | |
@@ -3153,7 +3153,7 @@ var debug = _dereq_('debug')('engine.io-client:websocket'); | |
* in the browser. | |
*/ | |
-var WebSocket = _dereq_('ws'); | |
+var WebSocket = require('net.iamyellow.tiws'); | |
/** | |
* Module exports. | |
@@ -3203,6 +3203,7 @@ WS.prototype.supportsBinary = true; | |
*/ | |
WS.prototype.doOpen = function(){ | |
+ // TODO: Figure out and fix. This causes initial connection to time out on iOS. | |
if (!this.check()) { | |
// let probe timeout | |
return; | |
@@ -3213,7 +3214,7 @@ WS.prototype.doOpen = function(){ | |
var protocols = void(0); | |
var opts = { agent: this.agent }; | |
- this.ws = new WebSocket(uri, protocols, opts); | |
+ this.ws = WebSocket.createWS(); | |
if (this.ws.binaryType === undefined) { | |
this.supportsBinary = false; | |
@@ -3221,6 +3222,7 @@ WS.prototype.doOpen = function(){ | |
this.ws.binaryType = 'arraybuffer'; | |
this.addEventListeners(); | |
+ this.ws.open(uri); | |
}; | |
/** | |
@@ -3232,18 +3234,18 @@ WS.prototype.doOpen = function(){ | |
WS.prototype.addEventListeners = function(){ | |
var self = this; | |
- this.ws.onopen = function(){ | |
+ this.ws.addEventListener('open', function () { | |
self.onOpen(); | |
- }; | |
- this.ws.onclose = function(){ | |
+ }); | |
+ this.ws.addEventListener('close', function (ev) { | |
self.onClose(); | |
- }; | |
- this.ws.onmessage = function(ev){ | |
+ }); | |
+ this.ws.addEventListener('message', function (ev) { | |
self.onData(ev.data); | |
- }; | |
- this.ws.onerror = function(e){ | |
+ }); | |
+ this.ws.addEventListener('error', function (e) { | |
self.onError('websocket error', e); | |
- }; | |
+ }); | |
}; | |
/** | |
@@ -3432,7 +3434,7 @@ var utf8 = _dereq_('utf8'); | |
* http://ghinda.net/jpeg-blob-ajax-android/ | |
*/ | |
-var isAndroid = navigator.userAgent.match(/Android/i); | |
+var isAndroid = false; // navigator.userAgent.match(/Android/i); | |
/** | |
* Current protocol version. |
Great and thanks for the info!
Celso, can you provide an example of connecting to sockei.io on server ? Thanks
Thanks for updating
@DouglasHennrich using this example you can connect mobile app to server using web socket!
var socketIO = require('socket.io'); //Socket io library or path to socket io lib
var socket = socketIO.connect('http://yourdomain.com', {
transports : ['websocket'],
timeout : 20000,
reconnection : true
});
socket.io.on('connect_error', function(e) {
console.info('Socket Manager - Connect error');
});
socket.io.on('reconnect_failed', function() {
console.info('Socket Manager - Reconnect failed');
});
socket.on('connect', function() {
console.info('Socket - Connect event received from server');
});
socket.on('disconnect', function() {
console.info('Socket - Disconnection event received');
});
socket.on('error', function() {
console.info('Socket - Error while connection');
});
For implement SocketIO into your server use official SocketIO docs: http://socket.io/docs/server-api/
enjoy!
How can I use this with Titanium?
For support iOS change the function for check if can use websocket, because only web socket is supported!
WS.prototype.check = function(){
return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name);
};
WS.prototype.check = function(){
return true
};