UNPKG

faye

Version:

Simple pub/sub messaging for the web

1,468 lines (1,201 loc) 103 kB
var Faye = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/faye_browser.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/asap/browser-asap.js": /*!*******************************************!*\ !*** ./node_modules/asap/browser-asap.js ***! \*******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; // rawAsap provides everything we need except exception management. var rawAsap = __webpack_require__(/*! ./raw */ "./node_modules/asap/browser-raw.js"); // RawTasks are recycled to reduce GC churn. var freeTasks = []; // We queue errors to ensure they are thrown in right order (FIFO). // Array-as-queue is good enough here, since we are just dealing with exceptions. var pendingErrors = []; var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError); function throwFirstError() { if (pendingErrors.length) { throw pendingErrors.shift(); } } /** * Calls a task as soon as possible after returning, in its own event, with priority * over other events like animation, reflow, and repaint. An error thrown from an * event will not interrupt, nor even substantially slow down the processing of * other events, but will be rather postponed to a lower priority event. * @param {{call}} task A callable object, typically a function that takes no * arguments. */ module.exports = asap; function asap(task) { var rawTask; if (freeTasks.length) { rawTask = freeTasks.pop(); } else { rawTask = new RawTask(); } rawTask.task = task; rawAsap(rawTask); } // We wrap tasks with recyclable task objects. A task object implements // `call`, just like a function. function RawTask() { this.task = null; } // The sole purpose of wrapping the task is to catch the exception and recycle // the task object after its single use. RawTask.prototype.call = function () { try { this.task.call(); } catch (error) { if (asap.onerror) { // This hook exists purely for testing purposes. // Its name will be periodically randomized to break any code that // depends on its existence. asap.onerror(error); } else { // In a web browser, exceptions are not fatal. However, to avoid // slowing down the queue of pending tasks, we rethrow the error in a // lower priority turn. pendingErrors.push(error); requestErrorThrow(); } } finally { this.task = null; freeTasks[freeTasks.length] = this; } }; /***/ }), /***/ "./node_modules/asap/browser-raw.js": /*!******************************************!*\ !*** ./node_modules/asap/browser-raw.js ***! \******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { // Use the fastest means possible to execute a task in its own turn, with // priority over other events including IO, animation, reflow, and redraw // events in browsers. // // An exception thrown by a task will permanently interrupt the processing of // subsequent tasks. The higher level `asap` function ensures that if an // exception is thrown by a task, that the task queue will continue flushing as // soon as possible, but if you use `rawAsap` directly, you are responsible to // either ensure that no exceptions are thrown from your task, or to manually // call `rawAsap.requestFlush` if an exception is thrown. module.exports = rawAsap; function rawAsap(task) { if (!queue.length) { requestFlush(); flushing = true; } // Equivalent to push, but avoids a function call. queue[queue.length] = task; } var queue = []; // Once a flush has been requested, no further calls to `requestFlush` are // necessary until the next `flush` completes. var flushing = false; // `requestFlush` is an implementation-specific method that attempts to kick // off a `flush` event as quickly as possible. `flush` will attempt to exhaust // the event queue before yielding to the browser's own event loop. var requestFlush; // The position of the next task to execute in the task queue. This is // preserved between calls to `flush` so that it can be resumed if // a task throws an exception. var index = 0; // If a task schedules additional tasks recursively, the task queue can grow // unbounded. To prevent memory exhaustion, the task queue will periodically // truncate already-completed tasks. var capacity = 1024; // The flush function processes all tasks that have been scheduled with // `rawAsap` unless and until one of those tasks throws an exception. // If a task throws an exception, `flush` ensures that its state will remain // consistent and will resume where it left off when called again. // However, `flush` does not make any arrangements to be called again if an // exception is thrown. function flush() { while (index < queue.length) { var currentIndex = index; // Advance the index before calling the task. This ensures that we will // begin flushing on the next task the task throws an error. index = index + 1; queue[currentIndex].call(); // Prevent leaking memory for long chains of recursive calls to `asap`. // If we call `asap` within tasks scheduled by `asap`, the queue will // grow, but to avoid an O(n) walk for every task we execute, we don't // shift tasks off the queue after they have been executed. // Instead, we periodically shift 1024 tasks off the queue. if (index > capacity) { // Manually shift all values starting at the index back to the // beginning of the queue. for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { queue[scan] = queue[scan + index]; } queue.length -= index; index = 0; } } queue.length = 0; index = 0; flushing = false; } // `requestFlush` is implemented using a strategy based on data collected from // every available SauceLabs Selenium web driver worker at time of writing. // https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593 // Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that // have WebKitMutationObserver but not un-prefixed MutationObserver. // Must use `global` or `self` instead of `window` to work in both frames and web // workers. `global` is a provision of Browserify, Mr, Mrs, or Mop. /* globals self */ var scope = typeof global !== "undefined" ? global : self; var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; // MutationObservers are desirable because they have high priority and work // reliably everywhere they are implemented. // They are implemented in all modern browsers. // // - Android 4-4.3 // - Chrome 26-34 // - Firefox 14-29 // - Internet Explorer 11 // - iPad Safari 6-7.1 // - iPhone Safari 7-7.1 // - Safari 6-7 if (typeof BrowserMutationObserver === "function") { requestFlush = makeRequestCallFromMutationObserver(flush); // MessageChannels are desirable because they give direct access to the HTML // task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera // 11-12, and in web workers in many engines. // Although message channels yield to any queued rendering and IO tasks, they // would be better than imposing the 4ms delay of timers. // However, they do not work reliably in Internet Explorer or Safari. // Internet Explorer 10 is the only browser that has setImmediate but does // not have MutationObservers. // Although setImmediate yields to the browser's renderer, it would be // preferrable to falling back to setTimeout since it does not have // the minimum 4ms penalty. // Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and // Desktop to a lesser extent) that renders both setImmediate and // MessageChannel useless for the purposes of ASAP. // https://github.com/kriskowal/q/issues/396 // Timers are implemented universally. // We fall back to timers in workers in most engines, and in foreground // contexts in the following browsers. // However, note that even this simple case requires nuances to operate in a // broad spectrum of browsers. // // - Firefox 3-13 // - Internet Explorer 6-9 // - iPad Safari 4.3 // - Lynx 2.8.7 } else { requestFlush = makeRequestCallFromTimer(flush); } // `requestFlush` requests that the high priority event queue be flushed as // soon as possible. // This is useful to prevent an error thrown in a task from stalling the event // queue if the exception handled by Node.js’s // `process.on("uncaughtException")` or by a domain. rawAsap.requestFlush = requestFlush; // To request a high priority event, we induce a mutation observer by toggling // the text of a text node between "1" and "-1". function makeRequestCallFromMutationObserver(callback) { var toggle = 1; var observer = new BrowserMutationObserver(callback); var node = document.createTextNode(""); observer.observe(node, {characterData: true}); return function requestCall() { toggle = -toggle; node.data = toggle; }; } // The message channel technique was discovered by Malte Ubl and was the // original foundation for this library. // http://www.nonblocking.io/2011/06/windownexttick.html // Safari 6.0.5 (at least) intermittently fails to create message ports on a // page's first load. Thankfully, this version of Safari supports // MutationObservers, so we don't need to fall back in that case. // function makeRequestCallFromMessageChannel(callback) { // var channel = new MessageChannel(); // channel.port1.onmessage = callback; // return function requestCall() { // channel.port2.postMessage(0); // }; // } // For reasons explained above, we are also unable to use `setImmediate` // under any circumstances. // Even if we were, there is another bug in Internet Explorer 10. // It is not sufficient to assign `setImmediate` to `requestFlush` because // `setImmediate` must be called *by name* and therefore must be wrapped in a // closure. // Never forget. // function makeRequestCallFromSetImmediate(callback) { // return function requestCall() { // setImmediate(callback); // }; // } // Safari 6.0 has a problem where timers will get lost while the user is // scrolling. This problem does not impact ASAP because Safari 6.0 supports // mutation observers, so that implementation is used instead. // However, if we ever elect to use timers in Safari, the prevalent work-around // is to add a scroll event listener that calls for a flush. // `setTimeout` does not call the passed callback if the delay is less than // approximately 7 in web workers in Firefox 8 through 18, and sometimes not // even then. function makeRequestCallFromTimer(callback) { return function requestCall() { // We dispatch a timeout with a specified delay of 0 for engines that // can reliably accommodate that request. This will usually be snapped // to a 4 milisecond delay, but once we're flushing, there's no delay // between events. var timeoutHandle = setTimeout(handleTimer, 0); // However, since this timer gets frequently dropped in Firefox // workers, we enlist an interval handle that will try to fire // an event 20 times per second until it succeeds. var intervalHandle = setInterval(handleTimer, 50); function handleTimer() { // Whichever timer succeeds will cancel both timers and // execute the callback. clearTimeout(timeoutHandle); clearInterval(intervalHandle); callback(); } }; } // This is for `asap.js` only. // Its name will be periodically randomized to break any code that depends on // its existence. rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer; // ASAP was originally a nextTick shim included in Q. This was factored out // into this ASAP package. It was later adapted to RSVP which made further // amendments. These decisions, particularly to marginalize MessageChannel and // to capture the MutationObserver implementation in a closure, were integrated // back into ASAP proper. // https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./node_modules/webpack/buildin/global.js": /*!***********************************!*\ !*** (webpack)/buildin/global.js ***! \***********************************/ /*! no static exports found */ /***/ (function(module, exports) { var g; // This works in non-strict mode g = (function() { return this; })(); try { // This works if eval is allowed (see CSP) g = g || new Function("return this")(); } catch (e) { // This works if the window reference is available if (typeof window === "object") g = window; } // g can still be undefined, but nothing to do about it... // We return undefined, instead of nothing here, so it's // easier to handle this case. if(!global) { ...} module.exports = g; /***/ }), /***/ "./src/faye_browser.js": /*!*****************************!*\ !*** ./src/faye_browser.js ***! \*****************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var constants = __webpack_require__(/*! ./util/constants */ "./src/util/constants.js"), Logging = __webpack_require__(/*! ./mixins/logging */ "./src/mixins/logging.js"); var Faye = { VERSION: constants.VERSION, Client: __webpack_require__(/*! ./protocol/client */ "./src/protocol/client.js"), Scheduler: __webpack_require__(/*! ./protocol/scheduler */ "./src/protocol/scheduler.js") }; Logging.wrapper = Faye; module.exports = Faye; /***/ }), /***/ "./src/mixins/deferrable.js": /*!**********************************!*\ !*** ./src/mixins/deferrable.js ***! \**********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { var Promise = __webpack_require__(/*! ../util/promise */ "./src/util/promise.js"); module.exports = { then: function(callback, errback) { var self = this; if (!this._promise) this._promise = new Promise(function(resolve, reject) { self._resolve = resolve; self._reject = reject; }); if (arguments.length === 0) return this._promise; else return this._promise.then(callback, errback); }, callback: function(callback, context) { return this.then(function(value) { callback.call(context, value) }); }, errback: function(callback, context) { return this.then(null, function(reason) { callback.call(context, reason) }); }, timeout: function(seconds, message) { this.then(); var self = this; this._timer = global.setTimeout(function() { self._reject(message); }, seconds * 1000); }, setDeferredStatus: function(status, value) { if (this._timer) global.clearTimeout(this._timer); this.then(); if (status === 'succeeded') this._resolve(value); else if (status === 'failed') this._reject(value); else delete this._promise; } }; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./src/mixins/logging.js": /*!*******************************!*\ !*** ./src/mixins/logging.js ***! \*******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var toJSON = __webpack_require__(/*! ../util/to_json */ "./src/util/to_json.js"); var Logging = { LOG_LEVELS: { fatal: 4, error: 3, warn: 2, info: 1, debug: 0 }, writeLog: function(messageArgs, level) { var logger = Logging.logger || (Logging.wrapper || Logging).logger; if (!logger) return; var args = Array.prototype.slice.apply(messageArgs), banner = '[Faye', klass = this.className, message = args.shift().replace(/\?/g, function() { try { return toJSON(args.shift()); } catch (error) { return '[Object]'; } }); if (klass) banner += '.' + klass; banner += '] '; if (typeof logger[level] === 'function') logger[level](banner + message); else if (typeof logger === 'function') logger(banner + message); } }; for (var key in Logging.LOG_LEVELS) (function(level) { Logging[level] = function() { this.writeLog(arguments, level); }; })(key); module.exports = Logging; /***/ }), /***/ "./src/mixins/publisher.js": /*!*********************************!*\ !*** ./src/mixins/publisher.js ***! \*********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var assign = __webpack_require__(/*! ../util/assign */ "./src/util/assign.js"), EventEmitter = __webpack_require__(/*! ../util/event_emitter */ "./src/util/event_emitter.js"); var Publisher = { countListeners: function(eventType) { return this.listeners(eventType).length; }, bind: function(eventType, listener, context) { var slice = Array.prototype.slice, handler = function() { listener.apply(context, slice.call(arguments)) }; this._listeners = this._listeners || []; this._listeners.push([eventType, listener, context, handler]); return this.on(eventType, handler); }, unbind: function(eventType, listener, context) { this._listeners = this._listeners || []; var n = this._listeners.length, tuple; while (n--) { tuple = this._listeners[n]; if (tuple[0] !== eventType) continue; if (listener && (tuple[1] !== listener || tuple[2] !== context)) continue; this._listeners.splice(n, 1); this.removeListener(eventType, tuple[3]); } } }; assign(Publisher, EventEmitter.prototype); Publisher.trigger = Publisher.emit; module.exports = Publisher; /***/ }), /***/ "./src/mixins/timeouts.js": /*!********************************!*\ !*** ./src/mixins/timeouts.js ***! \********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { module.exports = { addTimeout: function(name, delay, callback, context) { this._timeouts = this._timeouts || {}; if (this._timeouts.hasOwnProperty(name)) return; var self = this; this._timeouts[name] = global.setTimeout(function() { delete self._timeouts[name]; callback.call(context); }, 1000 * delay); }, removeTimeout: function(name) { this._timeouts = this._timeouts || {}; var timeout = this._timeouts[name]; if (!timeout) return; global.clearTimeout(timeout); delete this._timeouts[name]; }, removeAllTimeouts: function() { this._timeouts = this._timeouts || {}; for (var name in this._timeouts) this.removeTimeout(name); } }; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./src/protocol/channel.js": /*!*********************************!*\ !*** ./src/protocol/channel.js ***! \*********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var Class = __webpack_require__(/*! ../util/class */ "./src/util/class.js"), assign = __webpack_require__(/*! ../util/assign */ "./src/util/assign.js"), Publisher = __webpack_require__(/*! ../mixins/publisher */ "./src/mixins/publisher.js"), Grammar = __webpack_require__(/*! ./grammar */ "./src/protocol/grammar.js"); var Channel = Class({ initialize: function(name) { this.id = this.name = name; }, push: function(message) { this.trigger('message', message); }, isUnused: function() { return this.countListeners('message') === 0; } }); assign(Channel.prototype, Publisher); assign(Channel, { HANDSHAKE: '/meta/handshake', CONNECT: '/meta/connect', SUBSCRIBE: '/meta/subscribe', UNSUBSCRIBE: '/meta/unsubscribe', DISCONNECT: '/meta/disconnect', META: 'meta', SERVICE: 'service', expand: function(name) { var segments = this.parse(name), channels = ['/**', name]; var copy = segments.slice(); copy[copy.length - 1] = '*'; channels.push(this.unparse(copy)); for (var i = 1, n = segments.length; i < n; i++) { copy = segments.slice(0, i); copy.push('**'); channels.push(this.unparse(copy)); } return channels; }, isValid: function(name) { return Grammar.CHANNEL_NAME.test(name) || Grammar.CHANNEL_PATTERN.test(name); }, parse: function(name) { if (!this.isValid(name)) return null; return name.split('/').slice(1); }, unparse: function(segments) { return '/' + segments.join('/'); }, isMeta: function(name) { var segments = this.parse(name); return segments ? (segments[0] === this.META) : null; }, isService: function(name) { var segments = this.parse(name); return segments ? (segments[0] === this.SERVICE) : null; }, isSubscribable: function(name) { if (!this.isValid(name)) return null; return !this.isMeta(name) && !this.isService(name); }, Set: Class({ initialize: function() { this._channels = {}; }, getKeys: function() { var keys = []; for (var key in this._channels) keys.push(key); return keys; }, remove: function(name) { delete this._channels[name]; }, hasSubscription: function(name) { return this._channels.hasOwnProperty(name); }, subscribe: function(names, subscription) { var name; for (var i = 0, n = names.length; i < n; i++) { name = names[i]; var channel = this._channels[name] = this._channels[name] || new Channel(name); channel.bind('message', subscription); } }, unsubscribe: function(name, subscription) { var channel = this._channels[name]; if (!channel) return false; channel.unbind('message', subscription); if (channel.isUnused()) { this.remove(name); return true; } else { return false; } }, distributeMessage: function(message) { var channels = Channel.expand(message.channel); for (var i = 0, n = channels.length; i < n; i++) { var channel = this._channels[channels[i]]; if (channel) channel.trigger('message', message); } } }) }); module.exports = Channel; /***/ }), /***/ "./src/protocol/client.js": /*!********************************!*\ !*** ./src/protocol/client.js ***! \********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { var asap = __webpack_require__(/*! asap */ "./node_modules/asap/browser-asap.js"), Class = __webpack_require__(/*! ../util/class */ "./src/util/class.js"), Promise = __webpack_require__(/*! ../util/promise */ "./src/util/promise.js"), array = __webpack_require__(/*! ../util/array */ "./src/util/array.js"), browser = __webpack_require__(/*! ../util/browser */ "./src/util/browser/event.js"), constants = __webpack_require__(/*! ../util/constants */ "./src/util/constants.js"), assign = __webpack_require__(/*! ../util/assign */ "./src/util/assign.js"), validateOptions = __webpack_require__(/*! ../util/validate_options */ "./src/util/validate_options.js"), Deferrable = __webpack_require__(/*! ../mixins/deferrable */ "./src/mixins/deferrable.js"), Logging = __webpack_require__(/*! ../mixins/logging */ "./src/mixins/logging.js"), Publisher = __webpack_require__(/*! ../mixins/publisher */ "./src/mixins/publisher.js"), Channel = __webpack_require__(/*! ./channel */ "./src/protocol/channel.js"), Dispatcher = __webpack_require__(/*! ./dispatcher */ "./src/protocol/dispatcher.js"), Error = __webpack_require__(/*! ./error */ "./src/protocol/error.js"), Extensible = __webpack_require__(/*! ./extensible */ "./src/protocol/extensible.js"), Publication = __webpack_require__(/*! ./publication */ "./src/protocol/publication.js"), Subscription = __webpack_require__(/*! ./subscription */ "./src/protocol/subscription.js"); var Client = Class({ className: 'Client', UNCONNECTED: 1, CONNECTING: 2, CONNECTED: 3, DISCONNECTED: 4, HANDSHAKE: 'handshake', RETRY: 'retry', NONE: 'none', CONNECTION_TIMEOUT: 60, DEFAULT_ENDPOINT: '/bayeux', INTERVAL: 0, initialize: function(endpoint, options) { this.info('New client created for ?', endpoint); options = options || {}; validateOptions(options, ['interval', 'timeout', 'endpoints', 'proxy', 'retry', 'scheduler', 'websocketExtensions', 'tls', 'ca']); this._channels = new Channel.Set(); this._dispatcher = Dispatcher.create(this, endpoint || this.DEFAULT_ENDPOINT, options); this._messageId = 0; this._state = this.UNCONNECTED; this._responseCallbacks = {}; this._advice = { reconnect: this.RETRY, 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 (browser.Event && global.onbeforeunload !== undefined) browser.Event.on(global, 'beforeunload', function() { if (array.indexOf(this._dispatcher._disabled, 'autodisconnect') < 0) this.disconnect(); }, this); }, addWebsocketExtension: function(extension) { return this._dispatcher.addWebsocketExtension(extension); }, disable: function(feature) { return this._dispatcher.disable(feature); }, setHeader: function(name, value) { return this._dispatcher.setHeader(name, value); }, // Request // MUST include: * channel // * version // * supportedConnectionTypes // MAY include: * minimumVersion // * ext // * id // // Success Response Failed Response // MUST include: * channel MUST include: * channel // * version * successful // * supportedConnectionTypes * error // * clientId MAY include: * supportedConnectionTypes // * successful * advice // MAY include: * minimumVersion * version // * advice * minimumVersion // * ext * ext // * id * id // * authSuccessful handshake: function(callback, context) { if (this._advice.reconnect === this.NONE) return; if (this._state !== this.UNCONNECTED) return; this._state = this.CONNECTING; var self = this; this.info('Initiating handshake with ?', this._dispatcher.endpoint.href); this._dispatcher.selectTransport(constants.MANDATORY_CONNECTION_TYPES); this._sendMessage({ channel: Channel.HANDSHAKE, version: constants.BAYEUX_VERSION, supportedConnectionTypes: this._dispatcher.getConnectionTypes() }, {}, function(response) { if (response.successful) { this._state = this.CONNECTED; this._dispatcher.clientId = response.clientId; this._dispatcher.selectTransport(response.supportedConnectionTypes); this.info('Handshake successful: ?', this._dispatcher.clientId); this.subscribe(this._channels.getKeys(), true); if (callback) asap(function() { callback.call(context) }); } else { this.info('Handshake unsuccessful'); global.setTimeout(function() { self.handshake(callback, context) }, this._dispatcher.retry * 1000); this._state = this.UNCONNECTED; } }, this); }, // Request Response // MUST include: * channel MUST include: * channel // * clientId * successful // * connectionType * clientId // MAY include: * ext MAY include: * error // * id * advice // * ext // * id // * timestamp connect: function(callback, context) { if (this._advice.reconnect === this.NONE) return; if (this._state === this.DISCONNECTED) return; if (this._state === this.UNCONNECTED) 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._dispatcher.clientId); this.setDeferredStatus('succeeded'); this.setDeferredStatus('unknown'); if (this._connectRequest) return; this._connectRequest = true; this.info('Initiating connection for ?', this._dispatcher.clientId); this._sendMessage({ channel: Channel.CONNECT, clientId: this._dispatcher.clientId, connectionType: this._dispatcher.connectionType }, {}, this._cycleConnection, this); }, // Request Response // MUST include: * channel MUST include: * channel // * clientId * successful // MAY include: * ext * clientId // * id MAY include: * error // * ext // * id disconnect: function() { if (this._state !== this.CONNECTED) return; this._state = this.DISCONNECTED; this.info('Disconnecting ?', this._dispatcher.clientId); var promise = new Publication(); this._sendMessage({ channel: Channel.DISCONNECT, clientId: this._dispatcher.clientId }, {}, function(response) { if (response.successful) { this._dispatcher.close(); promise.setDeferredStatus('succeeded'); } else { promise.setDeferredStatus('failed', Error.parse(response.error)); } }, this); this.info('Clearing channel listeners for ?', this._dispatcher.clientId); this._channels = new Channel.Set(); return promise; }, // Request Response // MUST include: * channel MUST include: * channel // * clientId * successful // * subscription * clientId // MAY include: * ext * subscription // * id MAY include: * error // * advice // * ext // * id // * timestamp subscribe: function(channel, callback, context) { if (channel instanceof Array) return array.map(channel, function(c) { return this.subscribe(c, callback, context); }, this); var subscription = new Subscription(this, channel, callback, context), force = (callback === true), hasSubscribe = this._channels.hasSubscription(channel); if (hasSubscribe && !force) { this._channels.subscribe([channel], subscription); subscription.setDeferredStatus('succeeded'); return subscription; } this.connect(function() { this.info('Client ? attempting to subscribe to ?', this._dispatcher.clientId, channel); if (!force) this._channels.subscribe([channel], subscription); this._sendMessage({ channel: Channel.SUBSCRIBE, clientId: this._dispatcher.clientId, subscription: channel }, {}, function(response) { if (!response.successful) { subscription.setDeferredStatus('failed', Error.parse(response.error)); return this._channels.unsubscribe(channel, subscription); } var channels = [].concat(response.subscription); this.info('Subscription acknowledged for ? to ?', this._dispatcher.clientId, channels); subscription.setDeferredStatus('succeeded'); }, this); }, this); return subscription; }, // Request Response // MUST include: * channel MUST include: * channel // * clientId * successful // * subscription * clientId // MAY include: * ext * subscription // * id MAY include: * error // * advice // * ext // * id // * timestamp unsubscribe: function(channel, subscription) { if (channel instanceof Array) return array.map(channel, function(c) { return this.unsubscribe(c, subscription); }, this); var dead = this._channels.unsubscribe(channel, subscription); if (!dead) return; this.connect(function() { this.info('Client ? attempting to unsubscribe from ?', this._dispatcher.clientId, channel); this._sendMessage({ channel: Channel.UNSUBSCRIBE, clientId: this._dispatcher.clientId, subscription: channel }, {}, function(response) { if (!response.successful) return; var channels = [].concat(response.subscription); 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, options) { validateOptions(options || {}, ['attempts', 'deadline']); var publication = new Publication(); this.connect(function() { this.info('Client ? queueing published message to ?: ?', this._dispatcher.clientId, channel, data); this._sendMessage({ channel: channel, data: data, clientId: this._dispatcher.clientId }, options, function(response) { if (response.successful) publication.setDeferredStatus('succeeded'); else publication.setDeferredStatus('failed', Error.parse(response.error)); }, this); }, this); return publication; }, _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); }, _handleAdvice: function(advice) { assign(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(); } }, _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); }, _cycleConnection: function() { if (this._connectRequest) { this._connectRequest = null; this.info('Closed connection for ?', this._dispatcher.clientId); } var self = this; global.setTimeout(function() { self.connect() }, this._advice.interval); } }); assign(Client.prototype, Deferrable); assign(Client.prototype, Publisher); assign(Client.prototype, Logging); assign(Client.prototype, Extensible); module.exports = Client; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./src/protocol/dispatcher.js": /*!************************************!*\ !*** ./src/protocol/dispatcher.js ***! \************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { var Class = __webpack_require__(/*! ../util/class */ "./src/util/class.js"), URI = __webpack_require__(/*! ../util/uri */ "./src/util/uri.js"), cookies = __webpack_require__(/*! ../util/cookies */ "./src/util/cookies/browser_cookies.js"), assign = __webpack_require__(/*! ../util/assign */ "./src/util/assign.js"), Logging = __webpack_require__(/*! ../mixins/logging */ "./src/mixins/logging.js"), Publisher = __webpack_require__(/*! ../mixins/publisher */ "./src/mixins/publisher.js"), Transport = __webpack_require__(/*! ../transport */ "./src/transport/browser_transports.js"), Scheduler = __webpack_require__(/*! ./scheduler */ "./src/protocol/scheduler.js"); var Dispatcher = Class({ className: 'Dispatcher', MAX_REQUEST_SIZE: 2048, DEFAULT_RETRY: 5, UP: 1, DOWN: 2, initialize: function(client, endpoint, options) { this._client = client; this.endpoint = URI.parse(endpoint); this._alternates = options.endpoints || {}; this.cookies = cookies.CookieJar && new cookies.CookieJar(); this._disabled = []; this._envelopes = {}; this.headers = {}; this.retry = options.retry || this.DEFAULT_RETRY; this._scheduler = options.scheduler || 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]); } this.tls = options.tls || {}; this.tls.ca = this.tls.ca || options.ca; for (var type in this._alternates) this._alternates[type] = URI.parse(this._alternates[type]); this.maxRequestSize = this.MAX_REQUEST_SIZE; }, endpointFor: function(connectionType) { return this._alternates[connectionType] || this.endpoint; }, addWebsocketExtension: function(extension) { this.wsExtensions.push(extension); }, disable: function(feature) { this._disabled.push(feature); Transport.disable(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 Transport.getConnectionTypes(); }, selectTransport: function(transportTypes) { Transport.get(this, transportTypes, this._disabled, function(transport) { this.debug('Selected ? transport for ?', transport.connectionType, transport.endpoint.href); if (transport === this._transport) return; if (this._transport) this._transport.close(); this._transport = transport; this.connectionType = transport.connectionType; }, this); }, sendMessage: function(message, timeout, options) { options = options || {}; 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); }, _sendEnvelope: function(envelope) { if (!this._transport) return; if (envelope.request || envelope.timer) return; var message = envelope.message, scheduler = envelope.scheduler, self = this; if (!scheduler.isDeliverable()) { scheduler.abort(); delete this._envelopes[message.id]; return; } envelope.timer = global.setTimeout(function() { self.handleError(message); }, scheduler.getTimeout() * 1000); scheduler.send(); envelope.request = this._transport.sendMessage(message); }, handleResponse: function(reply) { var envelope = this._envelopes[reply.id]; if (reply.successful !== undefined && envelope) { envelope.scheduler.succeed(); delete this._envelopes[reply.id]; global.clearTimeout(envelope.timer); } this.trigger('message', reply); if (this._state === this.UP) return; this._state = this.UP; this._client.trigger('transport:up'); }, 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(); global.clearTimeout(envelope.timer); envelope.request = envelope.timer = null; if (immediate) { this._sendEnvelope(envelope); } else { envelope.timer = global.setTimeout(function() { envelope.timer = null; self._sendEnvelope(envelope); }, scheduler.getInterval() * 1000); } if (this._state === this.DOWN) return; this._state = this.DOWN; this._client.trigger('transport:down'); } }); Dispatcher.create = function(client, endpoint, options) { return new Dispatcher(client, endpoint, options); }; assign(Dispatcher.prototype, Publisher); assign(Dispatcher.prototype, Logging); module.exports = Dispatcher; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./src/protocol/error.js": /*!*******************************!*\ !*** ./src/protocol/error.js ***! \*******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var Class = __webpack_require__(/*! ../util/class */ "./src/util/class.js"), Grammar = __webpack_require__(/*! ./grammar */ "./src/protocol/grammar.js"); var Error = Class({ initialize: function(code, params, message) { this.code = code; this.params = Array.prototype.slice.call(params); this.message = message; }, toString: function() { return this.code + ':' + this.params.join(',') + ':' + this.message; } }); Error.parse = function(message) { message = message || ''; if (!Grammar.ERROR.test(message)) return new Error(null, [], message); var parts = message.split(':'), code = parseInt(parts[0]), params = parts[1].split(','), message = parts[2]; return new Error(code, params, message); }; // http://code.google.com/p/cometd/wiki/BayeuxCodes var errors = { versionMismatch: [300, 'Version mismatch'], conntypeMismatch: [301, 'Connection types not supported'], extMismatch: [302, 'Extension mismatch'], badRequest: [400, 'Bad request'], clientUnknown: [401, 'Unknown client'], parameterMissing: [402, 'Missing required parameter'], channelForbidden: [403, 'Forbidden channel'], channelUnknown: [404, 'Unknown channel'], channelInvalid: [405, 'Invalid channel'], extUnknown: [406, 'Unknown extension'], publishFailed: [407, 'Failed to publish'], serverError: [500, 'Internal server error'] }; for (var name in errors) (function(name) { Error[na