From a3448a84b8e918bb18e95c89a63adc1052d5a1c6 Mon Sep 17 00:00:00 2001 From: Matthew O'Riordan Date: Thu, 17 Dec 2015 11:29:47 +0200 Subject: [PATCH 1/2] Regenerate and release version 0.8.10 --- browser/fragments/license.js | 2 +- browser/static/ably.js | 422 +++++++++++---- browser/static/ably.min.js | 484 +++++++++--------- browser/static/ably.noencryption.js | 422 +++++++++++---- browser/static/ably.noencryption.min.js | 448 ++++++++-------- browser/static/compat-pubnub.js | 8 +- browser/static/compat-pubnub.min.js | 10 +- browser/static/compat-pusher.js | 60 ++- browser/static/compat-pusher.min.js | 37 +- .../{iframe-0.8.9.html => iframe-0.8.10.html} | 102 +++- browser/static/iframe.js | 102 +++- browser/static/iframe.min-0.8.10.html | 61 +++ browser/static/iframe.min-0.8.9.html | 59 --- browser/static/iframe.min.js | 68 +-- common/lib/util/defaults.js | 2 +- package.json | 2 +- spec/support/browser_file_list.js | 2 +- 17 files changed, 1422 insertions(+), 869 deletions(-) rename browser/static/{iframe-0.8.9.html => iframe-0.8.10.html} (92%) create mode 100644 browser/static/iframe.min-0.8.10.html delete mode 100644 browser/static/iframe.min-0.8.9.html diff --git a/browser/fragments/license.js b/browser/fragments/license.js index 8542a12dda..5a3cd625a7 100644 --- a/browser/fragments/license.js +++ b/browser/fragments/license.js @@ -1,7 +1,7 @@ /** * @license Copyright 2015, Ably * - * Ably JavaScript Library v0.8.9 + * Ably JavaScript Library v0.8.10 * https://github.com/ably/ably-js * * Ably Realtime Messaging diff --git a/browser/static/ably.js b/browser/static/ably.js index 0c29ad9a1d..dc74c41777 100644 --- a/browser/static/ably.js +++ b/browser/static/ably.js @@ -1,7 +1,7 @@ /** * @license Copyright 2015, Ably * - * Ably JavaScript Library v0.8.9 + * Ably JavaScript Library v0.8.10 * https://github.com/ably/ably-js * * Ably Realtime Messaging @@ -3899,8 +3899,8 @@ var DomEvent = (function() { }); Defaults.protocolVersion = 1; Defaults.ENVIRONMENT = ''; -Defaults.HOST = 'rest.ably.io'; -Defaults.WS_HOST = 'realtime.ably.io'; +Defaults.REST_HOST = 'rest.ably.io'; +Defaults.REALTIME_HOST = 'realtime.ably.io'; Defaults.FALLBACK_HOSTS = ['A.ably-realtime.com', 'B.ably-realtime.com', 'C.ably-realtime.com', 'D.ably-realtime.com', 'E.ably-realtime.com']; Defaults.PORT = 80; Defaults.TLS_PORT = 443; @@ -3917,13 +3917,14 @@ Defaults.TIMEOUTS = { }; Defaults.httpMaxRetryCount = 3; -Defaults.version = '0.8.9'; +Defaults.version = '0.8.10'; +Defaults.apiVersion = '0.8'; Defaults.getHost = function(options, host, ws) { if(ws) - host = ((host == options.host) && options.wsHost) || host || options.wsHost; + host = ((host == options.restHost) && options.realtimeHost) || host || options.realtimeHost; else - host = host || options.host; + host = host || options.restHost; return host; }; @@ -3937,7 +3938,7 @@ Defaults.getHttpScheme = function(options) { }; Defaults.getHosts = function(options) { - var hosts = [options.host], + var hosts = [options.restHost], fallbackHosts = options.fallbackHosts, httpMaxRetryCount = typeof(options.httpMaxRetryCount) !== 'undefined' ? options.httpMaxRetryCount : Defaults.httpMaxRetryCount; @@ -3946,13 +3947,30 @@ Defaults.getHosts = function(options) { }; Defaults.normaliseOptions = function(options) { + /* Deprecated options */ if(options.host) { - options.wsHost = options.wsHost || options.host; + Logger.deprecated('host', 'restHost'); + options.restHost = options.host; + } + if(options.wsHost) { + Logger.deprecated('wsHost', 'realtimeHost'); + options.realtimeHost = options.wsHost; + } + if(options.queueEvents) { + Logger.deprecated('queueEvents', 'queueMessages'); + options.queueMessages = options.queueEvents; + } + + if(!('queueMessages' in options)) + options.queueMessages = true; + + if(options.restHost) { + options.realtimeHost = options.realtimeHost || options.restHost; } else { var environment = (options.environment && String(options.environment).toLowerCase()) || Defaults.ENVIRONMENT, - production = !environment || (environment === 'production'); - options.host = production ? Defaults.HOST : environment + '-' + Defaults.HOST; - options.wsHost = production ? Defaults.WS_HOST : environment + '-' + Defaults.WS_HOST; + production = !environment || (environment === 'production'); + options.restHost = production ? Defaults.REST_HOST : environment + '-' + Defaults.REST_HOST; + options.realtimeHost = production ? Defaults.REALTIME_HOST : environment + '-' + Defaults.REALTIME_HOST; options.fallbackHosts = production ? Defaults.FALLBACK_HOSTS : options.fallbackHosts; } options.port = options.port || Defaults.PORT; @@ -3978,6 +3996,13 @@ var EventEmitter = (function() { this.eventsOnce = {}; } + /* Call the listener, catch any exceptions and log, but continue operation*/ + function callListener(eventThis, listener, args) { + try { listener.apply(eventThis, args); } catch(e) { + Logger.logAction(Logger.LOG_ERROR, 'EventEmitter.emit()', 'Unexpected listener exception: ' + e + '; stack = ' + e.stack); + } + } + /** * Add an event listener * @param event (optional) the name of the event to listen to @@ -4074,18 +4099,11 @@ var EventEmitter = (function() { var args = Array.prototype.slice.call(arguments, 1); var eventThis = {event:event}; - /* wrap the try/catch in a function improves performance by 30% */ - function callListener(listener) { - try { listener.apply(eventThis, args); } catch(e) { - Logger.logAction(Logger.LOG_ERROR, 'EventEmitter.emit()', 'Unexpected listener exception: ' + e + '; stack = ' + e.stack); - throw e; - } - } if(this.anyOnce.length) { var listeners = this.anyOnce; this.anyOnce = []; for(var i = 0; i < listeners.length; i++) - callListener((listeners[i])); + callListener(eventThis, listeners[i], args); } for(var i = 0; i < this.any.length; i++) this.any[i].apply(eventThis, args); @@ -4093,12 +4111,12 @@ var EventEmitter = (function() { if(listeners) { delete this.eventsOnce[event]; for(var i = 0; i < listeners.length; i++) - callListener((listeners[i])); + callListener(eventThis, listeners[i], args); } var listeners = this.events[event]; if(listeners) for(var i = 0; i < listeners.length; i++) - callListener((listeners[i])); + callListener(eventThis, listeners[i], args); }; /** @@ -4117,6 +4135,30 @@ var EventEmitter = (function() { } }; + /** + * Private API + * + * Listen for a single occurrence of a state event and fire immediately if currentState matches targetState + * @param targetState the name of the state event to listen to + * @param currentState the name of the current state of this object + * @param listener the listener to be called + */ + EventEmitter.prototype.whenState = function(targetState, currentState, listener /* ...listenerArgs */) { + var eventThis = {event:targetState}, + listenerArgs = Array.prototype.slice.call(arguments, 3); + + if((typeof(targetState) !== 'string') || (typeof(currentState) !== 'string')) + throw("whenState requires a valid event String argument"); + if (typeof(listener) !== 'function') + throw("whenState requires a valid listener argument"); + + if(targetState === currentState) { + callListener(eventThis, listener, listenerArgs); + } else { + this.once(targetState, listener); + } + } + return EventEmitter; })(); @@ -4155,6 +4197,12 @@ var Logger = (function() { } }; + Logger.deprecated = function(original, replacement) { + if (Logger.shouldLog(LOG_ERROR)) { + logHandler("Ably: Deprecation warning - '" + original + "' is deprecated and will be removed from a future version. Please use '" + replacement + "' instead."); + } + } + /* Where a logging operation is expensive, such as serialisation of data, use shouldLog will prevent the object being serialised if the log level will not output the message */ Logger.shouldLog = function(level) { @@ -4384,7 +4432,10 @@ var Utils = (function() { Utils.defaultGetHeaders = function(format) { format = format || 'json'; var accept = (format === 'json') ? contentTypes.json : contentTypes[format] + ',' + contentTypes.json; - return { accept: accept }; + return { + accept: accept, + 'X-Ably-Version': Defaults.apiVersion + }; }; Utils.defaultPostHeaders = function(format) { @@ -4394,7 +4445,8 @@ var Utils = (function() { return { accept: accept, - 'content-type': contentType + 'content-type': contentType, + 'X-Ably-Version': Defaults.apiVersion }; }; @@ -4864,7 +4916,7 @@ var ProtocolMessage = (function() { return '[ ' + result.join(', ') + ' ]'; } - var simpleAttributes = 'id channel channelSerial connectionId connectionKey connectionSerial count flags messageSerial timestamp'.split(' '); + var simpleAttributes = 'id channel channelSerial connectionId connectionKey connectionSerial count flags msgSerial timestamp'.split(' '); ProtocolMessage.stringify = function(msg) { var result = '[ProtocolMessage'; @@ -5147,8 +5199,6 @@ var ConnectionManager = (function() { this.connectionKey = connectionKey; this.connectionSerial = connectionSerial; this.format = options.useBinaryProtocol ? 'msgpack' : 'json'; - if(options.transportParams && options.transportParams.stream !== undefined) - this.stream = options.transportParams.stream; } TransportParams.prototype.getConnectParams = function(params) { @@ -5187,8 +5237,10 @@ var ConnectionManager = (function() { params.echo = 'false'; if(this.format !== undefined) params.format = this.format; - if(this.stream !== undefined) - params.stream = this.stream; + if(options.transportParams !== undefined) { + Utils.mixin(params, options.transportParams); + } + params.v = Defaults.apiVersion; return params; }; @@ -5550,7 +5602,7 @@ var ConnectionManager = (function() { } var auth = this.realtime.auth; - if(clientId) { + if(clientId && !(clientId === '*')) { if(auth.clientId && auth.clientId != clientId) { /* Should never happen in normal circumstances as realtime should * recognise mismatch and return an error */ @@ -5646,6 +5698,7 @@ var ConnectionManager = (function() { this.realtime.connection.id = this.connectionId = connectionId; this.realtime.connection.key = this.connectionKey = connectionKey; this.realtime.connection.serial = this.connectionSerial = (connectionSerial === undefined) ? -1 : connectionSerial; + this.realtime.connection.recoveryKey = connectionKey + ':' + this.connectionSerial; this.msgSerial = 0; if(this.options.recover === true) this.persistConnection(); @@ -5656,6 +5709,7 @@ var ConnectionManager = (function() { this.realtime.connection.id = this.connectionId = undefined; this.realtime.connection.key = this.connectionKey = undefined; this.realtime.connection.serial = this.connectionSerial = undefined; + this.realtime.connection.recoveryKey = null; this.msgSerial = 0; this.unpersistConnection(); }; @@ -6043,6 +6097,7 @@ var ConnectionManager = (function() { } if(connectionSerial !== undefined) { this.realtime.connection.serial = this.connectionSerial = connectionSerial; + this.realtime.connection.recoveryKey = this.connectionKey + ':' + connectionSerial; } this.realtime.channels.onChannelMessage(message); } else { @@ -6306,14 +6361,21 @@ var WebSocketTransport = (function() { WebSocketTransport.tryConnect = function(connectionManager, auth, params, callback) { var transport = new WebSocketTransport(connectionManager, auth, params); var errorCb = function(err) { callback(err); }; + var closeHandler; transport.on('wserror', errorCb); transport.on('wsopen', function() { Logger.logAction(Logger.LOG_MINOR, 'WebSocketTransport.tryConnect()', 'viable transport ' + transport); transport.off('wserror', errorCb); transport.cancelConnectTimeout(); + connectionManager.off(closeHandler); callback(null, transport); }); + /* At this point connectionManager has no reference to websocketTransport. + * So need to handle a connect timeout and listen for close events here temporarily */ transport.startConnectTimeout(); + closeHandler = connectionManager.on('connectionstate', function(stateChange) { + if(stateChange.current === 'closing') transport.close(); + }); transport.connect(); }; @@ -6457,13 +6519,12 @@ var CometTransport = (function() { /* binary not supported for comet, so just fall back to default */ params.format = undefined; Transport.call(this, connectionManager, auth, params); - /* streaming defaults to true */ - this.stream = ('stream' in params) ? params.stream : true; this.sendRequest = null; this.recvRequest = null; this.pendingCallback = null; this.pendingItems = null; this.disposed = false; + this.stream = true; } Utils.inherits(CometTransport, Transport); @@ -6491,6 +6552,7 @@ var CometTransport = (function() { } self.authParams = authParams; var connectParams = self.params.getConnectParams(authParams); + if('stream' in connectParams) self.stream = connectParams.stream; Logger.logAction(Logger.LOG_MINOR, 'CometTransport.connect()', 'connectParams:' + Utils.toQueryString(connectParams)); /* this will be the 'recvRequest' so this connection can stream messages */ @@ -6520,6 +6582,9 @@ var CometTransport = (function() { self.finish('error', err); return; } + Utils.nextTick(function() { + self.recv(); + }); }); connectRequest.exec(); }); @@ -6579,11 +6644,6 @@ var CometTransport = (function() { this.sendUri = baseConnectionUri + '/send'; this.recvUri = baseConnectionUri + '/recv'; this.closeUri = function(closing) { return baseConnectionUri + (closing ? '/close' : '/disconnect'); }; - - var self = this; - Utils.nextTick(function() { - self.recv(); - }) }; CometTransport.prototype.send = function(message, callback) { @@ -6718,7 +6778,7 @@ var Presence = (function() { format = rest.options.useBinaryProtocol ? 'msgpack' : 'json', envelope = Http.supportsLinkHeaders ? undefined : format, headers = Utils.copy(Utils.defaultGetHeaders(format)), - options = this.channel.options; + options = this.channel.channelOptions; if(rest.options.headers) Utils.mixin(headers, rest.options.headers); @@ -6747,7 +6807,7 @@ var Presence = (function() { format = rest.options.useBinaryProtocol ? 'msgpack' : 'json', envelope = Http.supportsLinkHeaders ? undefined : format, headers = Utils.copy(Utils.defaultGetHeaders(format)), - options = this.channel.options, + options = this.channel.channelOptions, channel = this.channel; if(rest.options.headers) @@ -6998,8 +7058,15 @@ var PaginatedResource = (function() { this.first = function(cb) { self.get(relParams.first, cb); }; if('current' in relParams) this.current = function(cb) { self.get(relParams.current, cb); }; - if('next' in relParams) - this.next = function(cb) { self.get(relParams.next, cb); }; + this.next = function(cb) { + if('next' in relParams) + self.get(relParams.next, cb); + else + cb(null, null); + }; + + this.hasNext = function() { return ('next' in relParams) }; + this.isLast = function() { return !this.hasNext(); } } PaginatedResult.prototype.get = function(params, callback) { @@ -7011,6 +7078,7 @@ var PaginatedResource = (function() { return PaginatedResource; })(); + var Auth = (function() { var isBrowser = (typeof(window) == 'object'); var crypto = isBrowser ? null : require('crypto'); @@ -7053,8 +7121,14 @@ var Auth = (function() { function Auth(rest, options) { this.rest = rest; - this.tokenParams = {}; - this.clientId = options.clientId; + this.tokenParams = options.defaultTokenParams || {}; + + /* RSA7a4: if options.clientId is provided and is not + * null, it overrides defaultTokenParams.clientId */ + if(options.clientId) { + this.tokenParams.clientId = options.clientId; + this.clientId = options.clientId + } /* decide default auth method */ var key = options.key; @@ -7090,7 +7164,6 @@ var Auth = (function() { options.tokenDetails = (typeof(options.token) === 'string') ? {token: options.token} : options.token; } this.tokenDetails = options.tokenDetails; - this.tokenParams.clientId = options.clientId; if(options.authCallback) { Logger.logAction(Logger.LOG_MINOR, 'Auth()', 'using token auth with authCallback'); @@ -7145,9 +7218,18 @@ var Auth = (function() { * @param callback (err, tokenDetails) */ Auth.prototype.authorise = function(tokenParams, authOptions, callback) { + /* shuffle and normalise arguments as necessary */ + if(typeof(tokenParams) == 'function' && !callback) { + callback = tokenParams; + authOptions = tokenParams = null; + } else if(typeof(authOptions) == 'function' && !callback) { + callback = authOptions; + authOptions = null; + } + var token = this.tokenDetails; if(token) { - if(this.clientId && token.clientId && this.clientId !== token.clientId) { + if(this._tokenClientIdMismatch(token.clientId)) { callback(new ErrorInfo('ClientId in token was ' + token.clientId + ', but library was instantiated with clientId ' + this.clientId, 40102, 401)); return; } @@ -7383,6 +7465,15 @@ var Auth = (function() { * */ Auth.prototype.createTokenRequest = function(tokenParams, authOptions, callback) { + /* shuffle and normalise arguments as necessary */ + if(typeof(tokenParams) == 'function' && !callback) { + callback = tokenParams; + authOptions = tokenParams = null; + } else if(typeof(authOptions) == 'function' && !callback) { + callback = authOptions; + authOptions = null; + } + authOptions = Utils.mixin(Utils.copy(this.rest.options), authOptions); tokenParams = tokenParams || Utils.copy(this.tokenParams); @@ -7497,6 +7588,13 @@ var Auth = (function() { return Date.now() + (this.rest.serverTimeOffset || 0); }; + Auth.prototype._tokenClientIdMismatch = function(tokenClientId) { + return this.clientId && + tokenClientId && + (tokenClientId !== '*') && + (this.clientId !== tokenClientId); + }; + return Auth; })(); @@ -7530,6 +7628,14 @@ var Rest = (function() { options.keyName = keyMatch[1]; options.keySecret = keyMatch[2]; } + + if('clientId' in options) { + if(!(typeof(options.clientId) === 'string' || options.clientId === null)) + throw new ErrorInfo('clientId must be either a string or null', 40012, 400); + else if(options.clientId === '*') + throw new ErrorInfo('Can’t use "*" as a clientId as that string is reserved. (To change the default token request behaviour to use a wildcard clientId, use {defaultTokenParams: {clientId: "*"}})', 40012, 400); + } + if(options.log) Logger.setLog(options.log.level, options.log.handler); Logger.logAction(Logger.LOG_MINOR, 'Rest()', 'started'); @@ -7602,17 +7708,21 @@ var Rest = (function() { this.attached = {}; } - Channels.prototype.get = function(name) { + Channels.prototype.get = function(name, channelOptions) { name = String(name); var channel = this.attached[name]; if(!channel) { - this.attached[name] = channel = new Channel(this.rest, name); + this.attached[name] = channel = new Channel(this.rest, name, channelOptions); + } else if(channelOptions) { + channel.setOptions(channelOptions); } + return channel; }; return Rest; })(); + var Realtime = (function() { function Realtime(options) { @@ -7673,11 +7783,13 @@ var Realtime = (function() { } }; - Channels.prototype.get = function(name) { + Channels.prototype.get = function(name, channelOptions) { name = String(name); var channel = this.all[name]; if(!channel) { - channel = this.all[name] = new RealtimeChannel(this.realtime, name, this.realtime.options); + channel = this.all[name] = new RealtimeChannel(this.realtime, name, channelOptions); + } else if(channelOptions) { + channel.setOptions(channelOptions); } return channel; }; @@ -7735,6 +7847,7 @@ var Connection = (function() { this.key = undefined; this.id = undefined; this.serial = undefined; + this.recoveryKey = undefined; var self = this; this.connectionManager.on('connectionstate', function(stateChange) { @@ -7746,14 +7859,9 @@ var Connection = (function() { } Utils.inherits(Connection, EventEmitter); - /* public instance methods */ - Connection.prototype.on = function(state, callback) { - EventEmitter.prototype.on.apply(this, arguments); - if(this.state == state && callback) - try { - callback(new ConnectionStateChange(undefined, state)); - } catch(e) {} - }; + Connection.prototype.whenState = function(state, listener) { + EventEmitter.prototype.whenState.call(this, state, this.state, listener, new ConnectionStateChange(undefined, state)); + } Connection.prototype.connect = function() { Logger.logAction(Logger.LOG_MAJOR, 'Connection.connect()', ''); @@ -7777,23 +7885,21 @@ var Connection = (function() { var Channel = (function() { function noop() {} - var defaultOptions = {}; - /* public constructor */ - function Channel(rest, name, options) { + function Channel(rest, name, channelOptions) { Logger.logAction(Logger.LOG_MINOR, 'Channel()', 'started; name = ' + name); EventEmitter.call(this); this.rest = rest; this.name = name; this.basePath = '/channels/' + encodeURIComponent(name); this.presence = new Presence(this); - this.setOptions(options); + this.setOptions(channelOptions); } Utils.inherits(Channel, EventEmitter); Channel.prototype.setOptions = function(options, callback) { callback = callback || noop; - options = this.options = Utils.prototypicalClone(defaultOptions, options); + this.channelOptions = options = options || {}; if(options.encrypted) { if(!Crypto) throw new Error('Encryption not enabled; use ably.encryption.js instead'); Crypto.getCipher(options, function(err, cipher) { @@ -7825,7 +7931,7 @@ var Channel = (function() { format = rest.options.useBinaryProtocol ? 'msgpack' : 'json', envelope = Http.supportsLinkHeaders ? undefined : format, headers = Utils.copy(Utils.defaultGetHeaders(format)), - options = this.options, + options = this.channelOptions, channel = this; if(rest.options.headers) @@ -7846,16 +7952,19 @@ var Channel = (function() { ++argCount; } if(argCount == 2) { - if(!Utils.isArray(messages)) - messages = [messages]; - messages = Message.fromValuesArray(messages); + if(Utils.isObject(messages)) + messages = [Message.fromValues(messages)]; + else if(Utils.isArray(messages)) + messages = Message.fromValuesArray(messages); + else + throw new ErrorInfo('The single-argument form of publish() expects a message object or an array of message objects', 40013, 400); } else { messages = [Message.fromValues({name: arguments[0], data: arguments[1]})]; } var rest = this.rest, format = rest.options.useBinaryProtocol ? 'msgpack' : 'json', - requestBody = Message.toRequestBody(messages, this.options, format), + requestBody = Message.toRequestBody(messages, this.channelOptions, format), headers = Utils.copy(Utils.defaultPostHeaders(format)); if(rest.options.headers) @@ -7876,21 +7985,18 @@ var RealtimeChannel = (function() { var flags = ProtocolMessage.Flag; var noop = function() {}; - var defaultOptions = { - queueEvents: true - }; - /* public constructor */ function RealtimeChannel(realtime, name, options) { Logger.logAction(Logger.LOG_MINOR, 'RealtimeChannel()', 'started; name = ' + name); Channel.call(this, realtime, name, options); - this.presence = new RealtimePresence(this, options); + this.presence = new RealtimePresence(this, realtime.options); this.connectionManager = realtime.connection.connectionManager; this.state = 'initialized'; this.subscriptions = new EventEmitter(); this.pendingEvents = []; this.syncChannelSerial = undefined; this.attachSerial = undefined; + this.realtime = realtime; this.setOptions(options); } Utils.inherits(RealtimeChannel, Channel); @@ -7907,9 +8013,17 @@ var RealtimeChannel = (function() { message: 'Channel is detached' }; + RealtimeChannel.processListenerArgs = function(args) { + /* [event], listener, [callback] */ + if(typeof(args[0]) == 'function') + return [null, args[0], args[1] || noop]; + else + return [args[0], args[1], (args[2] || noop)]; + } + RealtimeChannel.prototype.setOptions = function(options, callback) { callback = callback || noop; - options = this.options = Utils.prototypicalClone(defaultOptions, options); + this.channelOptions = options = options || {}; if(options.encrypted) { if(!Crypto) throw new Error('Encryption not enabled; use ably.encryption.js instead'); Crypto.getCipher(options, function(err, cipher) { @@ -7925,7 +8039,7 @@ var RealtimeChannel = (function() { var argCount = arguments.length, messages = arguments[0], callback = arguments[argCount - 1], - options = this.options; + options = this.channelOptions; if(typeof(callback) !== 'function') { callback = noop; @@ -7937,9 +8051,12 @@ var RealtimeChannel = (function() { return; } if(argCount == 2) { - if(!Utils.isArray(messages)) - messages = [messages]; - messages = Message.fromValuesArray(messages); + if(Utils.isObject(messages)) + messages = [Message.fromValues(messages)]; + else if(Utils.isArray(messages)) + messages = Message.fromValuesArray(messages); + else + throw new ErrorInfo('The single-argument form of publish() expects a message object or an array of message objects', 40013, 400); } else { messages = [Message.fromValues({name: arguments[0], data: arguments[1]})]; } @@ -7952,6 +8069,9 @@ var RealtimeChannel = (function() { RealtimeChannel.prototype._publish = function(messages, callback) { Logger.logAction(Logger.LOG_MICRO, 'RealtimeChannel.publish()', 'message count = ' + messages.length); switch(this.state) { + case 'failed': + callback(ErrorInfo.fromValues(RealtimeChannel.invalidStateError)); + break; case 'attached': Logger.logAction(Logger.LOG_MICRO, 'RealtimeChannel.publish()', 'sending message'); var msg = new ProtocolMessage(); @@ -8053,15 +8173,17 @@ var RealtimeChannel = (function() { }; RealtimeChannel.prototype.subscribe = function(/* [event], listener, [callback] */) { - var args = Array.prototype.slice.call(arguments); - if(args.length == 1 && typeof(args[0]) == 'function') - args.unshift(null); - + var args = RealtimeChannel.processListenerArgs(arguments); var event = args[0]; var listener = args[1]; - var callback = (args[2] || (args[2] = noop)); + var callback = args[2]; var subscriptions = this.subscriptions; + if(this.state === 'failed') { + callback(ErrorInfo.fromValues(RealtimeChannel.invalidStateError)); + return; + } + if(event === null || !Utils.isArray(event)) subscriptions.on(event, listener); else @@ -8071,15 +8193,18 @@ var RealtimeChannel = (function() { this.attach(callback); }; - RealtimeChannel.prototype.unsubscribe = function(/* [event], listener */) { - var args = Array.prototype.slice.call(arguments); - if(args.length == 1 && typeof(args[0]) == 'function') - args.unshift(null); - + RealtimeChannel.prototype.unsubscribe = function(/* [event], listener, [callback] */) { + var args = RealtimeChannel.processListenerArgs(arguments); var event = args[0]; var listener = args[1]; + var callback = args[2]; var subscriptions = this.subscriptions; + if(this.state === 'failed') { + callback(ErrorInfo.fromValues(RealtimeChannel.invalidStateError)); + return; + } + if(event === null || !Utils.isArray(event)) subscriptions.off(event, listener); else @@ -8107,7 +8232,7 @@ var RealtimeChannel = (function() { }; RealtimeChannel.prototype.sendMessage = function(msg, callback) { - this.connectionManager.send(msg, this.options.queueEvents, callback); + this.connectionManager.send(msg, this.realtime.options.queueMessages, callback); }; RealtimeChannel.prototype.sendPresence = function(presence, callback) { @@ -8140,7 +8265,7 @@ var RealtimeChannel = (function() { id = message.id, connectionId = message.connectionId, timestamp = message.timestamp, - options = this.options; + options = this.channelOptions; for(var i = 0; i < presence.length; i++) { try { @@ -8162,7 +8287,7 @@ var RealtimeChannel = (function() { id = message.id, connectionId = message.connectionId, timestamp = message.timestamp, - options = this.options; + options = this.channelOptions; for(var i = 0; i < messages.length; i++) { try { @@ -8263,23 +8388,25 @@ var RealtimeChannel = (function() { if(msgErr) { /* this is an error message */ var err = {statusCode: msgErr.statusCode, code: msgErr.code, message: msgErr.message}; - this.failPendingMessages(err); this.setState('failed', err); + this.failPendingMessages(err); } else { - this.failPendingMessages({statusCode: 404, code: 90001, message: 'Channel detached'}); if(this.state !== 'detached') { this.setState('detached'); } + this.failPendingMessages({statusCode: 404, code: 90001, message: 'Channel detached'}); } }; RealtimeChannel.prototype.setSuspended = function(err, suppressEvent) { - Logger.logAction(Logger.LOG_MINOR, 'RealtimeChannel.setSuspended', 'deactivating channel; name = ' + this.name + ', err ' + (err ? err.message : 'none')); - this.clearStateTimer(); - this.failPendingMessages(err); - this.presence.setSuspended(err); - if(!suppressEvent && this.state !== 'detached') { - this.setState('detached'); + if(this.state !== 'detached' && this.state !== 'failed') { + Logger.logAction(Logger.LOG_MINOR, 'RealtimeChannel.setSuspended', 'deactivating channel; name = ' + this.name + ', err ' + (err ? err.message : 'none')); + this.clearStateTimer(); + this.presence.setSuspended(err); + if(!suppressEvent) { + this.setState('detached'); + } + this.failPendingMessages(err); } }; @@ -8312,7 +8439,7 @@ var RealtimeChannel = (function() { self.stateTimer = null; /* retry */ self.checkPendingState(); - }, this.options.timeouts.realtimeRequestTimeout); + }, this.realtime.options.timeouts.realtimeRequestTimeout); }; RealtimeChannel.prototype.checkPendingState = function() { @@ -8373,13 +8500,17 @@ var RealtimeChannel = (function() { delete params.untilAttach; params.from_serial = this.attachSerial; } else { - throw new ErrorInfo("option untilAttach requires the channel to be attached", 40000, 400); + callback(new ErrorInfo("option untilAttach requires the channel to be attached", 40000, 400)); } } Channel.prototype._history.call(this, params, callback); }; + RealtimeChannel.prototype.whenState = function(state, listener) { + EventEmitter.prototype.whenState.call(this, state, this.state, listener); + } + return RealtimeChannel; })(); @@ -8392,6 +8523,25 @@ var RealtimePresence = (function() { return item.clientId + ':' + item.connectionId; } + function waitAttached(channel, callback, action) { + switch(channel.state) { + case 'attached': + action(); + break; + case 'initialized': + case 'detached': + case 'detaching': + case 'attaching': + channel.attach(function(err) { + if(err) callback(err); + else action(); + }); + break; + default: + callback(ErrorInfo.fromValues(RealtimeChannel.invalidStateError)); + } + } + function RealtimePresence(channel, options) { EventEmitter.call(this); Presence.call(this, channel); @@ -8468,16 +8618,21 @@ var RealtimePresence = (function() { }; RealtimePresence.prototype.leave = function(data, callback) { - if (!callback && (typeof(data)==='function')) { - callback = data; - data = ''; - } if(!this.clientId) throw new Error('clientId must have been specified to enter or leave a presence channel'); this.leaveClient(undefined, data, callback); }; RealtimePresence.prototype.leaveClient = function(clientId, data, callback) { + if (!callback) { + if (typeof(data)==='function') { + callback = data; + data = null; + } else { + callback = noop; + } + } + Logger.logAction(Logger.LOG_MICRO, 'RealtimePresence.leaveClient()', 'leaving; channel = ' + this.channel.name + ', client = ' + clientId); var presence = PresenceMessage.fromValues({ action : presenceAction.LEAVE, @@ -8496,6 +8651,7 @@ var RealtimePresence = (function() { }; break; case 'initialized': + case 'failed': /* we're not attached; therefore we let any entered status * timeout by itself instead of attaching just in order to leave */ this.pendingPresence = null; @@ -8517,11 +8673,14 @@ var RealtimePresence = (function() { args.unshift(null); var params = args[0], - callback = args[1] || noop, - members = this.members; + callback = args[1] || noop; - members.waitSync(function() { - callback(null, params ? members.list(params) : members.values()); + var self = this; + waitAttached(this.channel, callback, function() { + var members = self.members; + members.waitSync(function() { + callback(null, params ? members.list(params) : members.values()); + }); }); }; @@ -8542,7 +8701,7 @@ var RealtimePresence = (function() { delete params.untilAttach; params.from_serial = this.channel.attachSerial; } else { - throw new ErrorInfo("option untilAttach requires the channel to be attached, was: " + this.channel.state, 40000, 400); + callback(new ErrorInfo("option untilAttach requires the channel to be attached, was: " + this.channel.state, 40000, 400)); } } @@ -8608,6 +8767,47 @@ var RealtimePresence = (function() { this.members.startSync(); }; + var _on = RealtimePresence.prototype.on; + var _off = RealtimePresence.prototype.off; + + RealtimePresence.prototype.subscribe = function(/* [event], listener, [callback] */) { + var args = RealtimeChannel.processListenerArgs(arguments); + var event = args[0]; + var listener = args[1]; + var callback = args[2]; + var self = this; + + waitAttached(this.channel, callback, function() { + _on.call(self, event, listener); + }); + } + + RealtimePresence.prototype.unsubscribe = function(/* [event], listener, [callback] */) { + var args = RealtimeChannel.processListenerArgs(arguments); + var event = args[0]; + var listener = args[1]; + var callback = args[2]; + + if(this.channel.state === 'failed') + callback(ErrorInfo.fromValues(RealtimeChannel.invalidStateError)); + + _off.call(this, event, listener); + } + + RealtimePresence.prototype.on = function() { + Logger.deprecated('presence.on', 'presence.subscribe'); + _on.apply(this, arguments); + } + + RealtimePresence.prototype.off = function() { + Logger.deprecated('presence.off', 'presence.unsubscribe'); + _off.apply(this, arguments); + } + + RealtimePresence.prototype.syncComplete = function() { + return !this.members.syncInProgress; + } + function PresenceMap(presence) { EventEmitter.call(this); this.presence = presence; @@ -8791,7 +8991,9 @@ var JSONPTransport = (function() { }; var createRequest = JSONPTransport.prototype.createRequest = function(uri, headers, params, body, requestMode) { - return new Request(undefined, uri, headers, params, body, requestMode, this.timeouts); + /* JSONP requests are used outside the context of a realtime transport, in which case use the default timeouts */ + var timeouts = (this && this.timeouts) || Defaults.TIMEOUTS; + return new Request(undefined, uri, headers, params, body, requestMode, timeouts); }; function Request(id, uri, headers, params, body, requestMode, timeouts) { @@ -8967,7 +9169,7 @@ var XHRRequest = (function() { var createRequest = XHRRequest.createRequest = function(uri, headers, params, body, requestMode) { /* XHR requests are used outside the context of a realtime transport, in which case use the default timeouts */ - var timeouts = (this && this.timeouts) ? this.timeouts : Defaults.TIMEOUTS; + var timeouts = (this && this.timeouts) || Defaults.TIMEOUTS; return xhrSupported ? new XHRRequest(uri, headers, params, body, requestMode, timeouts) : new XDRRequest(uri, headers, params, body, requestMode, timeouts); }; diff --git a/browser/static/ably.min.js b/browser/static/ably.min.js index 7326f752ee..01c1601376 100644 --- a/browser/static/ably.min.js +++ b/browser/static/ably.min.js @@ -1,7 +1,7 @@ /* Copyright 2015, Ably - Ably JavaScript Library v0.8.9 + Ably JavaScript Library v0.8.10 https://github.com/ably/ably-js Ably Realtime Messaging @@ -9,242 +9,248 @@ Released under the Apache Licence v2.0 */ -(function(){var Y=window.Ably=this,y=y||function(d,c){var b={},e=b.lib={},f=e.Base=function(){function a(){}return{extend:function(h){a.prototype=this;var b=new a;h&&b.mixIn(h);b.hasOwnProperty("init")||(b.init=function(){b.$super.init.apply(this,arguments)});b.init.prototype=b;b.$super=this;return b},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var h in a)a.hasOwnProperty(h)&&(this[h]=a[h]);a.hasOwnProperty("toString")&&(this.toString= -a.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),a=e.WordArray=f.extend({init:function(a,h){a=this.words=a||[];this.sigBytes=h!=c?h:4*a.length},toString:function(a){return(a||m).stringify(this)},concat:function(a){var h=this.words,b=a.words,n=this.sigBytes;a=a.sigBytes;this.clamp();if(n%4)for(var e=0;e>>2]|=(b[e>>>2]>>>24-e%4*8&255)<<24-(n+e)%4*8;else if(65535>>2]=b[e>>>2];else h.push.apply(h,b);this.sigBytes+=a;return this}, -clamp:function(){var a=this.words,h=this.sigBytes;a[h>>>2]&=4294967295<<32-h%4*8;a.length=d.ceil(h/4)},clone:function(){var a=f.clone.call(this);a.words=this.words.slice(0);return a},random:function(h){for(var b=[],e=function(a){var h=987654321;return function(){h=36969*(h&65535)+(h>>16)&4294967295;a=18E3*(a&65535)+(a>>16)&4294967295;var b=(h<<16)+a&4294967295,b=b/4294967296+.5;return b*(.5>>2]>>>24-e%4*8&255;b.push((n>>>4).toString(16));b.push((n&15).toString(16))}return b.join("")},parse:function(h){for(var b=h.length,e=[],n=0;n>>3]|=parseInt(h.substr(n,2),16)<<24-n%8*4;return new a.init(e,b/2)}},p=k.Latin1={stringify:function(a){var h=a.words;a=a.sigBytes;for(var b=[],e=0;e>>2]>>>24-e%4*8&255));return b.join("")}, -parse:function(h){for(var b=h.length,e=[],n=0;n>>2]|=(h.charCodeAt(n)&255)<<24-n%4*8;return new a.init(e,b)}},h=k.Utf8={stringify:function(a){try{return decodeURIComponent(escape(p.stringify(a)))}catch(h){throw Error("Malformed UTF-8 data");}},parse:function(a){return p.parse(unescape(encodeURIComponent(a)))}},n=e.BufferedBlockAlgorithm=f.extend({reset:function(){this._data=new a.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+= -a.sigBytes},_process:function(h){var b=this._data,e=b.words,n=b.sigBytes,k=this.blockSize,f=n/(4*k),f=h?d.ceil(f):d.max((f|0)-this._minBufferSize,0);h=f*k;n=d.min(4*h,n);if(h){for(var v=0;vf;)b(e)&&(8>f&&(a[f]=h(d.pow(e,.5))),k[f]=h(d.pow(e,1/3)),f++),e++})();var m=[],b=b.SHA256=f.extend({_doReset:function(){this._hash=new e.init(a.slice(0))},_doProcessBlock:function(a,h){for(var b=this._hash.words,e=b[0],f=b[1],d=b[2],c=b[3],g=b[4],H=b[5],I=b[6],U=b[7],l=0;64>l;l++){if(16>l)m[l]=a[h+l]|0;else{var L=m[l-15],G=m[l-2];m[l]=((L<<25|L>>>7)^(L<<14|L>>>18)^L>>>3)+m[l-7]+((G<<15|G>>>17)^(G<<13|G>>>19)^G>>> -10)+m[l-16]}L=U+((g<<26|g>>>6)^(g<<21|g>>>11)^(g<<7|g>>>25))+(g&H^~g&I)+k[l]+m[l];G=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&f^e&d^f&d);U=I;I=H;H=g;g=c+L|0;c=d;d=f;f=e;e=L+G|0}b[0]=b[0]+e|0;b[1]=b[1]+f|0;b[2]=b[2]+d|0;b[3]=b[3]+c|0;b[4]=b[4]+g|0;b[5]=b[5]+H|0;b[6]=b[6]+I|0;b[7]=b[7]+U|0},_doFinalize:function(){var a=this._data,h=a.words,b=8*this._nDataBytes,e=8*a.sigBytes;h[e>>>5]|=128<<24-e%32;h[(e+64>>>9<<4)+14]=d.floor(b/4294967296);h[(e+64>>>9<<4)+15]=b;a.sigBytes=4*h.length;this._process(); -return this._hash},clone:function(){var a=f.clone.call(this);a._hash=this._hash.clone();return a}});c.SHA256=f._createHelper(b);c.HmacSHA256=f._createHmacHelper(b)})(Math);(function(){var d=y,c=d.enc.Utf8;d.algo.HMAC=d.lib.Base.extend({init:function(b,e){b=this._hasher=new b.init;"string"==typeof e&&(e=c.parse(e));var f=b.blockSize,a=4*f;e.sigBytes>a&&(e=b.finalize(e));e.clamp();for(var k=this._oKey=e.clone(),d=this._iKey=e.clone(),p=k.words,h=d.words,n=0;n>>2]>>>24-k%4*8&255)<<16|(e[k+1>>>2]>>>24-(k+1)%4*8&255)<< -8|e[k+2>>>2]>>>24-(k+2)%4*8&255,c=0;4>c&&k+.75*c>>6*(3-c)&63));if(e=a.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,a=f.charAt(64);a&&(a=b.indexOf(a),-1!=a&&(e=a));for(var a=[],k=0,d=0;d>>6-d%4*2;a[k>>>2]|=(p|h)<<24-k%4*8;k++}return c.create(a,k)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();y.lib.Cipher||function(d){var c= -y,b=c.lib,e=b.Base,f=b.WordArray,a=b.BufferedBlockAlgorithm,k=c.enc.Base64,m=c.algo.EvpKDF,p=b.Cipher=a.extend({cfg:e.extend(),createEncryptor:function(a,h){return this.create(this._ENC_XFORM_MODE,a,h)},createDecryptor:function(a,h){return this.create(this._DEC_XFORM_MODE,a,h)},init:function(a,h,b){this.cfg=this.cfg.extend(b);this._xformMode=a;this._key=h;this.reset()},reset:function(){a.reset.call(this);this._doReset()},process:function(a){this._append(a);return this._process()},finalize:function(a){a&& -this._append(a);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(){return function(a){return{encrypt:function(h,b,e){return("string"==typeof b?Z:t).encrypt(a,h,b,e)},decrypt:function(h,b,e){return("string"==typeof b?Z:t).decrypt(a,h,b,e)}}}}()});b.StreamCipher=p.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var h=c.mode={},n=b.BlockCipherMode=e.extend({createEncryptor:function(a,h){return this.Encryptor.create(a,h)}, -createDecryptor:function(a,h){return this.Decryptor.create(a,h)},init:function(a,h){this._cipher=a;this._iv=h}}),h=h.CBC=function(){function a(h,b,e){var n=this._iv;n?this._iv=d:n=this._prevBlock;for(var f=0;f>>2]&255}};b.BlockCipher=p.extend({cfg:p.cfg.extend({mode:h,padding:v}),reset:function(){p.reset.call(this);var a=this.cfg,h=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var b=a.createEncryptor;else b=a.createDecryptor,this._minBufferSize= -1;this._mode=b.call(a,this,h&&h.words)},_doProcessBlock:function(a,h){this._mode.processBlock(a,h)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var h=this._process(!0)}else h=this._process(!0),a.unpad(h);return h},blockSize:4});var r=b.CipherParams=e.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),h=(c.format={}).OpenSSL={stringify:function(a){var h=a.ciphertext;a= -a.salt;return(a?f.create([1398893684,1701076831]).concat(a).concat(h):h).toString(k)},parse:function(a){a=k.parse(a);var h=a.words;if(1398893684==h[0]&&1701076831==h[1]){var b=f.create(h.slice(2,4));h.splice(0,4);a.sigBytes-=16}return r.create({ciphertext:a,salt:b})}},t=b.SerializableCipher=e.extend({cfg:e.extend({format:h}),encrypt:function(a,h,b,e){e=this.cfg.extend(e);var n=a.createEncryptor(b,e);h=n.finalize(h);n=n.cfg;return r.create({ciphertext:h,key:b,iv:n.iv,algorithm:a,mode:n.mode,padding:n.padding, -blockSize:a.blockSize,formatter:e.format})},decrypt:function(a,h,b,e){e=this.cfg.extend(e);h=this._parse(h,e.format);return a.createDecryptor(b,e).finalize(h.ciphertext)},_parse:function(a,h){return"string"==typeof a?h.parse(a,this):a}}),c=(c.kdf={}).OpenSSL={execute:function(a,h,b,e){e||(e=f.random(8));a=m.create({keySize:h+b}).compute(a,e);b=f.create(a.words.slice(h),4*b);a.sigBytes=4*h;return r.create({key:a,iv:b,salt:e})}},Z=b.PasswordBasedCipher=t.extend({cfg:t.cfg.extend({kdf:c}),encrypt:function(a, -h,b,e){e=this.cfg.extend(e);b=e.kdf.execute(b,a.keySize,a.ivSize);e.iv=b.iv;a=t.encrypt.call(this,a,h,b.key,e);a.mixIn(b);return a},decrypt:function(a,h,b,e){e=this.cfg.extend(e);h=this._parse(h,e.format);b=e.kdf.execute(b,a.keySize,a.ivSize,h.salt);e.iv=b.iv;return t.decrypt.call(this,a,h,b.key,e)}})}();(function(){var d=y,c=d.lib.BlockCipher,b=d.algo,e=[],f=[],a=[],k=[],m=[],p=[],h=[],n=[],v=[],r=[];(function(){for(var b=[],d=0;256>d;d++)b[d]=128>d?d<<1:d<<1^283;for(var c=0,g=0,d=0;256>d;d++){var t= -g^g<<1^g<<2^g<<3^g<<4,t=t>>>8^t&255^99;e[c]=t;f[t]=c;var l=b[c],q=b[l],G=b[q],A=257*b[t]^16843008*t;a[c]=A<<24|A>>>8;k[c]=A<<16|A>>>16;m[c]=A<<8|A>>>24;p[c]=A;A=16843009*G^65537*q^257*l^16843008*c;h[t]=A<<24|A>>>8;n[t]=A<<16|A>>>16;v[t]=A<<8|A>>>24;r[t]=A;c?(c=l^b[b[b[G^l]]],g^=b[b[g]]):c=g=1}})();var t=[0,1,2,4,8,16,32,64,128,27,54],b=b.AES=c.extend({_doReset:function(){for(var a=this._key,b=a.words,f=a.sigBytes/4,a=4*((this._nRounds=f+6)+1),d=this._keySchedule=[],k=0;k>>24]<<24|e[c>>>16&255]<<16|e[c>>>8&255]<<8|e[c&255]):(c=c<<8|c>>>24,c=e[c>>>24]<<24|e[c>>>16&255]<<16|e[c>>>8&255]<<8|e[c&255],c^=t[k/f|0]<<24);d[k]=d[k-f]^c}b=this._invKeySchedule=[];for(f=0;ff||4>=k?c:h[e[c>>>24]]^n[e[c>>>16&255]]^v[e[c>>>8&255]]^r[e[c&255]]},encryptBlock:function(h,b){this._doCryptBlock(h,b,this._keySchedule,a,k,m,p,e)},decryptBlock:function(a,b){var e=a[b+1];a[b+1]=a[b+3];a[b+3]=e;this._doCryptBlock(a,b,this._invKeySchedule, -h,n,v,r,f);e=a[b+1];a[b+1]=a[b+3];a[b+3]=e},_doCryptBlock:function(a,h,b,e,n,f,k,d){for(var c=this._nRounds,v=a[h]^b[0],g=a[h+1]^b[1],m=a[h+2]^b[2],r=a[h+3]^b[3],p=4,t=1;t>>24]^n[g>>>16&255]^f[m>>>8&255]^k[r&255]^b[p++],q=e[g>>>24]^n[m>>>16&255]^f[r>>>8&255]^k[v&255]^b[p++],u=e[m>>>24]^n[r>>>16&255]^f[v>>>8&255]^k[g&255]^b[p++],r=e[r>>>24]^n[v>>>16&255]^f[g>>>8&255]^k[m&255]^b[p++],v=l,g=q,m=u;l=(d[v>>>24]<<24|d[g>>>16&255]<<16|d[m>>>8&255]<<8|d[r&255])^b[p++];q=(d[g>>>24]<<24|d[m>>> -16&255]<<16|d[r>>>8&255]<<8|d[v&255])^b[p++];u=(d[m>>>24]<<24|d[r>>>16&255]<<16|d[v>>>8&255]<<8|d[g&255])^b[p++];r=(d[r>>>24]<<24|d[v>>>16&255]<<16|d[g>>>8&255]<<8|d[m&255])^b[p++];a[h]=l;a[h+1]=q;a[h+2]=u;a[h+3]=r},keySize:8});d.AES=c._createHelper(b)})();(function(){if("undefined"!==typeof ArrayBuffer){var d=y.lib.WordArray,c=d.init;(d.init=function(b){if(b instanceof ArrayBuffer)b=new Uint8Array(b);else if(b instanceof Int8Array||"undefined"!==typeof Uint8ClampedArray&&b instanceof Uint8ClampedArray|| -b instanceof Int16Array||b instanceof Uint16Array||b instanceof Int32Array||b instanceof Uint32Array||"undefined"!==typeof Float32Array&&b instanceof Float32Array||"undefined"!==typeof Float64Array&&b instanceof Float64Array)b=new Uint8Array(b.buffer,b.byteOffset,b.byteLength);if(b instanceof Uint8Array){for(var e=b.byteLength,d=[],a=0;a>>2]|=b[a]<<24-a%4*8;c.call(this,d,e)}else c.apply(this,arguments)}).prototype=d}})();var N=function(){function d(){}function g(){this.iv=this.key=this.mode= -this.keyLength=this.algorithm=null}function b(a){this.algorithm=a.algorithm+"-"+String(a.keyLength)+"-"+a.mode;var b=this.cjsAlgorithm=a.algorithm.toUpperCase().replace(/-\d+$/,""),e=this.key=s.toWordArray(a.key);a=this.iv=s.toWordArray(a.iv);this.encryptCipher=y.algo[b].createEncryptor(e,{iv:a});this.blockLengthWords=a.words.length}var e=y.lib.WordArray,f,a=window.crypto||window.msCrypto;if(window.Uint32Array&&a&&a.getRandomValues){var k=new Uint32Array(4);f=function(h,b){var e=h/4,e=4==e?k:new Uint32Array(e); -a.getRandomValues(e);b(null,s.toWordArray(e))}}else f=function(a,b){console.log("Ably.Crypto.generateRandom(): WARNING: using insecure Math.random() to generate key or iv; see http://ably.io/documentation for how to fix this");for(var d=a/4,k=Array(d),f=0;f>>2]|=(l[e>>>2]>>>24-e%4*8&255)<<24-(a+e)%4*8;else if(65535>>2]=l[e>>>2];else c.push.apply(c,l);this.sigBytes+=f;return this}, +clamp:function(){var f=this.words,c=this.sigBytes;f[c>>>2]&=4294967295<<32-c%4*8;f.length=d.ceil(c/4)},clone:function(){var f=c.clone.call(this);f.words=this.words.slice(0);return f},random:function(f){for(var c=[],l=function(f){var c=987654321;return function(){c=36969*(c&65535)+(c>>16)&4294967295;f=18E3*(f&65535)+(f>>16)&4294967295;var l=(c<<16)+f&4294967295,l=l/4294967296+.5;return l*(.5>>2]>>>24-a%4*8&255;l.push((e>>>4).toString(16));l.push((e&15).toString(16))}return l.join("")},parse:function(f){for(var c=f.length,l=[],a=0;a>>3]|=parseInt(f.substr(a,2),16)<<24-a%8*4;return new h.init(l,c/2)}},p=n.Latin1={stringify:function(f){var c=f.words;f=f.sigBytes;for(var l=[],a=0;a>>2]>>>24-a%4*8&255));return l.join("")}, +parse:function(f){for(var c=f.length,l=[],a=0;a>>2]|=(f.charCodeAt(a)&255)<<24-a%4*8;return new h.init(l,c)}},f=n.Utf8={stringify:function(f){try{return decodeURIComponent(escape(p.stringify(f)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(f){return p.parse(unescape(encodeURIComponent(f)))}},l=e.BufferedBlockAlgorithm=c.extend({reset:function(){this._data=new h.init;this._nDataBytes=0},_append:function(c){"string"==typeof c&&(c=f.parse(c));this._data.concat(c);this._nDataBytes+= +c.sigBytes},_process:function(f){var c=this._data,l=c.words,a=c.sigBytes,e=this.blockSize,s=a/(4*e),s=f?d.ceil(s):d.max((s|0)-this._minBufferSize,0);f=s*e;a=d.min(4*f,a);if(f){for(var b=0;ba;)c(l)&&(8>a&&(h[a]=f(d.pow(l,.5))),n[a]=f(d.pow(l,1/3)),a++),l++})();var k=[],a=a.SHA256=c.extend({_doReset:function(){this._hash=new e.init(h.slice(0))},_doProcessBlock:function(c,f){for(var l=this._hash.words,a=l[0],e=l[1],h=l[2],d=l[3],b=l[4],g=l[5],H=l[6],V=l[7],m=0;64>m;m++){if(16>m)k[m]=c[f+m]|0;else{var K=k[m-15],G=k[m-2];k[m]=((K<<25|K>>>7)^(K<<14|K>>>18)^K>>>3)+k[m-7]+((G<<15|G>>>17)^(G<<13|G>>>19)^G>>> +10)+k[m-16]}K=V+((b<<26|b>>>6)^(b<<21|b>>>11)^(b<<7|b>>>25))+(b&g^~b&H)+n[m]+k[m];G=((a<<30|a>>>2)^(a<<19|a>>>13)^(a<<10|a>>>22))+(a&e^a&h^e&h);V=H;H=g;g=b;b=d+K|0;d=h;h=e;e=a;a=K+G|0}l[0]=l[0]+a|0;l[1]=l[1]+e|0;l[2]=l[2]+h|0;l[3]=l[3]+d|0;l[4]=l[4]+b|0;l[5]=l[5]+g|0;l[6]=l[6]+H|0;l[7]=l[7]+V|0},_doFinalize:function(){var c=this._data,f=c.words,l=8*this._nDataBytes,a=8*c.sigBytes;f[a>>>5]|=128<<24-a%32;f[(a+64>>>9<<4)+14]=d.floor(l/4294967296);f[(a+64>>>9<<4)+15]=l;c.sigBytes=4*f.length;this._process(); +return this._hash},clone:function(){var a=c.clone.call(this);a._hash=this._hash.clone();return a}});b.SHA256=c._createHelper(a);b.HmacSHA256=c._createHmacHelper(a)})(Math);(function(){var d=y,b=d.enc.Utf8;d.algo.HMAC=d.lib.Base.extend({init:function(a,e){a=this._hasher=new a.init;"string"==typeof e&&(e=b.parse(e));var c=a.blockSize,h=4*c;e.sigBytes>h&&(e=a.finalize(e));e.clamp();for(var d=this._oKey=e.clone(),k=this._iKey=e.clone(),p=d.words,f=k.words,l=0;l>>2]>>>24-d%4*8&255)<<16|(e[d+1>>>2]>>>24-(d+1)%4*8&255)<< +8|e[d+2>>>2]>>>24-(d+2)%4*8&255,g=0;4>g&&d+.75*g>>6*(3-g)&63));if(e=h.charAt(64))for(;a.length%4;)a.push(e);return a.join("")},parse:function(a){var e=a.length,c=this._map,h=c.charAt(64);h&&(h=a.indexOf(h),-1!=h&&(e=h));for(var h=[],d=0,k=0;k>>6-k%4*2;h[d>>>2]|=(p|f)<<24-d%4*8;d++}return b.create(h,d)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();y.lib.Cipher||function(d){var b= +y,a=b.lib,e=a.Base,c=a.WordArray,h=a.BufferedBlockAlgorithm,n=b.enc.Base64,k=b.algo.EvpKDF,p=a.Cipher=h.extend({cfg:e.extend(),createEncryptor:function(f,c){return this.create(this._ENC_XFORM_MODE,f,c)},createDecryptor:function(f,c){return this.create(this._DEC_XFORM_MODE,f,c)},init:function(f,c,a){this.cfg=this.cfg.extend(a);this._xformMode=f;this._key=c;this.reset()},reset:function(){h.reset.call(this);this._doReset()},process:function(f){this._append(f);return this._process()},finalize:function(f){f&& +this._append(f);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(){return function(f){return{encrypt:function(c,a,l){return("string"==typeof a?$:u).encrypt(f,c,a,l)},decrypt:function(c,a,l){return("string"==typeof a?$:u).decrypt(f,c,a,l)}}}}()});a.StreamCipher=p.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var f=b.mode={},l=a.BlockCipherMode=e.extend({createEncryptor:function(f,c){return this.Encryptor.create(f,c)}, +createDecryptor:function(f,c){return this.Decryptor.create(f,c)},init:function(f,c){this._cipher=f;this._iv=c}}),f=f.CBC=function(){function f(c,a,l){var e=this._iv;e?this._iv=d:e=this._prevBlock;for(var h=0;h>>2]&255}};a.BlockCipher=p.extend({cfg:p.cfg.extend({mode:f,padding:s}),reset:function(){p.reset.call(this);var f=this.cfg,c=f.iv,f=f.mode;if(this._xformMode==this._ENC_XFORM_MODE)var a=f.createEncryptor;else a=f.createDecryptor,this._minBufferSize= +1;this._mode=a.call(f,this,c&&c.words)},_doProcessBlock:function(f,c){this._mode.processBlock(f,c)},_doFinalize:function(){var f=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){f.pad(this._data,this.blockSize);var c=this._process(!0)}else c=this._process(!0),f.unpad(c);return c},blockSize:4});var v=a.CipherParams=e.extend({init:function(f){this.mixIn(f)},toString:function(f){return(f||this.formatter).stringify(this)}}),f=(b.format={}).OpenSSL={stringify:function(f){var a=f.ciphertext;f= +f.salt;return(f?c.create([1398893684,1701076831]).concat(f).concat(a):a).toString(n)},parse:function(f){f=n.parse(f);var a=f.words;if(1398893684==a[0]&&1701076831==a[1]){var l=c.create(a.slice(2,4));a.splice(0,4);f.sigBytes-=16}return v.create({ciphertext:f,salt:l})}},u=a.SerializableCipher=e.extend({cfg:e.extend({format:f}),encrypt:function(f,c,a,l){l=this.cfg.extend(l);var e=f.createEncryptor(a,l);c=e.finalize(c);e=e.cfg;return v.create({ciphertext:c,key:a,iv:e.iv,algorithm:f,mode:e.mode,padding:e.padding, +blockSize:f.blockSize,formatter:l.format})},decrypt:function(f,c,a,l){l=this.cfg.extend(l);c=this._parse(c,l.format);return f.createDecryptor(a,l).finalize(c.ciphertext)},_parse:function(f,c){return"string"==typeof f?c.parse(f,this):f}}),b=(b.kdf={}).OpenSSL={execute:function(f,a,l,e){e||(e=c.random(8));f=k.create({keySize:a+l}).compute(f,e);l=c.create(f.words.slice(a),4*l);f.sigBytes=4*a;return v.create({key:f,iv:l,salt:e})}},$=a.PasswordBasedCipher=u.extend({cfg:u.cfg.extend({kdf:b}),encrypt:function(f, +c,a,l){l=this.cfg.extend(l);a=l.kdf.execute(a,f.keySize,f.ivSize);l.iv=a.iv;f=u.encrypt.call(this,f,c,a.key,l);f.mixIn(a);return f},decrypt:function(f,c,a,l){l=this.cfg.extend(l);c=this._parse(c,l.format);a=l.kdf.execute(a,f.keySize,f.ivSize,c.salt);l.iv=a.iv;return u.decrypt.call(this,f,c,a.key,l)}})}();(function(){var d=y,b=d.lib.BlockCipher,a=d.algo,e=[],c=[],h=[],n=[],k=[],p=[],f=[],l=[],s=[],v=[];(function(){for(var a=[],d=0;256>d;d++)a[d]=128>d?d<<1:d<<1^283;for(var b=0,g=0,d=0;256>d;d++){var u= +g^g<<1^g<<2^g<<3^g<<4,u=u>>>8^u&255^99;e[b]=u;c[u]=b;var m=a[b],q=a[m],G=a[q],A=257*a[u]^16843008*u;h[b]=A<<24|A>>>8;n[b]=A<<16|A>>>16;k[b]=A<<8|A>>>24;p[b]=A;A=16843009*G^65537*q^257*m^16843008*b;f[u]=A<<24|A>>>8;l[u]=A<<16|A>>>16;s[u]=A<<8|A>>>24;v[u]=A;b?(b=m^a[a[a[G^m]]],g^=a[a[g]]):b=g=1}})();var u=[0,1,2,4,8,16,32,64,128,27,54],a=a.AES=b.extend({_doReset:function(){for(var c=this._key,a=c.words,h=c.sigBytes/4,c=4*((this._nRounds=h+6)+1),d=this._keySchedule=[],b=0;b>>24]<<24|e[g>>>16&255]<<16|e[g>>>8&255]<<8|e[g&255]):(g=g<<8|g>>>24,g=e[g>>>24]<<24|e[g>>>16&255]<<16|e[g>>>8&255]<<8|e[g&255],g^=u[b/h|0]<<24);d[b]=d[b-h]^g}a=this._invKeySchedule=[];for(h=0;hh||4>=b?g:f[e[g>>>24]]^l[e[g>>>16&255]]^s[e[g>>>8&255]]^v[e[g&255]]},encryptBlock:function(f,c){this._doCryptBlock(f,c,this._keySchedule,h,n,k,p,e)},decryptBlock:function(a,e){var h=a[e+1];a[e+1]=a[e+3];a[e+3]=h;this._doCryptBlock(a,e,this._invKeySchedule, +f,l,s,v,c);h=a[e+1];a[e+1]=a[e+3];a[e+3]=h},_doCryptBlock:function(f,c,a,l,e,h,d,b){for(var s=this._nRounds,g=f[c]^a[0],n=f[c+1]^a[1],p=f[c+2]^a[2],k=f[c+3]^a[3],v=4,u=1;u>>24]^e[n>>>16&255]^h[p>>>8&255]^d[k&255]^a[v++],q=l[n>>>24]^e[p>>>16&255]^h[k>>>8&255]^d[g&255]^a[v++],r=l[p>>>24]^e[k>>>16&255]^h[g>>>8&255]^d[n&255]^a[v++],k=l[k>>>24]^e[g>>>16&255]^h[n>>>8&255]^d[p&255]^a[v++],g=m,n=q,p=r;m=(b[g>>>24]<<24|b[n>>>16&255]<<16|b[p>>>8&255]<<8|b[k&255])^a[v++];q=(b[n>>>24]<<24|b[p>>> +16&255]<<16|b[k>>>8&255]<<8|b[g&255])^a[v++];r=(b[p>>>24]<<24|b[k>>>16&255]<<16|b[g>>>8&255]<<8|b[n&255])^a[v++];k=(b[k>>>24]<<24|b[g>>>16&255]<<16|b[n>>>8&255]<<8|b[p&255])^a[v++];f[c]=m;f[c+1]=q;f[c+2]=r;f[c+3]=k},keySize:8});d.AES=b._createHelper(a)})();(function(){if("undefined"!==typeof ArrayBuffer){var d=y.lib.WordArray,b=d.init;(d.init=function(a){if(a instanceof ArrayBuffer)a=new Uint8Array(a);else if(a instanceof Int8Array||"undefined"!==typeof Uint8ClampedArray&&a instanceof Uint8ClampedArray|| +a instanceof Int16Array||a instanceof Uint16Array||a instanceof Int32Array||a instanceof Uint32Array||"undefined"!==typeof Float32Array&&a instanceof Float32Array||"undefined"!==typeof Float64Array&&a instanceof Float64Array)a=new Uint8Array(a.buffer,a.byteOffset,a.byteLength);if(a instanceof Uint8Array){for(var e=a.byteLength,c=[],h=0;h>>2]|=a[h]<<24-h%4*8;b.call(this,c,e)}else b.apply(this,arguments)}).prototype=d}})();var M=function(){function d(){}function g(){this.iv=this.key=this.mode= +this.keyLength=this.algorithm=null}function a(f){this.algorithm=f.algorithm+"-"+String(f.keyLength)+"-"+f.mode;var c=this.cjsAlgorithm=f.algorithm.toUpperCase().replace(/-\d+$/,""),a=this.key=t.toWordArray(f.key);f=this.iv=t.toWordArray(f.iv);this.encryptCipher=y.algo[c].createEncryptor(a,{iv:f});this.blockLengthWords=f.words.length}var e=y.lib.WordArray,c,h=window.crypto||window.msCrypto;if(window.Uint32Array&&h&&h.getRandomValues){var n=new Uint32Array(4);c=function(f,c){var a=f/4,a=4==a?n:new Uint32Array(a); +h.getRandomValues(a);c(null,t.toWordArray(a))}}else c=function(f,c){console.log("Ably.Crypto.generateRandom(): WARNING: using insecure Math.random() to generate key or iv; see http://ably.io/documentation for how to fix this");for(var a=f/4,h=Array(a),d=0;d>>2]>>>24-p%4*8&255;return b}throw Error("BufferUtils.toArrayBuffer expected a buffer");};b.toWordArray=function(a){return d(a)? -a:e.create(a)};b.base64Encode=function(a){if(c(a)){var b="";a=new Uint8Array(a);for(var e=a.byteLength,f=e%3,e=e-f,h,n,v,r,t=0;t>18,n=(r&258048)>>12,v=(r&4032)>>6,r&=63,b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[h]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[n]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[v]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[r]; -1==f?(r=a[e],b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(r&252)>>2]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(r&3)<<4]+"=="):2==f&&(r=a[e]<<8|a[e+1],b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(r&64512)>>10]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(r&1008)>>4]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(r&15)<<2]+"=");return b}if(d(a))return y.enc.Base64.stringify(a)}; -b.base64Decode=function(a){if(f){a=window.atob(a);for(var b=a.length,e=new Uint8Array(b),d=0;d>2,m=(m&3)<<4|p>>4,v=(p&15)<<2|h>>6,r=h&63;isNaN(p)?v=r=64:isNaN(h)&&(r=64);a.append(k.charAt(n)+k.charAt(m)+k.charAt(v)+k.charAt(r))}return a.toString()},decode:function(e){var a=new d;for(e=new b(e);e.moveNext();){var c=e.current;if(128>c)a.append(String.fromCharCode(c));else if(191c){e.moveNext();var g=e.current;a.append(String.fromCharCode((c& -31)<<6|g&63))}else e.moveNext(),g=e.current,e.moveNext(),a.append(String.fromCharCode((c&15)<<12|(g&63)<<6|e.current&63))}return a.toString()}};c.prototype={current:Number.NaN,moveNext:function(){if(0=this._input.length-1)return this.current=Number.NaN,!1;var b=this._input.charCodeAt(++this._index);13==b&&10==this._input.charCodeAt(this._index+1)&&(b=10,this._index+=2);128>b?this.current=b:(127b?this.current= -b>>6|192:(this.current=b>>12|224,this._buffer.push(b>>6&63|128)),this._buffer.push(b&63|128));return!0}};b.prototype={current:64,moveNext:function(){if(0=this._input.length-1)return this.current=64,!1;var b=e.codex.indexOf(this._input.charAt(++this._index)),a=e.codex.indexOf(this._input.charAt(++this._index)),d=e.codex.indexOf(this._input.charAt(++this._index)),c=e.codex.indexOf(this._input.charAt(++this._index)),g=(d& -3)<<6|c;this.current=b<<2|a>>4;64!=d&&this._buffer.push((a&15)<<4|d>>2);64!=c&&this._buffer.push(g);return!0}};return e}(),P=function(){function d(){}d.addListener=function(d,b,e){d.addEventListener?d.addEventListener(b,e,!1):d.attachEvent("on"+b,function(){e.apply(d,arguments)})};d.removeListener=function(d,b,e){d.removeEventListener?d.removeEventListener(b,e,!1):d.detachEvent("on"+b,function(){e.apply(d,arguments)})};d.addMessageListener=function(c,b){d.addListener(c,"message",b)};d.removeMessageListener= -function(c,b){d.removeListener(c,"message",b)};d.addUnloadListener=function(c){d.addListener(window,"unload",c)};return d}();(function(d){this.msgpack=d()}).call(this,function(){function d(a,b,e){for(var d=0,c=e.length;df)a.setUint8(b++,f>>>0&127|0);else if(2048>f)a.setUint8(b++,f>>>6&31|192),a.setUint8(b++,f>>>0&63|128);else if(65536>f)a.setUint8(b++,f>>>12&15|224),a.setUint8(b++,f>>>6&63|128),a.setUint8(b++,f>>>0&63|128);else if(1114112>f)a.setUint8(b++,f>>> -18&7|240),a.setUint8(b++,f>>>12&63|128),a.setUint8(b++,f>>>6&63|128),a.setUint8(b++,f>>>0&63|128);else throw Error("bad codepoint "+f);}}function c(a,b,e){var d="",f=b;for(b+=e;fc)b+=1;else if(2048>c)b+=2;else if(65536>c)b+=3;else if(1114112>c)b+=4;else throw Error("bad codepoint "+c);}return b}function e(a,b){this.offset=b||0;this.view=a}function f(a,b){return Object.keys(a).filter(function(e){e=a[e];return(!b||void 0!==e&&null!==e)&&("function"!==typeof e||!!e.toJSON)})}function a(h,e,c, -k){var m=typeof h;if("string"===m){var g=b(h);if(32>g)return e.setUint8(c,g|160),d(e,c+1,h),1+g;if(256>g)return e.setUint8(c,217),e.setUint8(c+1,g),d(e,c+2,h),2+g;if(65536>g)return e.setUint8(c,218),e.setUint16(c+1,g),d(e,c+3,h),3+g;if(4294967296>g)return e.setUint8(c,219),e.setUint32(c+1,g),d(e,c+5,h),5+g}if(h instanceof ArrayBuffer){g=h.byteLength;if(256>g)return e.setUint8(c,196),e.setUint8(c+1,g),(new Uint8Array(e.buffer)).set(new Uint8Array(h),c+2),2+g;if(65536>g)return e.setUint8(c,197),e.setUint16(c+ -1,g),(new Uint8Array(e.buffer)).set(new Uint8Array(h),c+3),3+g;if(4294967296>g)return e.setUint8(c,198),e.setUint32(c+1,g),(new Uint8Array(e.buffer)).set(new Uint8Array(h),c+5),5+g}if("number"===m){if(Math.floor(h)!==h)return e.setUint8(c,203),e.setFloat64(c+1,h),9;if(0<=h){if(128>h)return e.setUint8(c,h),1;if(256>h)return e.setUint8(c,204),e.setUint8(c+1,h),2;if(65536>h)return e.setUint8(c,205),e.setUint16(c+1,h),3;if(4294967296>h)return e.setUint8(c,206),e.setUint32(c+1,h),5;if(1.8446744073709552E19> -h)return e.setUint8(c,207),c+=1,1.8446744073709552E19>h?(e.setUint32(c,Math.floor(h*p)),e.setInt32(c+4,h&-1)):(e.setUint32(c,4294967295),e.setUint32(c+4,4294967295)),9;throw Error("Number too big 0x"+h.toString(16));}if(-32<=h)return e.setInt8(c,h),1;if(-128<=h)return e.setUint8(c,208),e.setInt8(c+1,h),2;if(-32768<=h)return e.setUint8(c,209),e.setInt16(c+1,h),3;if(-2147483648<=h)return e.setUint8(c,210),e.setInt32(c+1,h),5;if(-9223372036854775808<=h)return e.setUint8(c,211),c+=1,0x7fffffffffffffff> -h?(e.setInt32(c,Math.floor(h*p)),e.setInt32(c+4,h&-1)):(e.setUint32(c,2147483647),e.setUint32(c+4,2147483647)),9;throw Error("Number too small -0x"+(-h).toString(16).substr(1));}if("undefined"===m){if(k)return 0;e.setUint8(c,212);e.setUint8(c+1,0);e.setUint8(c+2,0);return 3}if(null===h){if(k)return 0;e.setUint8(c,192);return 1}if("boolean"===m)return e.setUint8(c,h?195:194),1;if("function"===typeof h.toJSON)return a(h.toJSON(),e,c,k);if("object"===m){var m=0,l=Array.isArray(h);if(l)g=h.length;else var H= -f(h,k),g=H.length;16>g?(e.setUint8(c,g|(l?144:128)),m=1):65536>g?(e.setUint8(c,l?220:222),e.setUint16(c+1,g),m=3):4294967296>g&&(e.setUint8(c,l?221:223),e.setUint32(c+1,g),m=5);if(l)for(l=0;ld)return 1+d;if(256>d)return 2+d;if(65536>d)return 3+d;if(4294967296>d)return 5+d}if(a instanceof -ArrayBuffer){d=a.byteLength;if(256>d)return 2+d;if(65536>d)return 3+d;if(4294967296>d)return 5+d}if("number"===c){if(Math.floor(a)!==a)return 9;if(0<=a){if(128>a)return 1;if(256>a)return 2;if(65536>a)return 3;if(4294967296>a)return 5;if(1.8446744073709552E19>a)return 9;throw Error("Number too big 0x"+a.toString(16));}if(-32<=a)return 1;if(-128<=a)return 2;if(-32768<=a)return 3;if(-2147483648<=a)return 5;if(-9223372036854775808<=a)return 9;throw Error("Number too small -0x"+a.toString(16).substr(1)); -}if("boolean"===c)return 1;if(null===a)return e?0:1;if(void 0===a)return e?0:3;if("function"===typeof a.toJSON)return k(a.toJSON(),e);if("object"===c){c=0;if(Array.isArray(a))for(var d=a.length,g=0;gd)return 1+c;if(65536>d)return 3+c;if(4294967296>d)return 5+c;throw Error("Array or object too long 0x"+d.toString(16));}if("function"===c)return 0;throw Error("Unknown type "+c);}var m={inspect:function(a){if(void 0=== -a)return"undefined";var e,b;a instanceof ArrayBuffer?(b="ArrayBuffer",e=new DataView(a)):a instanceof DataView&&(b="DataView",e=a);if(!e)return JSON.stringify(a);for(var d=[],c=0;c"}};m.utf8Write=d;m.utf8Read=c;m.utf8ByteCount=b;m.encode=function(e,b){var d=k(e,b);if(0!=d){var d=new ArrayBuffer(d),c=new DataView(d);a(e,c,0,b);return d}};m.decode=function(a){var b= -new DataView(a),b=new e(b),d=b.parse();if(b.offset!==a.byteLength)throw Error(a.byteLength-b.offset+" trailing bytes");return d};var p=1/4294967296;e.prototype.map=function(a){for(var b={},e=0;e=a?null:d.slice(0,a).join("/"),b.data=m}}};d.fromResponseBody=function(b,e,f,a){f&&(b="msgpack"==f?g.decode(b):JSON.parse(String(b)));for(f=0;fa.code:500>a.statusCode};d.prototype.getConnectParams=function(a){a=a?l.prototypicalClone(a):{};var c=this.options;switch(this.mode){case "upgrade":a.upgrade=this.connectionKey; -break;case "resume":a.resume=this.connectionKey;void 0!==this.connectionSerial&&(a.connection_serial=this.connectionSerial);break;case "recover":if(!0===c.recover){var e=b("ably-connection-key"),d=b("ably-connection-serial");null!==e&&null!==d&&(a.recover=e,a.connection_serial=d)}else if(e=c.recover.match(/^(\w+):(\w+)$/))a.recover=e[1],a.connection_serial=e[2]}void 0!==c.clientId&&(a.clientId=c.clientId);!1===c.echoMessages&&(a.echo="false");void 0!==this.format&&(a.format=this.format);void 0!== -this.stream&&(a.stream=this.stream);return a};l.inherits(g,x);g.httpTransports={};g.transports={};g.prototype.chooseTransport=function(a){c.logAction(c.LOG_MAJOR,"ConnectionManager.chooseTransport()","");if(this.activeProtocol)c.logAction(c.LOG_MINOR,"ConnectionManager.chooseTransport()","Transport already established"),a(null);else{var b=this.connectionKey?"resume":this.options.recover?"recover":"clean",e=new d(this.options,null,b,this.connectionKey,this.connectionSerial);c.logAction(c.LOG_MINOR, -"ConnectionManager.chooseTransport()","Transport recovery mode = "+b+("clean"==b?"":"; connectionKey = "+this.connectionKey+"; connectionSerial = "+this.connectionSerial));var k=this;this.httpTransports.length?this.chooseHttpTransport(e,function(b,f){if(b)c.logAction(c.LOG_ERROR,"ConnectionManager.chooseTransport()","Unexpected error establishing transport; err = "+l.inspectError(b)),a(b);else if(c.logAction(c.LOG_MINOR,"ConnectionManager.chooseTransport()","Establishing http transport: "+f),a(null, -f),k.upgradeTransports.length)f.once("connected",function(a,b){l.nextTick(function(){c.logAction(c.LOG_MAJOR,"ConnectionManager.chooseTransport()","upgrading ... connectionKey = "+b);e=new d(k.options,e.host,"upgrade",b);k.chooseTransportForHost(e,k.upgradeTransports.slice(),m)})})}):(e.host=this.httpHosts[0],c.logAction(c.LOG_MINOR,"ConnectionManager.chooseTransport()","No http transports available; ignoring fallback hosts"),this.chooseTransportForHost(e,k.transports.slice(),a))}};g.prototype.chooseTransportForHost= -function(a,b,e){var d=b.shift();if(d){var k=this;c.logAction(c.LOG_MICRO,"ConnectionManager.chooseTransportForHost()","trying "+d);g.transports[d].tryConnect(this,this.realtime.auth,a,function(f,g){var m=k.state;m==k.states.closing||m==k.states.closed||m==k.states.failed?(c.logAction(c.LOG_MINOR,"ConnectionManager.chooseTransportForHost()","connection closing"),g&&(c.logAction(c.LOG_MINOR,"ConnectionManager.chooseTransportForHost()","closing transport = "+g),g.close()),e(new u("Connection already closed", -400,80017))):f?p(f)?e(f):k.chooseTransportForHost(a,b,e):(c.logAction(c.LOG_MICRO,"ConnectionManager.chooseTransportForHost()","transport "+d+" connecting"),k.setTransportPending(g,a.mode),e(null,g))})}else e(new u("Unable to connect (no available transport)",8E4,404))};g.prototype.chooseHttpTransport=function(a,b){function c(){e.length?g.httpTransports[k.httpTransports[0]].checkConnectivity(function(d,f){d?b(d):f?(a.host=l.arrRandomElement(e),k.chooseTransportForHost(a,k.httpTransports.slice(),function(a, -e){a?p(a)?b(a):c():b(null,e)})):b(new u("Unable to connect (network unreachable)",8E4,404))}):b(new u("Unable to connect (no available host)",8E4,404))}var e=this.httpHosts.slice(),d=e.shift();if(d){a.host=d;var k=this;this.chooseTransportForHost(a,this.httpTransports.slice(),function(a,e){a?p(a)?b(a):c():b(null,e)})}else b(new u("Unable to connect (no available host)",8E4,404))};g.prototype.setTransportPending=function(a,b){c.logAction(c.LOG_MINOR,"ConnectionManager.setTransportPending()","transport = "+ -a+"; mode = "+b);this.pendingTransports.push(a);var e=this;a.on("connected",function(c,d,k,f,g){"upgrade"==b&&e.activeProtocol?e.scheduleTransportActivation(a):e.activateTransport(a,d,k,f,g)});for(var d=function(b){return function(c){e.deactivateTransport(a,b,c)}},k=["disconnected","closed","failed"],f=0;fg||300<=g?(e=e&&e.error,e||(e=Error(String(res)),e.statusCode=g),b(e)):b(null,e,f,!0))}}}function e(a){var b=[];if(a)for(var c in a)b.push(c+"="+a[c]);return b.join("&")}function f(a,b,d,h){return function(f,g,l,t){f?c.logAction(c.LOG_MICRO,"Resource."+b+"()","Received Error; "+ -(d+(h?"?":"")+e(h))+"; Error: "+JSON.stringify(f)):c.logAction(c.LOG_MICRO,"Resource."+b+"()","Received; "+(d+(h?"?":"")+e(h))+"; Headers: "+e(l)+"; Body: "+(s.isBuffer(g)?g.toString():g));a&&a(f,g,l,t)}}var a="object"==typeof window?window.Ably.msgpack:(void 0)("msgpack-js");d.get=function(a,d,p,h,n,l){function r(b,f){c.shouldLog(c.LOG_MICRO)&&c.logAction(c.LOG_MICRO,"Resource.get()","Sending; "+(d+(f?"?":"")+e(f)));z.get(a,d,b,f,function(b,c,e,d){b&&40140==b.code?a.auth.authorise(null,{force:!0}, -function(b){b?l(b):g(a,p,h,l,r)}):l(b,c,e,d)})}c.shouldLog(c.LOG_MICRO)&&(l=f(l,"get",d,h));n&&(l=l&&b(l,n),(h=h||{}).envelope=n);g(a,p,h,l,r)};d.post=function(d,m,p,h,n,l,r){function t(b,f){if(c.shouldLog(c.LOG_MICRO)){var l=p;if(0<(b["content-type"]||"").indexOf("msgpack"))try{p=a.decode(p)}catch(v){c.logAction(c.LOG_MICRO,"Resource.post()","Sending MsgPack Decoding Error: "+JSON.stringify(v))}c.logAction(c.LOG_MICRO,"Resource.post()","Sending; "+(m+(f?"?":"")+e(f))+"; Body: "+l)}z.post(d,m,b,p, -f,function(a,b,c,e){a&&40140==a.code?d.auth.authorise(null,{force:!0},function(a){a?r(a):g(d,h,n,r,t)}):r(a,b,c,e)})}c.shouldLog(c.LOG_MICRO)&&(r=f(r,"post",m,n));l&&(r=b(r,l),n.envelope=l);g(d,h,n,r,t)};return d}(),R=function(){function d(b,c,d,a,k){this.rest=b;this.path=c;this.headers=d;this.envelope=a;this.bodyHandler=k}function g(b,c,d){this.resource=b;this.items=c;var a=this;"first"in d&&(this.first=function(b){a.get(d.first,b)});"current"in d&&(this.current=function(b){a.get(d.current,b)}); -"next"in d&&(this.next=function(b){a.get(d.next,b)})}d.prototype.get=function(b,c){var d=this;X.get(d.rest,d.path,d.headers,b,d.envelope,function(a,b,g,p){d.handlePage(a,b,g,p,c)})};d.prototype.handlePage=function(b,e,d,a,k){if(b)c.logAction(c.LOG_ERROR,"PaginatedResource.get()","Unexpected error getting resource: err = "+JSON.stringify(b)),k(b);else{var m,p,h;try{m=this.bodyHandler(e,d,a)}catch(n){k(n);return}if(d&&(p=d.Link||d.link)){b=p;"string"==typeof b&&(b=b.split(","));e={};for(d=0;d;\s*rel="(\w+)"$/))&&(p=(p=a[1].match(/^\.\/(\w+)\?(.*)$/))&&l.parseQueryString(p[2]))&&(e[a[2]]=p);h=e}k(null,new g(this,m,h))}};g.prototype.get=function(b,c){var d=this.resource;X.get(d.rest,d.path,d.headers,b,d.envelope,function(a,b,g,p){d.handlePage(a,b,g,p,c)})};return d}(),ga=function(){function d(){}function g(a){if(!a)return"";"string"==typeof a&&(a=JSON.parse(a));var b={},c=l.keysArray(a,!0);if(!c)return"";c.sort();for(var e=0;ethis.getTimestamp()){if(!b||!b.force){c.logAction(c.LOG_MINOR,"Auth.getToken()","using cached token; expires = "+d.expires);e(null,d);return}}else c.logAction(c.LOG_MINOR,"Auth.getToken()", -"deleting expired token"),this.tokenDetails=null}var k=this;this.requestToken(a,b,function(a,b){a?e(a):e(null,k.tokenDetails=b)})};b.prototype.requestToken=function(b,e,k){"function"!=typeof b||k?"function"!=typeof e||k||(k=e,e=null):(k=b,e=b=null);e=l.mixin(l.copy(this.rest.options),e);b=b||l.copy(this.tokenParams);k=k||d;var f=e.format||"json",m,t=this.rest;if(e.authCallback)c.logAction(c.LOG_MINOR,"Auth.requestToken()","using token auth with auth_callback"),m=e.authCallback;else if(e.authUrl)c.logAction(c.LOG_MINOR, -"Auth.requestToken()","using token auth with auth_url"),e.authParams||(m=e.authUrl.indexOf("?"),-1a.statusCode, -c=a.response;c?b?g.complete(null,c):(a=c.error||new u("Error response received from server",5E4,a.statusCode),g.complete(a)):g.complete(new u("Invalid server response: no envelope detected",5E4,500))};this.timer=setTimeout(function(){g.abort()},this.requestMode==Q.REQ_SEND?this.timeouts.httpRequestTimeout:this.timeouts.recvTimeout);a.insertBefore(c,a.firstChild)};g.prototype.complete=function(a,b){if(!this.requestComplete){this.requestComplete=!0;var c;b&&(c="string"==typeof b?"text/plain":"application/json", -this.emit("data",b));this.emit("complete",a,b,c&&{"content-type":c},!0);this.dispose()}};g.prototype.abort=function(){this.dispose()};g.prototype.dispose=function(){var a=this.timer;a&&(clearTimeout(a),this.timer=null);a=this.script;a.parentNode&&a.parentNode.removeChild(a);delete e[this.id];this.emit("disposed")};var p=z.Request=function(a,b,c,d,e){a=m(a,b,c,d,Q.REQ_SEND);a.once("complete",e);a.exec();return a};return d})();var ca=function(){function d(){for(var a in k)k[a].dispose()}function g(){return window.XMLHttpRequest&& -"withCredentials"in new XMLHttpRequest?p=!0:m&&document.domain&&"https:"==window.location.protocol?!0:!1}function b(b,c,d,e,f,g){x.call(this);d=d||{};d.rnd=String(Math.random()).substr(2);var h,p;if(p=m)p=h=(h=navigator.userAgent.toString().match(/MSIE\s([\d.]+)/))&&Number(h[1]);p&&10===h&&!d.envelope&&(d.envelope="json");this.uri=b+l.toQueryString(d);this.headers=c||{};this.body=e;this.requestMode=f;this.timeouts=g;this.requestComplete=!1;k[this.id=String(++a)]=this}function e(a,c,d,e,f,g){d.ua= -"xdr";b.call(this,a,c,d,e,f,g)}var f=function(){},a=0,k={},m=window.XDomainRequest,p;l.inherits(b,x);b.isAvailable=g;var h=b.createRequest=function(a,c,d,f,g){var k=this&&this.timeouts?this.timeouts:q.TIMEOUTS;return p?new b(a,c,d,f,g,k):new e(a,c,d,f,g,k)};b.prototype.complete=function(a,b,c,d){this.requestComplete||(this.requestComplete=!0,b&&this.emit("data",b),this.emit("complete",a,b,c,d),this.dispose())};b.prototype.abort=function(){this.dispose()};b.prototype.exec=function(){function a(){B= -g.responseText;for(var b=B.length-1,c,d;xd)&&0!==g.status){if(void 0===s)if(s=g.status,1223===s&&(s=204),clearTimeout(c),w=400>s,204==s)k.complete();else{var e;if(e=3==k.requestMode&&w)e=g,e=e.getResponseHeader&&e.getResponseHeader("transfer-encoding")&&!e.getResponseHeader("content-length");u=e}if(3==d&&u)a();else if(4==d)if(u)b();else a:{try{var f=g.getResponseHeader&&g.getResponseHeader("content-type"), -d=null,h=f?"application/json"==f:"text"==g.responseType;B=h?g.responseText:g.response;if(!B){204!=status&&(m=Error("Incomplete response body from server"),m.statusCode=400,k.complete(m));break a}h&&(B=JSON.parse(String(B)),y=!0);void 0!==B.response&&(s=B.statusCode,w=400>s,d=B.headers,B=B.response)}catch(l){var m=Error("Malformed response body from server: "+l.message);m.statusCode=400;k.complete(m);break a}w?k.complete(null,B,d||f&&{"content-type":f},y):(m=B.error,m||(m=Error("Error response received from server: "+ -s),m.statusCode=s),k.complete(m))}}};g.send(d)};b.prototype.dispose=function(){var a=this.xhr;if(a){a.onreadystatechange=a.onerror=a.onabort=a.ontimeout=f;this.xhr=null;var b=this.timer;b&&(clearTimeout(b),this.timer=null);this.requestComplete||a.abort()}delete k[this.id]};l.inherits(e,b);e.prototype.exec=function(){function a(){clearTimeout(e);if(s=k.responseText){var b=s.length-1;if("\n"==s[b]||(b=-1\n\n \n + + +

Ably cross-origin iframe

+ + diff --git a/browser/static/iframe.min-0.8.9.html b/browser/static/iframe.min-0.8.9.html deleted file mode 100644 index beb5a7cf09..0000000000 --- a/browser/static/iframe.min-0.8.9.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - -

Ably cross-origin iframe

- - diff --git a/browser/static/iframe.min.js b/browser/static/iframe.min.js index 487e264b21..74b160dcb8 100644 --- a/browser/static/iframe.min.js +++ b/browser/static/iframe.min.js @@ -1,7 +1,7 @@ /* Copyright 2015, Ably - Ably JavaScript Library v0.8.9 + Ably JavaScript Library v0.8.10 https://github.com/ably/ably-js Ably Realtime Messaging @@ -9,39 +9,41 @@ Released under the Apache Licence v2.0 */ -(function(){var z=window.Ably=this,h={internetUpUrlWithoutExtension:"https://internet-up.ably-realtime.com/is-the-internet-up",httpTransports:["xhr","iframe","jsonp"],transports:["web_socket","xhr","iframe","jsonp"],minified:!function(){}.name},A=function(){function a(){}a.addListener=function(a,b,e){a.addEventListener?a.addEventListener(b,e,!1):a.attachEvent("on"+b,function(){e.apply(a,arguments)})};a.removeListener=function(a,b,e){a.removeEventListener?a.removeEventListener(b,e,!1):a.detachEvent("on"+ -b,function(){e.apply(a,arguments)})};a.addMessageListener=function(g,b){a.addListener(g,"message",b)};a.removeMessageListener=function(g,b){a.removeListener(g,"message",b)};a.addUnloadListener=function(g){a.addListener(window,"unload",g)};return a}();h.protocolVersion=1;h.ENVIRONMENT="";h.HOST="rest.ably.io";h.WS_HOST="realtime.ably.io";h.FALLBACK_HOSTS=["A.ably-realtime.com","B.ably-realtime.com","C.ably-realtime.com","D.ably-realtime.com","E.ably-realtime.com"];h.PORT=80;h.TLS_PORT=443;h.TIMEOUTS= -{disconnectedRetryTimeout:15E3,suspendedRetryTimeout:3E4,httpRequestTimeout:15E3,connectionStateTtl:6E4,realtimeRequestTimeout:1E4,recvTimeout:9E4,connectionPersistTimeout:15E3};h.httpMaxRetryCount=3;h.version="0.8.9";h.getHost=function(a,g,b){return g=b?g==a.host&&a.wsHost||g||a.wsHost:g||a.host};h.getPort=function(a,g){return g||a.tls?a.tlsPort:a.port};h.getHttpScheme=function(a){return a.tls?"https://":"http://"};h.getHosts=function(a){var g=[a.host],b=a.fallbackHosts;a="undefined"!==typeof a.httpMaxRetryCount? -a.httpMaxRetryCount:h.httpMaxRetryCount;b&&(g=g.concat(b.slice(0,a)));return g};h.normaliseOptions=function(a){if(a.host)a.wsHost=a.wsHost||a.host;else{var g=a.environment&&String(a.environment).toLowerCase()||h.ENVIRONMENT,b=!g||"production"===g;a.host=b?h.HOST:g+"-"+h.HOST;a.wsHost=b?h.WS_HOST:g+"-"+h.WS_HOST;a.fallbackHosts=b?h.FALLBACK_HOSTS:a.fallbackHosts}a.port=a.port||h.PORT;a.tlsPort=a.tlsPort||h.TLS_PORT;"tls"in a||(a.tls=!0);a.timeouts={};for(var e in h.TIMEOUTS)a.timeouts[e]=a[e]||h.TIMEOUTS[e]; -return a};var C=function(){function a(){this.any=[];this.events={};this.anyOnce=[];this.eventsOnce={}}a.prototype.on=function(a,b){1==arguments.length&&"function"==typeof a?this.any.push(a):null===a?this.any.push(b):(this.events[a]||(this.events[a]=[])).push(b)};a.prototype.off=function(a,b){if(0==arguments.length)this.any=[],this.events={},this.anyOnce=[],this.eventsOnce={};else{1==arguments.length&&"function"==typeof a&&(b=a,a=null);var e,d=-1;if(null===a)if(b){if(!(e=this.any)||-1==(d=n.arrIndexOf(e, -b)))if(e=this.anyOnce)d=n.arrIndexOf(e,b);-1d)&&0!==e.status){if(void 0===s)if(s=e.status,1223===s&&(s=204),clearTimeout(c),w=400>s,204==s)g.complete();else{var k;if(k=3==g.requestMode&&w)k=e,k=k.getResponseHeader&&k.getResponseHeader("transfer-encoding")&&!k.getResponseHeader("content-length");t=k}if(3==d&&t)a(); -else if(4==d)if(t)b();else a:{try{var f=e.getResponseHeader&&e.getResponseHeader("content-type"),d=null,h=f?"application/json"==f:"text"==e.responseType;p=h?e.responseText:e.response;if(!p){204!=status&&(q=Error("Incomplete response body from server"),q.statusCode=400,g.complete(q));break a}h&&(p=JSON.parse(String(p)),r=!0);void 0!==p.response&&(s=p.statusCode,w=400>s,d=p.headers,p=p.response)}catch(l){var q=Error("Malformed response body from server: "+l.message);q.statusCode=400;g.complete(q);break a}w? -g.complete(null,p,d||f&&{"content-type":f},r):(q=p.error,q||(q=Error("Error response received from server: "+s),q.statusCode=s),g.complete(q))}}};e.send(d)};b.prototype.dispose=function(){var a=this.xhr;if(a){a.onreadystatechange=a.onerror=a.onabort=a.ontimeout=d;this.xhr=null;var b=this.timer;b&&(clearTimeout(b),this.timer=null);this.requestComplete||a.abort()}delete f[this.id]};n.inherits(e,b);e.prototype.exec=function(){function a(){clearTimeout(c);if(m=f.responseText){var b=m.length-1;if("\n"== +(function(){var z=window.Ably=this,h={internetUpUrlWithoutExtension:"https://internet-up.ably-realtime.com/is-the-internet-up",httpTransports:["xhr","iframe","jsonp"],transports:["web_socket","xhr","iframe","jsonp"],minified:!function(){}.name},A=function(){function a(){}a.addListener=function(a,e,b){a.addEventListener?a.addEventListener(e,b,!1):a.attachEvent("on"+e,function(){b.apply(a,arguments)})};a.removeListener=function(a,e,b){a.removeEventListener?a.removeEventListener(e,b,!1):a.detachEvent("on"+ +e,function(){b.apply(a,arguments)})};a.addMessageListener=function(g,e){a.addListener(g,"message",e)};a.removeMessageListener=function(g,e){a.removeListener(g,"message",e)};a.addUnloadListener=function(g){a.addListener(window,"unload",g)};return a}();h.protocolVersion=1;h.ENVIRONMENT="";h.REST_HOST="rest.ably.io";h.REALTIME_HOST="realtime.ably.io";h.FALLBACK_HOSTS=["A.ably-realtime.com","B.ably-realtime.com","C.ably-realtime.com","D.ably-realtime.com","E.ably-realtime.com"];h.PORT=80;h.TLS_PORT=443; +h.TIMEOUTS={disconnectedRetryTimeout:15E3,suspendedRetryTimeout:3E4,httpRequestTimeout:15E3,connectionStateTtl:6E4,realtimeRequestTimeout:1E4,recvTimeout:9E4,connectionPersistTimeout:15E3};h.httpMaxRetryCount=3;h.version="0.8.10";h.apiVersion="0.8";h.getHost=function(a,g,e){return g=e?g==a.restHost&&a.realtimeHost||g||a.realtimeHost:g||a.restHost};h.getPort=function(a,g){return g||a.tls?a.tlsPort:a.port};h.getHttpScheme=function(a){return a.tls?"https://":"http://"};h.getHosts=function(a){var g=[a.restHost], +e=a.fallbackHosts;a="undefined"!==typeof a.httpMaxRetryCount?a.httpMaxRetryCount:h.httpMaxRetryCount;e&&(g=g.concat(e.slice(0,a)));return g};h.normaliseOptions=function(a){a.host&&(l.deprecated("host","restHost"),a.restHost=a.host);a.wsHost&&(l.deprecated("wsHost","realtimeHost"),a.realtimeHost=a.wsHost);a.queueEvents&&(l.deprecated("queueEvents","queueMessages"),a.queueMessages=a.queueEvents);"queueMessages"in a||(a.queueMessages=!0);if(a.restHost)a.realtimeHost=a.realtimeHost||a.restHost;else{var g= +a.environment&&String(a.environment).toLowerCase()||h.ENVIRONMENT,e=!g||"production"===g;a.restHost=e?h.REST_HOST:g+"-"+h.REST_HOST;a.realtimeHost=e?h.REALTIME_HOST:g+"-"+h.REALTIME_HOST;a.fallbackHosts=e?h.FALLBACK_HOSTS:a.fallbackHosts}a.port=a.port||h.PORT;a.tlsPort=a.tlsPort||h.TLS_PORT;"tls"in a||(a.tls=!0);a.timeouts={};for(var b in h.TIMEOUTS)a.timeouts[b]=a[b]||h.TIMEOUTS[b];return a};var B=function(){function a(){this.any=[];this.events={};this.anyOnce=[];this.eventsOnce={}}function g(a, +b,d){try{b.apply(a,d)}catch(c){l.logAction(l.LOG_ERROR,"EventEmitter.emit()","Unexpected listener exception: "+c+"; stack = "+c.stack)}}a.prototype.on=function(a,b){1==arguments.length&&"function"==typeof a?this.any.push(a):null===a?this.any.push(b):(this.events[a]||(this.events[a]=[])).push(b)};a.prototype.off=function(a,b){if(0==arguments.length)this.any=[],this.events={},this.anyOnce=[],this.eventsOnce={};else{1==arguments.length&&"function"==typeof a&&(b=a,a=null);var d,c=-1;if(null===a)if(b){if(!(d= +this.any)||-1==(c=n.arrIndexOf(d,b)))if(d=this.anyOnce)c=n.arrIndexOf(d,b);-1b)&&0!==k.status){if(void 0===s)if(s=k.status,1223===s&&(s=204),clearTimeout(c),w=400>s,204==s)g.complete();else{var e;if(e=3==g.requestMode&&w)e=k,e=e.getResponseHeader&&e.getResponseHeader("transfer-encoding")&&!e.getResponseHeader("content-length");t=e}if(3==b&&t)a(); +else if(4==b)if(t)d();else a:{try{var f=k.getResponseHeader&&k.getResponseHeader("content-type"),b=null,h=f?"application/json"==f:"text"==k.responseType;p=h?k.responseText:k.response;if(!p){204!=status&&(q=Error("Incomplete response body from server"),q.statusCode=400,g.complete(q));break a}h&&(p=JSON.parse(String(p)),r=!0);void 0!==p.response&&(s=p.statusCode,w=400>s,b=p.headers,p=p.response)}catch(l){var q=Error("Malformed response body from server: "+l.message);q.statusCode=400;g.complete(q);break a}w? +g.complete(null,p,b||f&&{"content-type":f},r):(q=p.error,q||(q=Error("Error response received from server: "+s),q.statusCode=s),g.complete(q))}}};k.send(b)};e.prototype.dispose=function(){var a=this.xhr;if(a){a.onreadystatechange=a.onerror=a.onabort=a.ontimeout=d;this.xhr=null;var b=this.timer;b&&(clearTimeout(b),this.timer=null);this.requestComplete||a.abort()}delete f[this.id]};n.inherits(b,e);b.prototype.exec=function(){function a(){clearTimeout(c);if(m=f.responseText){var b=m.length-1;if("\n"== m[b]||(b=-1 Date: Thu, 17 Dec 2015 21:18:18 +0000 Subject: [PATCH 2/2] WebsocketTransport: use event handlers correctly (wasn't correctly turing off the handler) also add comment --- common/lib/transport/connectionmanager.js | 2 ++ common/lib/transport/websockettransport.js | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/common/lib/transport/connectionmanager.js b/common/lib/transport/connectionmanager.js index fd335da868..d8634c3761 100644 --- a/common/lib/transport/connectionmanager.js +++ b/common/lib/transport/connectionmanager.js @@ -822,6 +822,8 @@ var ConnectionManager = (function() { } closeTransport(this.activeProtocol && this.activeProtocol.getTransport()); + /* If there was an active transport, this will probably be + * preempted by the notifyState call in deactivateTransport */ this.notifyState({state: 'closed'}); }; diff --git a/common/lib/transport/websockettransport.js b/common/lib/transport/websockettransport.js index c5a6d51917..c3a187d6fb 100644 --- a/common/lib/transport/websockettransport.js +++ b/common/lib/transport/websockettransport.js @@ -19,21 +19,22 @@ var WebSocketTransport = (function() { WebSocketTransport.tryConnect = function(connectionManager, auth, params, callback) { var transport = new WebSocketTransport(connectionManager, auth, params); var errorCb = function(err) { callback(err); }; - var closeHandler; + var closeHandler = function(stateChange) { + if(stateChange.current === 'closing') + transport.close(); + }; transport.on('wserror', errorCb); transport.on('wsopen', function() { Logger.logAction(Logger.LOG_MINOR, 'WebSocketTransport.tryConnect()', 'viable transport ' + transport); transport.off('wserror', errorCb); transport.cancelConnectTimeout(); - connectionManager.off(closeHandler); + connectionManager.off('connectionstate', closeHandler); callback(null, transport); }); /* At this point connectionManager has no reference to websocketTransport. * So need to handle a connect timeout and listen for close events here temporarily */ transport.startConnectTimeout(); - closeHandler = connectionManager.on('connectionstate', function(stateChange) { - if(stateChange.current === 'closing') transport.close(); - }); + connectionManager.on('connectionstate', closeHandler); transport.connect(); };