vendor/assets/javascripts/faye-browser.js in faye-rails-2.0.0 vs vendor/assets/javascripts/faye-browser.js in faye-rails-2.0.1
- old
+ new
@@ -1,11 +1,11 @@
(function() {
(function() {
'use strict';
var Faye = {
- VERSION: '1.0.1',
+ VERSION: '1.1.1',
BAYEUX_VERSION: '1.0',
ID_LENGTH: 160,
JSONP_CALLBACK: 'jsonpcallback',
CONNECTION_TYPES: ['long-polling', 'cross-origin-long-polling', 'callback-polling', 'websocket', 'eventsource', 'in-process'],
@@ -25,13 +25,23 @@
return dest;
},
random: function(bitlength) {
bitlength = bitlength || this.ID_LENGTH;
- return csprng(bitlength, 36);
+ var maxLength = Math.ceil(bitlength * Math.log(2) / Math.log(36));
+ var string = csprng(bitlength, 36);
+ while (string.length < maxLength) string = '0' + string;
+ return string;
},
+ validateOptions: function(options, validKeys) {
+ for (var key in options) {
+ if (this.indexOf(validKeys, key) < 0)
+ throw new Error('Unrecognized option: ' + key);
+ }
+ },
+
clientIdFromMessages: function(messages) {
var connect = this.filter([].concat(messages), function(message) {
return message.channel === '/meta/connect';
});
return connect[0] && connect[0].clientId;
@@ -356,13 +366,12 @@
});
(function() {
'use strict';
-var timeout = setTimeout;
+var timeout = setTimeout, defer;
-var defer;
if (typeof setImmediate === 'function')
defer = function(fn) { setImmediate(fn) };
else if (typeof process === 'object' && process.nextTick)
defer = function(fn) { process.nextTick(fn) };
else
@@ -371,113 +380,134 @@
var PENDING = 0,
FULFILLED = 1,
REJECTED = 2;
var RETURN = function(x) { return x },
- THROW = function(x) { throw x };
+ THROW = function(x) { throw x };
var Promise = function(task) {
- this._state = PENDING;
- this._callbacks = [];
- this._errbacks = [];
+ this._state = PENDING;
+ this._onFulfilled = [];
+ this._onRejected = [];
if (typeof task !== 'function') return;
var self = this;
task(function(value) { fulfill(self, value) },
function(reason) { reject(self, reason) });
};
-Promise.prototype.then = function(callback, errback) {
- var next = {}, self = this;
-
- next.promise = new Promise(function(fulfill, reject) {
- next.fulfill = fulfill;
- next.reject = reject;
-
- registerCallback(self, callback, next);
- registerErrback(self, errback, next);
- });
- return next.promise;
+Promise.prototype.then = function(onFulfilled, onRejected) {
+ var next = new Promise();
+ registerOnFulfilled(this, onFulfilled, next);
+ registerOnRejected(this, onRejected, next);
+ return next;
};
-var registerCallback = function(promise, callback, next) {
- if (typeof callback !== 'function') callback = RETURN;
- var handler = function(value) { invoke(callback, value, next) };
+var registerOnFulfilled = function(promise, onFulfilled, next) {
+ if (typeof onFulfilled !== 'function') onFulfilled = RETURN;
+ var handler = function(value) { invoke(onFulfilled, value, next) };
+
if (promise._state === PENDING) {
- promise._callbacks.push(handler);
+ promise._onFulfilled.push(handler);
} else if (promise._state === FULFILLED) {
handler(promise._value);
}
};
-var registerErrback = function(promise, errback, next) {
- if (typeof errback !== 'function') errback = THROW;
- var handler = function(reason) { invoke(errback, reason, next) };
+var registerOnRejected = function(promise, onRejected, next) {
+ if (typeof onRejected !== 'function') onRejected = THROW;
+ var handler = function(reason) { invoke(onRejected, reason, next) };
+
if (promise._state === PENDING) {
- promise._errbacks.push(handler);
+ promise._onRejected.push(handler);
} else if (promise._state === REJECTED) {
handler(promise._reason);
}
};
var invoke = function(fn, value, next) {
defer(function() { _invoke(fn, value, next) });
};
var _invoke = function(fn, value, next) {
- var called = false, outcome, type, then;
+ var outcome;
try {
outcome = fn(value);
- type = typeof outcome;
- then = outcome !== null && (type === 'function' || type === 'object') && outcome.then;
+ } catch (error) {
+ return reject(next, error);
+ }
- if (outcome === next.promise)
- return next.reject(new TypeError('Recursive promise chain detected'));
+ if (outcome === next) {
+ reject(next, new TypeError('Recursive promise chain detected'));
+ } else {
+ fulfill(next, outcome);
+ }
+};
- if (typeof then !== 'function') return next.fulfill(outcome);
+var fulfill = Promise.fulfill = Promise.resolve = function(promise, value) {
+ var called = false, type, then;
- then.call(outcome, function(v) {
- if (called) return;
- called = true;
- _invoke(RETURN, v, next);
+ try {
+ type = typeof value;
+ then = value !== null && (type === 'function' || type === 'object') && value.then;
+
+ if (typeof then !== 'function') return _fulfill(promise, value);
+
+ then.call(value, function(v) {
+ if (!(called ^ (called = true))) return;
+ fulfill(promise, v);
}, function(r) {
- if (called) return;
- called = true;
- next.reject(r);
+ if (!(called ^ (called = true))) return;
+ reject(promise, r);
});
-
} catch (error) {
- if (called) return;
- called = true;
- next.reject(error);
+ if (!(called ^ (called = true))) return;
+ reject(promise, error);
}
};
-var fulfill = Promise.fulfill = Promise.resolve = function(promise, value) {
+var _fulfill = function(promise, value) {
if (promise._state !== PENDING) return;
- promise._state = FULFILLED;
- promise._value = value;
- promise._errbacks = [];
+ promise._state = FULFILLED;
+ promise._value = value;
+ promise._onRejected = [];
- var callbacks = promise._callbacks, cb;
- while (cb = callbacks.shift()) cb(value);
+ var onFulfilled = promise._onFulfilled, fn;
+ while (fn = onFulfilled.shift()) fn(value);
};
var reject = Promise.reject = function(promise, reason) {
if (promise._state !== PENDING) return;
- promise._state = REJECTED;
- promise._reason = reason;
- promise._callbacks = [];
+ promise._state = REJECTED;
+ promise._reason = reason;
+ promise._onFulfilled = [];
- var errbacks = promise._errbacks, eb;
- while (eb = errbacks.shift()) eb(reason);
+ var onRejected = promise._onRejected, fn;
+ while (fn = onRejected.shift()) fn(reason);
};
+Promise.all = function(promises) {
+ return new Promise(function(fulfill, reject) {
+ var list = [],
+ n = promises.length,
+ i;
+
+ if (n === 0) return fulfill(list);
+
+ for (i = 0; i < n; i++) (function(promise, i) {
+ Promise.fulfilled(promise).then(function(value) {
+ list[i] = value;
+ if (--n === 0) fulfill(list);
+ }, reject);
+ })(promises[i], i);
+ });
+};
+
Promise.defer = defer;
Promise.deferred = Promise.pending = function() {
var tuple = {};
@@ -743,11 +773,11 @@
},
setDeferredStatus: function(status, value) {
if (this._timer) Faye.ENV.clearTimeout(this._timer);
- var promise = this.then();
+ this.then();
if (status === 'succeeded')
this._fulfill(value);
else if (status === 'failed')
this._reject(value);
@@ -800,11 +830,11 @@
removeTimeout: function(name) {
this._timeouts = this._timeouts || {};
var timeout = this._timeouts[name];
if (!timeout) return;
- clearTimeout(timeout);
+ Faye.ENV.clearTimeout(timeout);
delete this._timeouts[name];
},
removeAllTimeouts: function() {
this._timeouts = this._timeouts || {};
@@ -822,17 +852,17 @@
},
writeLog: function(messageArgs, level) {
if (!Faye.logger) return;
- var messageArgs = Array.prototype.slice.apply(messageArgs),
- banner = '[Faye',
- klass = this.className,
+ var args = Array.prototype.slice.apply(messageArgs),
+ banner = '[Faye',
+ klass = this.className,
- message = messageArgs.shift().replace(/\?/g, function() {
+ message = args.shift().replace(/\?/g, function() {
try {
- return Faye.toJSON(messageArgs.shift());
+ return Faye.toJSON(args.shift());
} catch (e) {
return '[Object]';
}
});
@@ -851,15 +881,15 @@
}
};
(function() {
for (var key in Faye.Logging.LOG_LEVELS)
- (function(level, value) {
+ (function(level) {
Faye.Logging[level] = function() {
this.writeLog(arguments, level);
};
- })(key, Faye.Logging.LOG_LEVELS[key]);
+ })(key);
})();
Faye.Grammar = {
CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,
@@ -998,16 +1028,15 @@
hasSubscription: function(name) {
return this._channels.hasOwnProperty(name);
},
subscribe: function(names, callback, context) {
- if (!callback) return;
var name;
for (var i = 0, n = names.length; i < n; i++) {
name = names[i];
var channel = this._channels[name] = this._channels[name] || new Faye.Channel(name);
- channel.bind('message', callback, context);
+ if (callback) channel.bind('message', callback, context);
}
},
unsubscribe: function(name, callback, context) {
var channel = this._channels[name];
@@ -1031,21 +1060,10 @@
}
}
})
});
-Faye.Envelope = Faye.Class({
- initialize: function(message, timeout) {
- this.id = message.id;
- this.message = message;
-
- if (timeout !== undefined) this.timeout(timeout / 1000, false);
- }
-});
-
-Faye.extend(Faye.Envelope.prototype, Faye.Deferrable);
-
Faye.Publication = Faye.Class(Faye.Deferrable);
Faye.Subscription = Faye.Class({
initialize: function(client, channels, callback, context) {
this._client = client;
@@ -1067,69 +1085,65 @@
});
Faye.extend(Faye.Subscription.prototype, Faye.Deferrable);
Faye.Client = Faye.Class({
- UNCONNECTED: 1,
- CONNECTING: 2,
- CONNECTED: 3,
- DISCONNECTED: 4,
+ UNCONNECTED: 1,
+ CONNECTING: 2,
+ CONNECTED: 3,
+ DISCONNECTED: 4,
- HANDSHAKE: 'handshake',
- RETRY: 'retry',
- NONE: 'none',
+ HANDSHAKE: 'handshake',
+ RETRY: 'retry',
+ NONE: 'none',
- CONNECTION_TIMEOUT: 60,
- DEFAULT_RETRY: 5,
- MAX_REQUEST_SIZE: 2048,
+ CONNECTION_TIMEOUT: 60,
- DEFAULT_ENDPOINT: '/bayeux',
- INTERVAL: 0,
+ DEFAULT_ENDPOINT: '/bayeux',
+ INTERVAL: 0,
initialize: function(endpoint, options) {
this.info('New client created for ?', endpoint);
+ options = options || {};
- this._options = options || {};
- this.endpoint = Faye.URI.parse(endpoint || this.DEFAULT_ENDPOINT);
- this.endpoints = this._options.endpoints || {};
- this.transports = {};
- this.cookies = Faye.CookieJar && new Faye.CookieJar();
- this.headers = {};
- this.ca = this._options.ca;
- this._disabled = [];
- this._retry = this._options.retry || this.DEFAULT_RETRY;
+ Faye.validateOptions(options, ['interval', 'timeout', 'endpoints', 'proxy', 'retry', 'scheduler', 'websocketExtensions', 'tls', 'ca']);
- for (var key in this.endpoints)
- this.endpoints[key] = Faye.URI.parse(this.endpoints[key]);
+ this._endpoint = endpoint || this.DEFAULT_ENDPOINT;
+ this._channels = new Faye.Channel.Set();
+ this._dispatcher = new Faye.Dispatcher(this, this._endpoint, options);
- this.maxRequestSize = this.MAX_REQUEST_SIZE;
-
- this._state = this.UNCONNECTED;
- this._channels = new Faye.Channel.Set();
this._messageId = 0;
+ this._state = this.UNCONNECTED;
this._responseCallbacks = {};
this._advice = {
reconnect: this.RETRY,
- interval: 1000 * (this._options.interval || this.INTERVAL),
- timeout: 1000 * (this._options.timeout || this.CONNECTION_TIMEOUT)
+ interval: 1000 * (options.interval || this.INTERVAL),
+ timeout: 1000 * (options.timeout || this.CONNECTION_TIMEOUT)
};
+ this._dispatcher.timeout = this._advice.timeout / 1000;
+ this._dispatcher.bind('message', this._receiveMessage, this);
+
if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
Faye.Event.on(Faye.ENV, 'beforeunload', function() {
- if (Faye.indexOf(this._disabled, 'autodisconnect') < 0)
+ if (Faye.indexOf(this._dispatcher._disabled, 'autodisconnect') < 0)
this.disconnect();
}, this);
},
+ addWebsocketExtension: function(extension) {
+ return this._dispatcher.addWebsocketExtension(extension);
+ },
+
disable: function(feature) {
- this._disabled.push(feature);
+ return this._dispatcher.disable(feature);
},
setHeader: function(name, value) {
- this.headers[name] = value;
+ return this._dispatcher.setHeader(name, value);
},
// Request
// MUST include: * channel
// * version
@@ -1154,34 +1168,34 @@
if (this._state !== this.UNCONNECTED) return;
this._state = this.CONNECTING;
var self = this;
- this.info('Initiating handshake with ?', Faye.URI.stringify(this.endpoint));
- this._selectTransport(Faye.MANDATORY_CONNECTION_TYPES);
+ this.info('Initiating handshake with ?', Faye.URI.stringify(this._endpoint));
+ this._dispatcher.selectTransport(Faye.MANDATORY_CONNECTION_TYPES);
- this._send({
+ this._sendMessage({
channel: Faye.Channel.HANDSHAKE,
version: Faye.BAYEUX_VERSION,
- supportedConnectionTypes: [this._transport.connectionType]
+ supportedConnectionTypes: this._dispatcher.getConnectionTypes()
- }, function(response) {
+ }, {}, function(response) {
if (response.successful) {
- this._state = this.CONNECTED;
- this._clientId = response.clientId;
+ this._state = this.CONNECTED;
+ this._dispatcher.clientId = response.clientId;
- this._selectTransport(response.supportedConnectionTypes);
+ this._dispatcher.selectTransport(response.supportedConnectionTypes);
- this.info('Handshake successful: ?', this._clientId);
+ this.info('Handshake successful: ?', this._dispatcher.clientId);
this.subscribe(this._channels.getKeys(), true);
if (callback) Faye.Promise.defer(function() { callback.call(context) });
} else {
this.info('Handshake unsuccessful');
- Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this._advice.interval);
+ Faye.ENV.setTimeout(function() { self.handshake(callback, context) }, this._dispatcher.retry * 1000);
this._state = this.UNCONNECTED;
}
}, this);
},
@@ -1202,25 +1216,25 @@
return this.handshake(function() { this.connect(callback, context) }, this);
this.callback(callback, context);
if (this._state !== this.CONNECTED) return;
- this.info('Calling deferred actions for ?', this._clientId);
+ this.info('Calling deferred actions for ?', this._dispatcher.clientId);
this.setDeferredStatus('succeeded');
this.setDeferredStatus('unknown');
if (this._connectRequest) return;
this._connectRequest = true;
- this.info('Initiating connection for ?', this._clientId);
+ this.info('Initiating connection for ?', this._dispatcher.clientId);
- this._send({
+ this._sendMessage({
channel: Faye.Channel.CONNECT,
- clientId: this._clientId,
- connectionType: this._transport.connectionType
+ clientId: this._dispatcher.clientId,
+ connectionType: this._dispatcher.connectionType
- }, this._cycleConnection, this);
+ }, {}, this._cycleConnection, this);
},
// Request Response
// MUST include: * channel MUST include: * channel
// * clientId * successful
@@ -1230,24 +1244,30 @@
// * id
disconnect: function() {
if (this._state !== this.CONNECTED) return;
this._state = this.DISCONNECTED;
- this.info('Disconnecting ?', this._clientId);
+ this.info('Disconnecting ?', this._dispatcher.clientId);
+ var promise = new Faye.Publication();
- this._send({
+ this._sendMessage({
channel: Faye.Channel.DISCONNECT,
- clientId: this._clientId
+ clientId: this._dispatcher.clientId
- }, function(response) {
- if (!response.successful) return;
- this._transport.close();
- delete this._transport;
+ }, {}, function(response) {
+ if (response.successful) {
+ this._dispatcher.close();
+ promise.setDeferredStatus('succeeded');
+ } else {
+ promise.setDeferredStatus('failed', Faye.Error.parse(response.error));
+ }
}, this);
- this.info('Clearing channel listeners for ?', this._clientId);
+ this.info('Clearing channel listeners for ?', this._dispatcher.clientId);
this._channels = new Faye.Channel.Set();
+
+ return promise;
},
// Request Response
// MUST include: * channel MUST include: * channel
// * clientId * successful
@@ -1273,26 +1293,26 @@
subscription.setDeferredStatus('succeeded');
return subscription;
}
this.connect(function() {
- this.info('Client ? attempting to subscribe to ?', this._clientId, channel);
+ this.info('Client ? attempting to subscribe to ?', this._dispatcher.clientId, channel);
if (!force) this._channels.subscribe([channel], callback, context);
- this._send({
+ this._sendMessage({
channel: Faye.Channel.SUBSCRIBE,
- clientId: this._clientId,
+ clientId: this._dispatcher.clientId,
subscription: channel
- }, function(response) {
+ }, {}, function(response) {
if (!response.successful) {
subscription.setDeferredStatus('failed', Faye.Error.parse(response.error));
return this._channels.unsubscribe(channel, callback, context);
}
var channels = [].concat(response.subscription);
- this.info('Subscription acknowledged for ? to ?', this._clientId, channels);
+ this.info('Subscription acknowledged for ? to ?', this._dispatcher.clientId, channels);
subscription.setDeferredStatus('succeeded');
}, this);
}, this);
return subscription;
@@ -1316,284 +1336,467 @@
var dead = this._channels.unsubscribe(channel, callback, context);
if (!dead) return;
this.connect(function() {
- this.info('Client ? attempting to unsubscribe from ?', this._clientId, channel);
+ this.info('Client ? attempting to unsubscribe from ?', this._dispatcher.clientId, channel);
- this._send({
+ this._sendMessage({
channel: Faye.Channel.UNSUBSCRIBE,
- clientId: this._clientId,
+ clientId: this._dispatcher.clientId,
subscription: channel
- }, function(response) {
+ }, {}, function(response) {
if (!response.successful) return;
var channels = [].concat(response.subscription);
- this.info('Unsubscription acknowledged for ? from ?', this._clientId, channels);
+ this.info('Unsubscription acknowledged for ? from ?', this._dispatcher.clientId, channels);
}, this);
}, this);
},
// Request Response
// MUST include: * channel MUST include: * channel
// * data * successful
// MAY include: * clientId MAY include: * id
// * id * error
// * ext * ext
- publish: function(channel, data) {
+ publish: function(channel, data, options) {
+ Faye.validateOptions(options || {}, ['attempts', 'deadline']);
var publication = new Faye.Publication();
this.connect(function() {
- this.info('Client ? queueing published message to ?: ?', this._clientId, channel, data);
+ this.info('Client ? queueing published message to ?: ?', this._dispatcher.clientId, channel, data);
- this._send({
+ this._sendMessage({
channel: channel,
data: data,
- clientId: this._clientId
+ clientId: this._dispatcher.clientId
- }, function(response) {
+ }, options, function(response) {
if (response.successful)
publication.setDeferredStatus('succeeded');
else
publication.setDeferredStatus('failed', Faye.Error.parse(response.error));
}, this);
}, this);
return publication;
},
- receiveMessage: function(message) {
- var id = message.id, timeout, callback;
+ _sendMessage: function(message, options, callback, context) {
+ message.id = this._generateMessageId();
+ var timeout = this._advice.timeout
+ ? 1.2 * this._advice.timeout / 1000
+ : 1.2 * this._dispatcher.retry;
+
+ this.pipeThroughExtensions('outgoing', message, null, function(message) {
+ if (!message) return;
+ if (callback) this._responseCallbacks[message.id] = [callback, context];
+ this._dispatcher.sendMessage(message, timeout, options || {});
+ }, this);
+ },
+
+ _generateMessageId: function() {
+ this._messageId += 1;
+ if (this._messageId >= Math.pow(2,32)) this._messageId = 0;
+ return this._messageId.toString(36);
+ },
+
+ _receiveMessage: function(message) {
+ var id = message.id, callback;
+
if (message.successful !== undefined) {
callback = this._responseCallbacks[id];
delete this._responseCallbacks[id];
}
this.pipeThroughExtensions('incoming', message, null, function(message) {
if (!message) return;
-
if (message.advice) this._handleAdvice(message.advice);
this._deliverMessage(message);
-
if (callback) callback[0].call(callback[1], message);
}, this);
+ },
- if (this._transportUp === true) return;
- this._transportUp = true;
- this.trigger('transport:up');
+ _handleAdvice: function(advice) {
+ Faye.extend(this._advice, advice);
+ this._dispatcher.timeout = this._advice.timeout / 1000;
+
+ if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) {
+ this._state = this.UNCONNECTED;
+ this._dispatcher.clientId = null;
+ this._cycleConnection();
+ }
},
- messageError: function(messages, immediate) {
- var retry = this._retry,
- self = this,
- id, message, timeout;
+ _deliverMessage: function(message) {
+ if (!message.channel || message.data === undefined) return;
+ this.info('Client ? calling listeners for ? with ?', this._dispatcher.clientId, message.channel, message.data);
+ this._channels.distributeMessage(message);
+ },
- for (var i = 0, n = messages.length; i < n; i++) {
- message = messages[i];
- id = message.id;
+ _cycleConnection: function() {
+ if (this._connectRequest) {
+ this._connectRequest = null;
+ this.info('Closed connection for ?', this._dispatcher.clientId);
+ }
+ var self = this;
+ Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval);
+ }
+});
- if (immediate)
- this._transportSend(message);
- else
- Faye.ENV.setTimeout(function() { self._transportSend(message) }, retry * 1000);
+Faye.extend(Faye.Client.prototype, Faye.Deferrable);
+Faye.extend(Faye.Client.prototype, Faye.Publisher);
+Faye.extend(Faye.Client.prototype, Faye.Logging);
+Faye.extend(Faye.Client.prototype, Faye.Extensible);
+
+Faye.Dispatcher = Faye.Class({
+ MAX_REQUEST_SIZE: 2048,
+ DEFAULT_RETRY: 5,
+
+ UP: 1,
+ DOWN: 2,
+
+ initialize: function(client, endpoint, options) {
+ this._client = client;
+ this.endpoint = Faye.URI.parse(endpoint);
+ this._alternates = options.endpoints || {};
+
+ this.cookies = Faye.Cookies && new Faye.Cookies.CookieJar();
+ this._disabled = [];
+ this._envelopes = {};
+ this.headers = {};
+ this.retry = options.retry || this.DEFAULT_RETRY;
+ this._scheduler = options.scheduler || Faye.Scheduler;
+ this._state = 0;
+ this.transports = {};
+ this.wsExtensions = [];
+
+ this.proxy = options.proxy || {};
+ if (typeof this._proxy === 'string') this._proxy = {origin: this._proxy};
+
+ var exts = options.websocketExtensions;
+ if (exts) {
+ exts = [].concat(exts);
+ for (var i = 0, n = exts.length; i < n; i++)
+ this.addWebsocketExtension(exts[i]);
}
- if (immediate || this._transportUp === false) return;
- this._transportUp = false;
- this.trigger('transport:down');
+ this.tls = options.tls || {};
+ this.tls.ca = this.tls.ca || options.ca;
+
+ for (var type in this._alternates)
+ this._alternates[type] = Faye.URI.parse(this._alternates[type]);
+
+ this.maxRequestSize = this.MAX_REQUEST_SIZE;
},
- _selectTransport: function(transportTypes) {
+ endpointFor: function(connectionType) {
+ return this._alternates[connectionType] || this.endpoint;
+ },
+
+ addWebsocketExtension: function(extension) {
+ this.wsExtensions.push(extension);
+ },
+
+ disable: function(feature) {
+ this._disabled.push(feature);
+ },
+
+ setHeader: function(name, value) {
+ this.headers[name] = value;
+ },
+
+ close: function() {
+ var transport = this._transport;
+ delete this._transport;
+ if (transport) transport.close();
+ },
+
+ getConnectionTypes: function() {
+ return Faye.Transport.getConnectionTypes();
+ },
+
+ selectTransport: function(transportTypes) {
Faye.Transport.get(this, transportTypes, this._disabled, function(transport) {
this.debug('Selected ? transport for ?', transport.connectionType, Faye.URI.stringify(transport.endpoint));
if (transport === this._transport) return;
if (this._transport) this._transport.close();
this._transport = transport;
+ this.connectionType = transport.connectionType;
}, this);
},
- _send: function(message, callback, context) {
- if (!this._transport) return;
- message.id = message.id || this._generateMessageId();
+ sendMessage: function(message, timeout, options) {
+ options = options || {};
- this.pipeThroughExtensions('outgoing', message, null, function(message) {
- if (!message) return;
- if (callback) this._responseCallbacks[message.id] = [callback, context];
- this._transportSend(message);
- }, this);
+ var id = message.id,
+ attempts = options.attempts,
+ deadline = options.deadline && new Date().getTime() + (options.deadline * 1000),
+ envelope = this._envelopes[id],
+ scheduler;
+
+ if (!envelope) {
+ scheduler = new this._scheduler(message, {timeout: timeout, interval: this.retry, attempts: attempts, deadline: deadline});
+ envelope = this._envelopes[id] = {message: message, scheduler: scheduler};
+ }
+
+ this._sendEnvelope(envelope);
},
- _transportSend: function(message) {
+ _sendEnvelope: function(envelope) {
if (!this._transport) return;
+ if (envelope.request || envelope.timer) return;
- var timeout = 1.2 * (this._advice.timeout || this._retry * 1000),
- envelope = new Faye.Envelope(message, timeout);
+ var message = envelope.message,
+ scheduler = envelope.scheduler,
+ self = this;
- envelope.errback(function(immediate) {
- this.messageError([message], immediate);
- }, this);
+ if (!scheduler.isDeliverable()) {
+ scheduler.abort();
+ delete this._envelopes[message.id];
+ return;
+ }
- this._transport.send(envelope);
- },
+ envelope.timer = Faye.ENV.setTimeout(function() {
+ self.handleError(message);
+ }, scheduler.getTimeout() * 1000);
- _generateMessageId: function() {
- this._messageId += 1;
- if (this._messageId >= Math.pow(2,32)) this._messageId = 0;
- return this._messageId.toString(36);
+ scheduler.send();
+ envelope.request = this._transport.sendMessage(message);
},
- _handleAdvice: function(advice) {
- Faye.extend(this._advice, advice);
+ handleResponse: function(reply) {
+ var envelope = this._envelopes[reply.id];
- if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) {
- this._state = this.UNCONNECTED;
- this._clientId = null;
- this._cycleConnection();
+ if (reply.successful !== undefined && envelope) {
+ envelope.scheduler.succeed();
+ delete this._envelopes[reply.id];
+ Faye.ENV.clearTimeout(envelope.timer);
}
- },
- _deliverMessage: function(message) {
- if (!message.channel || message.data === undefined) return;
- this.info('Client ? calling listeners for ? with ?', this._clientId, message.channel, message.data);
- this._channels.distributeMessage(message);
+ this.trigger('message', reply);
+
+ if (this._state === this.UP) return;
+ this._state = this.UP;
+ this._client.trigger('transport:up');
},
- _cycleConnection: function() {
- if (this._connectRequest) {
- this._connectRequest = null;
- this.info('Closed connection for ?', this._clientId);
+ handleError: function(message, immediate) {
+ var envelope = this._envelopes[message.id],
+ request = envelope && envelope.request,
+ self = this;
+
+ if (!request) return;
+
+ request.then(function(req) {
+ if (req && req.abort) req.abort();
+ });
+
+ var scheduler = envelope.scheduler;
+ scheduler.fail();
+
+ Faye.ENV.clearTimeout(envelope.timer);
+ envelope.request = envelope.timer = null;
+
+ if (immediate) {
+ this._sendEnvelope(envelope);
+ } else {
+ envelope.timer = Faye.ENV.setTimeout(function() {
+ envelope.timer = null;
+ self._sendEnvelope(envelope);
+ }, scheduler.getInterval() * 1000);
}
- var self = this;
- Faye.ENV.setTimeout(function() { self.connect() }, this._advice.interval);
+
+ if (this._state === this.DOWN) return;
+ this._state = this.DOWN;
+ this._client.trigger('transport:down');
}
});
-Faye.extend(Faye.Client.prototype, Faye.Deferrable);
-Faye.extend(Faye.Client.prototype, Faye.Publisher);
-Faye.extend(Faye.Client.prototype, Faye.Logging);
-Faye.extend(Faye.Client.prototype, Faye.Extensible);
+Faye.extend(Faye.Dispatcher.prototype, Faye.Publisher);
+Faye.extend(Faye.Dispatcher.prototype, Faye.Logging);
+Faye.Scheduler = function(message, options) {
+ this.message = message;
+ this.options = options;
+ this.attempts = 0;
+};
+
+Faye.extend(Faye.Scheduler.prototype, {
+ getTimeout: function() {
+ return this.options.timeout;
+ },
+
+ getInterval: function() {
+ return this.options.interval;
+ },
+
+ isDeliverable: function() {
+ var attempts = this.options.attempts,
+ made = this.attempts,
+ deadline = this.options.deadline,
+ now = new Date().getTime();
+
+ if (attempts !== undefined && made >= attempts)
+ return false;
+
+ if (deadline !== undefined && now > deadline)
+ return false;
+
+ return true;
+ },
+
+ send: function() {
+ this.attempts += 1;
+ },
+
+ succeed: function() {},
+
+ fail: function() {},
+
+ abort: function() {}
+});
+
Faye.Transport = Faye.extend(Faye.Class({
- MAX_DELAY: 0,
+ DEFAULT_PORTS: {'http:': 80, 'https:': 443, 'ws:': 80, 'wss:': 443},
+ SECURE_PROTOCOLS: ['https:', 'wss:'],
+ MAX_DELAY: 0,
+
batching: true,
- initialize: function(client, endpoint) {
- this._client = client;
- this.endpoint = endpoint;
- this._outbox = [];
+ initialize: function(dispatcher, endpoint) {
+ this._dispatcher = dispatcher;
+ this.endpoint = endpoint;
+ this._outbox = [];
+ this._proxy = Faye.extend({}, this._dispatcher.proxy);
+
+ if (!this._proxy.origin && Faye.NodeAdapter) {
+ this._proxy.origin = Faye.indexOf(this.SECURE_PROTOCOLS, this.endpoint.protocol) >= 0
+ ? (process.env.HTTPS_PROXY || process.env.https_proxy)
+ : (process.env.HTTP_PROXY || process.env.http_proxy);
+ }
},
close: function() {},
- encode: function(envelopes) {
+ encode: function(messages) {
return '';
},
- send: function(envelope) {
- var message = envelope.message;
-
+ sendMessage: function(message) {
this.debug('Client ? sending message to ?: ?',
- this._client._clientId, Faye.URI.stringify(this.endpoint), message);
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), message);
- if (!this.batching) return this.request([envelope]);
+ if (!this.batching) return Faye.Promise.fulfilled(this.request([message]));
- this._outbox.push(envelope);
+ this._outbox.push(message);
+ this._flushLargeBatch();
+ this._promise = this._promise || new Faye.Promise();
- if (message.channel === Faye.Channel.HANDSHAKE)
- return this.addTimeout('publish', 0.01, this.flush, this);
+ if (message.channel === Faye.Channel.HANDSHAKE) {
+ this.addTimeout('publish', 0.01, this._flush, this);
+ return this._promise;
+ }
if (message.channel === Faye.Channel.CONNECT)
this._connectMessage = message;
- this.flushLargeBatch();
- this.addTimeout('publish', this.MAX_DELAY, this.flush, this);
+ this.addTimeout('publish', this.MAX_DELAY, this._flush, this);
+ return this._promise;
},
- flush: function() {
+ _flush: function() {
this.removeTimeout('publish');
if (this._outbox.length > 1 && this._connectMessage)
this._connectMessage.advice = {timeout: 0};
- this.request(this._outbox);
+ Faye.Promise.fulfill(this._promise, this.request(this._outbox));
+ delete this._promise;
this._connectMessage = null;
this._outbox = [];
},
- flushLargeBatch: function() {
+ _flushLargeBatch: function() {
var string = this.encode(this._outbox);
- if (string.length < this._client.maxRequestSize) return;
+ if (string.length < this._dispatcher.maxRequestSize) return;
var last = this._outbox.pop();
- this.flush();
+ this._flush();
if (last) this._outbox.push(last);
},
- receive: function(envelopes, responses) {
- var n = envelopes.length;
- while (n--) envelopes[n].setDeferredStatus('succeeded');
+ _receive: function(replies) {
+ if (!replies) return;
+ replies = [].concat(replies);
- responses = [].concat(responses);
+ this.debug('Client ? received from ? via ?: ?',
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, replies);
- this.debug('Client ? received from ?: ?',
- this._client._clientId, Faye.URI.stringify(this.endpoint), responses);
-
- for (var i = 0, n = responses.length; i < n; i++)
- this._client.receiveMessage(responses[i]);
+ for (var i = 0, n = replies.length; i < n; i++)
+ this._dispatcher.handleResponse(replies[i]);
},
- handleError: function(envelopes, immediate) {
- var n = envelopes.length;
- while (n--) envelopes[n].setDeferredStatus('failed', immediate);
+ _handleError: function(messages, immediate) {
+ messages = [].concat(messages);
+
+ this.debug('Client ? failed to send to ? via ?: ?',
+ this._dispatcher.clientId, Faye.URI.stringify(this.endpoint), this.connectionType, messages);
+
+ for (var i = 0, n = messages.length; i < n; i++)
+ this._dispatcher.handleError(messages[i]);
},
_getCookies: function() {
- var cookies = this._client.cookies;
+ var cookies = this._dispatcher.cookies,
+ url = Faye.URI.stringify(this.endpoint);
+
if (!cookies) return '';
- return cookies.getCookies({
- domain: this.endpoint.hostname,
- path: this.endpoint.path,
- secure: this.endpoint.protocol === 'https:'
- }).toValueString();
+ return Faye.map(cookies.getCookiesSync(url), function(cookie) {
+ return cookie.cookieString();
+ }).join('; ');
},
_storeCookies: function(setCookie) {
- if (!setCookie || !this._client.cookies) return;
+ var cookies = this._dispatcher.cookies,
+ url = Faye.URI.stringify(this.endpoint),
+ cookie;
+
+ if (!setCookie || !cookies) return;
setCookie = [].concat(setCookie);
- var cookie;
for (var i = 0, n = setCookie.length; i < n; i++) {
- cookie = this._client.cookies.setCookie(setCookie[i]);
- cookie = cookie[0] || cookie;
- cookie.domain = cookie.domain || this.endpoint.hostname;
+ cookie = Faye.Cookies.Cookie.parse(setCookie[i]);
+ cookies.setCookieSync(cookie, url);
}
}
}), {
- get: function(client, allowed, disabled, callback, context) {
- var endpoint = client.endpoint;
+ get: function(dispatcher, allowed, disabled, callback, context) {
+ var endpoint = dispatcher.endpoint;
Faye.asyncEach(this._transports, function(pair, resume) {
var connType = pair[0], klass = pair[1],
- connEndpoint = client.endpoints[connType] || endpoint;
+ connEndpoint = dispatcher.endpointFor(connType);
if (Faye.indexOf(disabled, connType) >= 0)
return resume();
if (Faye.indexOf(allowed, connType) < 0) {
- klass.isUsable(client, connEndpoint, function() {});
+ klass.isUsable(dispatcher, connEndpoint, function() {});
return resume();
}
- klass.isUsable(client, connEndpoint, function(isUsable) {
+ klass.isUsable(dispatcher, connEndpoint, function(isUsable) {
if (!isUsable) return resume();
- var transport = klass.hasOwnProperty('create') ? klass.create(client, connEndpoint) : new klass(client, connEndpoint);
+ var transport = klass.hasOwnProperty('create') ? klass.create(dispatcher, connEndpoint) : new klass(dispatcher, connEndpoint);
callback.call(context, transport);
});
}, function() {
throw new Error('Could not find a usable connection type for ' + Faye.URI.stringify(endpoint));
});
@@ -1602,10 +1805,14 @@
register: function(type, klass) {
this._transports.push([type, klass]);
klass.prototype.connectionType = type;
},
+ getConnectionTypes: function() {
+ return Faye.map(this._transports, function(t) { return t[0] });
+ },
+
_transports: []
});
Faye.extend(Faye.Transport.prototype, Faye.Logging);
Faye.extend(Faye.Transport.prototype, Faye.Timeouts);
@@ -2153,20 +2360,27 @@
this.callback(function() { callback.call(context, true) });
this.errback(function() { callback.call(context, false) });
this.connect();
},
- request: function(envelopes) {
+ request: function(messages) {
this._pending = this._pending || new Faye.Set();
- for (var i = 0, n = envelopes.length; i < n; i++) this._pending.add(envelopes[i]);
+ for (var i = 0, n = messages.length; i < n; i++) this._pending.add(messages[i]);
+ var promise = new Faye.Promise();
+
this.callback(function(socket) {
if (!socket) return;
- var messages = Faye.map(envelopes, function(e) { return e.message });
socket.send(Faye.toJSON(messages));
+ Faye.Promise.fulfill(promise, socket);
}, this);
+
this.connect();
+
+ return {
+ abort: function() { promise.then(function(ws) { ws.close() }) }
+ };
},
connect: function() {
if (Faye.Transport.WebSocket._unloaded) return;
@@ -2203,97 +2417,98 @@
var pending = self._pending ? self._pending.toArray() : [];
delete self._pending;
if (wasConnected) {
- self.handleError(pending, true);
+ self._handleError(pending, true);
} else if (self._everConnected) {
- self.handleError(pending);
+ self._handleError(pending);
} else {
self.setDeferredStatus('failed');
}
};
socket.onmessage = function(event) {
- var messages = JSON.parse(event.data),
- envelopes = [],
- envelope;
+ var replies = JSON.parse(event.data);
+ if (!replies) return;
- if (!messages) return;
- messages = [].concat(messages);
+ replies = [].concat(replies);
- for (var i = 0, n = messages.length; i < n; i++) {
- if (messages[i].successful === undefined) continue;
- envelope = self._pending.remove(messages[i]);
- if (envelope) envelopes.push(envelope);
+ for (var i = 0, n = replies.length; i < n; i++) {
+ if (replies[i].successful === undefined) continue;
+ self._pending.remove(replies[i]);
}
- self.receive(envelopes, messages);
+ self._receive(replies);
};
},
close: function() {
if (!this._socket) return;
this._socket.close();
},
_createSocket: function() {
- var url = Faye.Transport.WebSocket.getSocketUrl(this.endpoint),
- options = {headers: Faye.copyObject(this._client.headers), ca: this._client.ca};
+ var url = Faye.Transport.WebSocket.getSocketUrl(this.endpoint),
+ headers = this._dispatcher.headers,
+ extensions = this._dispatcher.wsExtensions,
+ cookie = this._getCookies(),
+ tls = this._dispatcher.tls,
+ options = {extensions: extensions, headers: headers, proxy: this._proxy, tls: tls};
- options.headers['Cookie'] = this._getCookies();
+ if (cookie !== '') options.headers['Cookie'] = cookie;
if (Faye.WebSocket) return new Faye.WebSocket.Client(url, [], options);
if (Faye.ENV.MozWebSocket) return new MozWebSocket(url);
if (Faye.ENV.WebSocket) return new WebSocket(url);
},
_ping: function() {
if (!this._socket) return;
this._socket.send('[]');
- this.addTimeout('ping', this._client._advice.timeout/2000, this._ping, this);
+ this.addTimeout('ping', this._dispatcher.timeout / 2, this._ping, this);
}
}), {
PROTOCOLS: {
'http:': 'ws:',
'https:': 'wss:'
},
- create: function(client, endpoint) {
- var sockets = client.transports.websocket = client.transports.websocket || {};
- sockets[endpoint.href] = sockets[endpoint.href] || new this(client, endpoint);
+ create: function(dispatcher, endpoint) {
+ var sockets = dispatcher.transports.websocket = dispatcher.transports.websocket || {};
+ sockets[endpoint.href] = sockets[endpoint.href] || new this(dispatcher, endpoint);
return sockets[endpoint.href];
},
getSocketUrl: function(endpoint) {
endpoint = Faye.copyObject(endpoint);
endpoint.protocol = this.PROTOCOLS[endpoint.protocol];
return Faye.URI.stringify(endpoint);
},
- isUsable: function(client, endpoint, callback, context) {
- this.create(client, endpoint).isUsable(callback, context);
+ isUsable: function(dispatcher, endpoint, callback, context) {
+ this.create(dispatcher, endpoint).isUsable(callback, context);
}
});
Faye.extend(Faye.Transport.WebSocket.prototype, Faye.Deferrable);
Faye.Transport.register('websocket', Faye.Transport.WebSocket);
-if (Faye.Event)
+if (Faye.Event && Faye.ENV.onbeforeunload !== undefined)
Faye.Event.on(Faye.ENV, 'beforeunload', function() {
Faye.Transport.WebSocket._unloaded = true;
});
Faye.Transport.EventSource = Faye.extend(Faye.Class(Faye.Transport, {
- initialize: function(client, endpoint) {
- Faye.Transport.prototype.initialize.call(this, client, endpoint);
+ initialize: function(dispatcher, endpoint) {
+ Faye.Transport.prototype.initialize.call(this, dispatcher, endpoint);
if (!Faye.ENV.EventSource) return this.setDeferredStatus('failed');
- this._xhr = new Faye.Transport.XHR(client, endpoint);
+ this._xhr = new Faye.Transport.XHR(dispatcher, endpoint);
endpoint = Faye.copyObject(endpoint);
- endpoint.pathname += '/' + client._clientId;
+ endpoint.pathname += '/' + dispatcher.clientId;
var socket = new EventSource(Faye.URI.stringify(endpoint)),
self = this;
socket.onopen = function() {
@@ -2301,19 +2516,19 @@
self.setDeferredStatus('succeeded');
};
socket.onerror = function() {
if (self._everConnected) {
- self._client.messageError([]);
+ self._handleError([]);
} else {
self.setDeferredStatus('failed');
socket.close();
}
};
socket.onmessage = function(event) {
- self.receive([], JSON.parse(event.data));
+ self._receive(JSON.parse(event.data));
};
this._socket = socket;
},
@@ -2327,114 +2542,113 @@
isUsable: function(callback, context) {
this.callback(function() { callback.call(context, true) });
this.errback(function() { callback.call(context, false) });
},
- encode: function(envelopes) {
- return this._xhr.encode(envelopes);
+ encode: function(messages) {
+ return this._xhr.encode(messages);
},
- request: function(envelopes) {
- this._xhr.request(envelopes);
+ request: function(messages) {
+ return this._xhr.request(messages);
}
}), {
- isUsable: function(client, endpoint, callback, context) {
- var id = client._clientId;
+ isUsable: function(dispatcher, endpoint, callback, context) {
+ var id = dispatcher.clientId;
if (!id) return callback.call(context, false);
- Faye.Transport.XHR.isUsable(client, endpoint, function(usable) {
+ Faye.Transport.XHR.isUsable(dispatcher, endpoint, function(usable) {
if (!usable) return callback.call(context, false);
- this.create(client, endpoint).isUsable(callback, context);
+ this.create(dispatcher, endpoint).isUsable(callback, context);
}, this);
},
- create: function(client, endpoint) {
- var sockets = client.transports.eventsource = client.transports.eventsource || {},
- id = client._clientId;
+ create: function(dispatcher, endpoint) {
+ var sockets = dispatcher.transports.eventsource = dispatcher.transports.eventsource || {},
+ id = dispatcher.clientId;
- endpoint = Faye.copyObject(endpoint);
- endpoint.pathname += '/' + (id || '');
- var url = Faye.URI.stringify(endpoint);
+ var url = Faye.copyObject(endpoint);
+ url.pathname += '/' + (id || '');
+ url = Faye.URI.stringify(url);
- sockets[url] = sockets[url] || new this(client, endpoint);
+ sockets[url] = sockets[url] || new this(dispatcher, endpoint);
return sockets[url];
}
});
Faye.extend(Faye.Transport.EventSource.prototype, Faye.Deferrable);
Faye.Transport.register('eventsource', Faye.Transport.EventSource);
Faye.Transport.XHR = Faye.extend(Faye.Class(Faye.Transport, {
- encode: function(envelopes) {
- var messages = Faye.map(envelopes, function(e) { return e.message });
+ encode: function(messages) {
return Faye.toJSON(messages);
},
- request: function(envelopes) {
- var path = this.endpoint.path,
+ request: function(messages) {
+ var href = this.endpoint.href,
xhr = Faye.ENV.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest(),
self = this;
- xhr.open('POST', path, true);
+ xhr.open('POST', href, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Pragma', 'no-cache');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- var headers = this._client.headers;
+ var headers = this._dispatcher.headers;
for (var key in headers) {
if (!headers.hasOwnProperty(key)) continue;
xhr.setRequestHeader(key, headers[key]);
}
var abort = function() { xhr.abort() };
- Faye.Event.on(Faye.ENV, 'beforeunload', abort);
+ if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.on(Faye.ENV, 'beforeunload', abort);
xhr.onreadystatechange = function() {
if (!xhr || xhr.readyState !== 4) return;
- var parsedMessage = null,
- status = xhr.status,
- text = xhr.responseText,
- successful = (status >= 200 && status < 300) || status === 304 || status === 1223;
+ var replies = null,
+ status = xhr.status,
+ text = xhr.responseText,
+ successful = (status >= 200 && status < 300) || status === 304 || status === 1223;
- Faye.Event.detach(Faye.ENV, 'beforeunload', abort);
+ if (Faye.ENV.onbeforeunload !== undefined) Faye.Event.detach(Faye.ENV, 'beforeunload', abort);
xhr.onreadystatechange = function() {};
xhr = null;
- if (!successful) return self.handleError(envelopes);
+ if (!successful) return self._handleError(messages);
try {
- parsedMessage = JSON.parse(text);
+ replies = JSON.parse(text);
} catch (e) {}
- if (parsedMessage)
- self.receive(envelopes, parsedMessage);
+ if (replies)
+ self._receive(replies);
else
- self.handleError(envelopes);
+ self._handleError(messages);
};
- xhr.send(this.encode(envelopes));
+ xhr.send(this.encode(messages));
+ return xhr;
}
}), {
- isUsable: function(client, endpoint, callback, context) {
+ isUsable: function(dispatcher, endpoint, callback, context) {
callback.call(context, Faye.URI.isSameOrigin(endpoint));
}
});
Faye.Transport.register('long-polling', Faye.Transport.XHR);
Faye.Transport.CORS = Faye.extend(Faye.Class(Faye.Transport, {
- encode: function(envelopes) {
- var messages = Faye.map(envelopes, function(e) { return e.message });
+ encode: function(messages) {
return 'message=' + encodeURIComponent(Faye.toJSON(messages));
},
- request: function(envelopes) {
+ request: function(messages) {
var xhrClass = Faye.ENV.XDomainRequest ? XDomainRequest : XMLHttpRequest,
xhr = new xhrClass(),
- headers = this._client.headers,
+ headers = this._dispatcher.headers,
self = this,
key;
xhr.open('POST', Faye.URI.stringify(this.endpoint), true);
@@ -2451,33 +2665,34 @@
xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onprogress = null;
xhr = null;
};
xhr.onload = function() {
- var parsedMessage = null;
+ var replies = null;
try {
- parsedMessage = JSON.parse(xhr.responseText);
+ replies = JSON.parse(xhr.responseText);
} catch (e) {}
cleanUp();
- if (parsedMessage)
- self.receive(envelopes, parsedMessage);
+ if (replies)
+ self._receive(replies);
else
- self.handleError(envelopes);
+ self._handleError(messages);
};
xhr.onerror = xhr.ontimeout = function() {
cleanUp();
- self.handleError(envelopes);
+ self._handleError(messages);
};
xhr.onprogress = function() {};
- xhr.send(this.encode(envelopes));
+ xhr.send(this.encode(messages));
+ return xhr;
}
}), {
- isUsable: function(client, endpoint, callback, context) {
+ isUsable: function(dispatcher, endpoint, callback, context) {
if (Faye.URI.isSameOrigin(endpoint))
return callback.call(context, false);
if (Faye.ENV.XDomainRequest)
return callback.call(context, endpoint.protocol === Faye.ENV.location.protocol);
@@ -2491,49 +2706,58 @@
});
Faye.Transport.register('cross-origin-long-polling', Faye.Transport.CORS);
Faye.Transport.JSONP = Faye.extend(Faye.Class(Faye.Transport, {
- encode: function(envelopes) {
- var messages = Faye.map(envelopes, function(e) { return e.message });
+ encode: function(messages) {
var url = Faye.copyObject(this.endpoint);
url.query.message = Faye.toJSON(messages);
url.query.jsonp = '__jsonp' + Faye.Transport.JSONP._cbCount + '__';
return Faye.URI.stringify(url);
},
- request: function(envelopes) {
- var messages = Faye.map(envelopes, function(e) { return e.message }),
- head = document.getElementsByTagName('head')[0],
+ request: function(messages) {
+ var head = document.getElementsByTagName('head')[0],
script = document.createElement('script'),
callbackName = Faye.Transport.JSONP.getCallbackName(),
endpoint = Faye.copyObject(this.endpoint),
self = this;
endpoint.query.message = Faye.toJSON(messages);
endpoint.query.jsonp = callbackName;
- Faye.ENV[callbackName] = function(data) {
+ var cleanup = function() {
if (!Faye.ENV[callbackName]) return false;
Faye.ENV[callbackName] = undefined;
try { delete Faye.ENV[callbackName] } catch (e) {}
script.parentNode.removeChild(script);
- self.receive(envelopes, data);
};
+ Faye.ENV[callbackName] = function(replies) {
+ cleanup();
+ self._receive(replies);
+ };
+
script.type = 'text/javascript';
script.src = Faye.URI.stringify(endpoint);
head.appendChild(script);
+
+ script.onerror = function() {
+ cleanup();
+ self._handleError(messages);
+ };
+
+ return {abort: cleanup};
}
}), {
_cbCount: 0,
getCallbackName: function() {
this._cbCount += 1;
return '__jsonp' + this._cbCount + '__';
},
- isUsable: function(client, endpoint, callback, context) {
+ isUsable: function(dispatcher, endpoint, callback, context) {
callback.call(context, true);
}
});
Faye.Transport.register('callback-polling', Faye.Transport.JSONP);
\ No newline at end of file