diff --git a/dist/js/getstream.js b/dist/js/getstream.js index 48094c0c..1f058bed 100644 --- a/dist/js/getstream.js +++ b/dist/js/getstream.js @@ -46,34 +46,19 @@ return /******/ (function(modules) { // webpackBootstrap /******/ // 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 }); +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ 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 ? @@ -91,7 +76,7 @@ return /******/ (function(modules) { // webpackBootstrap /******/ /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 26); +/******/ return __webpack_require__(__webpack_require__.s = 56); /******/ }) /************************************************************************/ /******/ ([ @@ -147,26 +132,26 @@ module.exports = function(parent, methods) { /* 2 */ /***/ (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 || Function("return this")() || (1, eval)("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; +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || Function("return this")() || (1, eval)("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; /***/ }), @@ -267,14 +252,14 @@ module.exports = { /* WEBPACK VAR INJECTION */(function(process) { var Class = __webpack_require__(1), - Cookie = __webpack_require__(22).Cookie, - Promise = __webpack_require__(5), + Cookie = __webpack_require__(19).Cookie, + Promise = __webpack_require__(9), URI = __webpack_require__(3), - array = __webpack_require__(12), + array = __webpack_require__(13), extend = __webpack_require__(0), - Logging = __webpack_require__(6), - Timeouts = __webpack_require__(45), - Channel = __webpack_require__(20); + Logging = __webpack_require__(8), + Timeouts = __webpack_require__(37), + Channel = __webpack_require__(21); var Transport = extend(Class({ className: 'Transport', DEFAULT_PORTS: {'http:': 80, 'https:': 443, 'ws:': 80, 'wss:': 443}, @@ -476,7 +461,7 @@ extend(Transport.prototype, Timeouts); module.exports = Transport; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(10))) +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(15))) /***/ }), /* 5 */ @@ -485,7 +470,228 @@ module.exports = Transport; "use strict"; -var asap = __webpack_require__(18); +var errors = module.exports; +var canCapture = typeof Error.captureStackTrace === 'function'; +var canStack = !!new Error().stack; +/** + * Abstract error object + * @class ErrorAbstract + * @access private + * @param {string} [msg] Error message + * @param {function} constructor + */ + +function ErrorAbstract(msg, constructor) { + this.message = msg; + Error.call(this, this.message); + /* istanbul ignore else */ + + if (canCapture) { + Error.captureStackTrace(this, constructor); + } else if (canStack) { + this.stack = new Error().stack; + } else { + this.stack = ''; + } +} + +errors._Abstract = ErrorAbstract; +ErrorAbstract.prototype = new Error(); +/** + * FeedError + * @class FeedError + * @access private + * @extends ErrorAbstract + * @memberof Stream.errors + * @param {String} [msg] - An error message that will probably end up in a log. + */ + +errors.FeedError = function FeedError(msg) { + ErrorAbstract.call(this, msg); +}; + +errors.FeedError.prototype = new ErrorAbstract(); +/** + * SiteError + * @class SiteError + * @access private + * @extends ErrorAbstract + * @memberof Stream.errors + * @param {string} [msg] An error message that will probably end up in a log. + */ + +errors.SiteError = function SiteError(msg) { + ErrorAbstract.call(this, msg); +}; + +errors.SiteError.prototype = new ErrorAbstract(); +/** + * MissingSchemaError + * @method MissingSchemaError + * @access private + * @extends ErrorAbstract + * @memberof Stream.errors + * @param {string} msg + */ + +errors.MissingSchemaError = function MissingSchemaError(msg) { + ErrorAbstract.call(this, msg); +}; + +errors.MissingSchemaError.prototype = new ErrorAbstract(); +/** + * StreamApiError + * @method StreamApiError + * @access private + * @extends ErrorAbstract + * @memberof Stream.errors + * @param {string} msg + * @param {object} data + * @param {object} response + */ + +errors.StreamApiError = function StreamApiError(msg, data, response) { + this.error = data; + this.response = response; + ErrorAbstract.call(this, msg); +}; + +errors.StreamApiError.prototype = new ErrorAbstract(); + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { + +var Promise = __webpack_require__(9); + +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__(2))) + +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +// http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/ + +module.exports = function(object) { + return JSON.stringify(object, function(key, value) { + return (this[key] instanceof Array) ? this[key] : value; + }); +}; + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var toJSON = __webpack_require__(7); + +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; + + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var asap = __webpack_require__(23); var PENDING = 0, FULFILLED = 1, @@ -647,228 +853,362 @@ module.exports = Promise; /***/ }), -/* 6 */ +/* 10 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toJSON = __webpack_require__(7); +var copyObject = function(object) { + var clone, i, key; + if (object instanceof Array) { + clone = []; + i = object.length; + while (i--) clone[i] = copyObject(object[i]); + return clone; + } else if (typeof object === 'object') { + clone = (object === null) ? null : {}; + for (key in object) clone[key] = copyObject(object[key]); + return clone; + } else { + return object; + } +}; -var Logging = { - LOG_LEVELS: { - fatal: 4, - error: 3, - warn: 2, - info: 1, - debug: 0 - }, +module.exports = copyObject; - 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, +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { - message = args.shift().replace(/\?/g, function() { - try { - return toJSON(args.shift()); - } catch (error) { - return '[Object]'; - } - }); +"use strict"; - if (klass) banner += '.' + klass; - banner += '] '; - if (typeof logger[level] === 'function') - logger[level](banner + message); - else if (typeof logger === 'function') - logger(banner + message); +var extend = __webpack_require__(0), + EventEmitter = __webpack_require__(40); + +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]); + } } }; -for (var key in Logging.LOG_LEVELS) - (function(level) { - Logging[level] = function() { - this.writeLog(arguments, level); - }; - })(key); +extend(Publisher, EventEmitter.prototype); +Publisher.trigger = Publisher.emit; -module.exports = Logging; +module.exports = Publisher; /***/ }), -/* 7 */ +/* 12 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* WEBPACK VAR INJECTION */(function(global) { + +var Event = { + _registry: [], + on: function(element, eventName, callback, context) { + var wrapped = function() { callback.call(context) }; -// http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/ + if (element.addEventListener) + element.addEventListener(eventName, wrapped, false); + else + element.attachEvent('on' + eventName, wrapped); -module.exports = function(object) { - return JSON.stringify(object, function(key, value) { - return (this[key] instanceof Array) ? this[key] : value; - }); + this._registry.push({ + _element: element, + _type: eventName, + _callback: callback, + _context: context, + _handler: wrapped + }); + }, + + detach: function(element, eventName, callback, context) { + var i = this._registry.length, register; + while (i--) { + register = this._registry[i]; + + if ((element && element !== register._element) || + (eventName && eventName !== register._type) || + (callback && callback !== register._callback) || + (context && context !== register._context)) + continue; + + if (register._element.removeEventListener) + register._element.removeEventListener(register._type, register._handler, false); + else + register._element.detachEvent('on' + register._type, register._handler); + + this._registry.splice(i,1); + register = null; + } + } +}; + +if (global.onunload !== undefined) + Event.on(global, 'unload', Event.detach, Event); + +module.exports = { + Event: Event }; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 8 */ +/* 13 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(global) { -var Promise = __webpack_require__(5); 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); + commonElement: function(lista, listb) { + for (var i = 0, n = lista.length; i < n; i++) { + if (this.indexOf(listb, lista[i]) !== -1) + return lista[i]; + } + return null; }, - callback: function(callback, context) { - return this.then(function(value) { callback.call(context, value) }); + indexOf: function(list, needle) { + if (list.indexOf) return list.indexOf(needle); + + for (var i = 0, n = list.length; i < n; i++) { + if (list[i] === needle) return i; + } + return -1; }, - errback: function(callback, context) { - return this.then(null, function(reason) { callback.call(context, reason) }); + map: function(object, callback, context) { + if (object.map) return object.map(callback, context); + var result = []; + + if (object instanceof Array) { + for (var i = 0, n = object.length; i < n; i++) { + result.push(callback.call(context || null, object[i], i)); + } + } else { + for (var key in object) { + if (!object.hasOwnProperty(key)) continue; + result.push(callback.call(context || null, key, object[key])); + } + } + return result; }, - timeout: function(seconds, message) { - this.then(); - var self = this; - this._timer = global.setTimeout(function() { - self._reject(message); - }, seconds * 1000); + filter: function(array, callback, context) { + if (array.filter) return array.filter(callback, context); + var result = []; + for (var i = 0, n = array.length; i < n; i++) { + if (callback.call(context || null, array[i], i)) + result.push(array[i]); + } + return result; }, - setDeferredStatus: function(status, value) { - if (this._timer) global.clearTimeout(this._timer); + asyncEach: function(list, iterator, callback, context) { + var n = list.length, + i = -1, + calls = 0, + looping = false; - this.then(); + var iterate = function() { + calls -= 1; + i += 1; + if (i === n) return callback && callback.call(context); + iterator(list[i], resume); + }; - if (status === 'succeeded') - this._resolve(value); - else if (status === 'failed') - this._reject(value); - else - delete this._promise; + var loop = function() { + if (looping) return; + looping = true; + while (calls > 0) iterate(); + looping = false; + }; + + var resume = function() { + calls += 1; + loop(); + }; + resume(); } }; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 9 */ +/* 14 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var errors = module.exports; -var canCapture = typeof Error.captureStackTrace === 'function'; -var canStack = !!new Error().stack; -/** - * Abstract error object - * @class ErrorAbstract - * @access private - * @param {string} [msg] Error message - * @param {function} constructor - */ - -function ErrorAbstract(msg, constructor) { - this.message = msg; - Error.call(this, this.message); - /* istanbul ignore else */ +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } - if (canCapture) { - Error.captureStackTrace(this, constructor); - } else if (canStack) { - this.stack = new Error().stack; - } else { - this.stack = ''; - } -} +var crypto = __webpack_require__(51); -errors._Abstract = ErrorAbstract; -ErrorAbstract.prototype = new Error(); -/** - * FeedError - * @class FeedError - * @access private - * @extends ErrorAbstract - * @memberof Stream.errors - * @param {String} [msg] - An error message that will probably end up in a log. - */ +var jwt = __webpack_require__(50); -errors.FeedError = function FeedError(msg) { - ErrorAbstract.call(this, msg); -}; +var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/; -errors.FeedError.prototype = new ErrorAbstract(); -/** - * SiteError - * @class SiteError - * @access private - * @extends ErrorAbstract - * @memberof Stream.errors - * @param {string} [msg] An error message that will probably end up in a log. - */ +var Base64 = __webpack_require__(49); -errors.SiteError = function SiteError(msg) { - ErrorAbstract.call(this, msg); -}; +function makeUrlSafe(s) { + /* + * Makes the given base64 encoded string urlsafe + */ + var escaped = s.replace(/\+/g, '-').replace(/\//g, '_'); + return escaped.replace(/^=+/, '').replace(/=+$/, ''); +} -errors.SiteError.prototype = new ErrorAbstract(); -/** - * MissingSchemaError - * @method MissingSchemaError - * @access private - * @extends ErrorAbstract - * @memberof Stream.errors - * @param {string} msg - */ +function decodeBase64Url(base64UrlString) { + try { + return Base64.atob(toBase64(base64UrlString)); + } catch (e) { + /* istanbul ignore else */ + if (e.name === 'InvalidCharacterError') { + return undefined; + } else { + throw e; + } + } +} -errors.MissingSchemaError = function MissingSchemaError(msg) { - ErrorAbstract.call(this, msg); +function safeJsonParse(thing) { + if (_typeof(thing) === 'object') return thing; + + try { + return JSON.parse(thing); + } catch (e) { + return undefined; + } +} + +function padString(string) { + var segmentLength = 4; + var diff = string.length % segmentLength; + if (!diff) return string; + var padLength = segmentLength - diff; + + while (padLength--) { + string += '='; + } + + return string; +} + +function toBase64(base64UrlString) { + var b64str = padString(base64UrlString).replace(/\-/g, '+') // eslint-disable-line no-useless-escape + .replace(/_/g, '/'); + return b64str; +} + +function headerFromJWS(jwsSig) { + var encodedHeader = jwsSig.split('.', 1)[0]; + return safeJsonParse(decodeBase64Url(encodedHeader)); +} + +exports.headerFromJWS = headerFromJWS; + +exports.sign = function (apiSecret, feedId) { + /* + * Setup sha1 based on the secret + * Get the digest of the value + * Base64 encode the result + * + * Also see + * https://github.com/tbarbugli/stream-ruby/blob/master/lib/stream/signer.rb + * https://github.com/tschellenbach/stream-python/blob/master/stream/signing.py + * + * Steps + * apiSecret: tfq2sdqpj9g446sbv653x3aqmgn33hsn8uzdc9jpskaw8mj6vsnhzswuwptuj9su + * feedId: flat1 + * digest: Q\xb6\xd5+\x82\xd58\xdeu\x80\xc5\xe3\xb8\xa5bL1\xf1\xa3\xdb + * token: UbbVK4LVON51gMXjuKViTDHxo9s + */ + var hashedSecret = new crypto.createHash('sha1').update(apiSecret).digest(); + var hmac = crypto.createHmac('sha1', hashedSecret); + var digest = hmac.update(feedId).digest('base64'); + var token = makeUrlSafe(digest); + return token; }; -errors.MissingSchemaError.prototype = new ErrorAbstract(); -/** - * StreamApiError - * @method StreamApiError - * @access private - * @extends ErrorAbstract - * @memberof Stream.errors - * @param {string} msg - * @param {object} data - * @param {object} response - */ +exports.JWTScopeToken = function (apiSecret, resource, action, opts) { + /** + * Creates the JWT token for feedId, resource and action using the apiSecret + * @method JWTScopeToken + * @memberof signing + * @private + * @param {string} apiSecret - API Secret key + * @param {string} resource - JWT payload resource + * @param {string} action - JWT payload action + * @param {object} [options] - Optional additional options + * @param {string} [options.feedId] - JWT payload feed identifier + * @param {string} [options.userId] - JWT payload user identifier + * @return {string} JWT Token + */ + var options = opts || {}, + noTimestamp = options.expireTokens ? !options.expireTokens : true; + var payload = { + resource: resource, + action: action + }; -errors.StreamApiError = function StreamApiError(msg, data, response) { - this.error = data; - this.response = response; - ErrorAbstract.call(this, msg); + if (options.feedId) { + payload['feed_id'] = options.feedId; + } + + if (options.userId) { + payload['user_id'] = options.userId; + } + + var token = jwt.sign(payload, apiSecret, { + algorithm: 'HS256', + noTimestamp: noTimestamp + }); + return token; }; -errors.StreamApiError.prototype = new ErrorAbstract(); +exports.isJWTSignature = function (signature) { + /** + * check if token is a valid JWT token + * @method isJWTSignature + * @memberof signing + * @private + * @param {string} signature - Signature to check + * @return {boolean} + */ + var token = signature.split(' ')[1] || signature; + return JWS_REGEX.test(token) && !!headerFromJWS(token); +}; /***/ }), -/* 10 */ +/* 15 */ /***/ (function(module, exports) { // shim for using process in browser @@ -1058,462 +1398,629 @@ process.umask = function() { return 0; }; /***/ }), -/* 11 */ +/* 16 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } +var extend = __webpack_require__(0); -var crypto = __webpack_require__(31); +var Scheduler = function(message, options) { + this.message = message; + this.options = options; + this.attempts = 0; +}; -var jwt = __webpack_require__(32); +extend(Scheduler.prototype, { + getTimeout: function() { + return this.options.timeout; + }, -var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/; + getInterval: function() { + return this.options.interval; + }, -var Base64 = __webpack_require__(33); + isDeliverable: function() { + var attempts = this.options.attempts, + made = this.attempts, + deadline = this.options.deadline, + now = new Date().getTime(); -function makeUrlSafe(s) { - /* - * Makes the given base64 encoded string urlsafe - */ - var escaped = s.replace(/\+/g, '-').replace(/\//g, '_'); - return escaped.replace(/^=+/, '').replace(/=+$/, ''); -} + if (attempts !== undefined && made >= attempts) + return false; -function decodeBase64Url(base64UrlString) { - try { - return Base64.atob(toBase64(base64UrlString)); - } catch (e) { - /* istanbul ignore else */ - if (e.name === 'InvalidCharacterError') { - return undefined; - } else { - throw e; - } - } -} + if (deadline !== undefined && now > deadline) + return false; -function safeJsonParse(thing) { - if (_typeof(thing) === 'object') return thing; + return true; + }, - try { - return JSON.parse(thing); - } catch (e) { - return undefined; - } -} + send: function() { + this.attempts += 1; + }, -function padString(string) { - var segmentLength = 4; - var diff = string.length % segmentLength; - if (!diff) return string; - var padLength = segmentLength - diff; + succeed: function() {}, - while (padLength--) { - string += '='; - } + fail: function() {}, - return string; -} + abort: function() {} +}); -function toBase64(base64UrlString) { - var b64str = padString(base64UrlString).replace(/\-/g, '+') // eslint-disable-line no-useless-escape - .replace(/_/g, '/'); - return b64str; -} +module.exports = Scheduler; -function headerFromJWS(jwsSig) { - var encodedHeader = jwsSig.split('.', 1)[0]; - return safeJsonParse(decodeBase64Url(encodedHeader)); -} -exports.headerFromJWS = headerFromJWS; +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { -exports.sign = function (apiSecret, feedId) { - /* - * Setup sha1 based on the secret - * Get the digest of the value - * Base64 encode the result - * - * Also see - * https://github.com/tbarbugli/stream-ruby/blob/master/lib/stream/signer.rb - * https://github.com/tschellenbach/stream-python/blob/master/stream/signing.py - * - * Steps - * apiSecret: tfq2sdqpj9g446sbv653x3aqmgn33hsn8uzdc9jpskaw8mj6vsnhzswuwptuj9su - * feedId: flat1 - * digest: Q\xb6\xd5+\x82\xd58\xdeu\x80\xc5\xe3\xb8\xa5bL1\xf1\xa3\xdb - * token: UbbVK4LVON51gMXjuKViTDHxo9s - */ - var hashedSecret = new crypto.createHash('sha1').update(apiSecret).digest(); - var hmac = crypto.createHmac('sha1', hashedSecret); - var digest = hmac.update(feedId).digest('base64'); - var token = makeUrlSafe(digest); - return token; -}; +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { -exports.JWTScopeToken = function (apiSecret, resource, action, opts) { - /** - * Creates the JWT token for feedId, resource and action using the apiSecret - * @method JWTScopeToken - * @memberof signing - * @private - * @param {string} apiSecret - API Secret key - * @param {string} resource - JWT payload resource - * @param {string} action - JWT payload action - * @param {object} [options] - Optional additional options - * @param {string} [options.feedId] - JWT payload feed identifier - * @param {string} [options.userId] - JWT payload user identifier - * @return {string} JWT Token - */ - var options = opts || {}, - noTimestamp = options.expireTokens ? !options.expireTokens : true; - var payload = { - resource: resource, - action: action - }; +var Class = __webpack_require__(1), + URI = __webpack_require__(3), + browser = __webpack_require__(12), + extend = __webpack_require__(0), + toJSON = __webpack_require__(7), + Transport = __webpack_require__(4); - if (options.feedId) { - payload['feed_id'] = options.feedId; +var XHR = extend(Class(Transport, { + encode: function(messages) { + return toJSON(messages); + }, + + request: function(messages) { + var href = this.endpoint.href, + self = this, + xhr; + + // Prefer XMLHttpRequest over ActiveXObject if they both exist + if (global.XMLHttpRequest) { + xhr = new XMLHttpRequest(); + } else if (global.ActiveXObject) { + xhr = new ActiveXObject('Microsoft.XMLHTTP'); + } else { + return this._handleError(messages); + } + + xhr.open('POST', href, true); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('Pragma', 'no-cache'); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + + var headers = this._dispatcher.headers; + for (var key in headers) { + if (!headers.hasOwnProperty(key)) continue; + xhr.setRequestHeader(key, headers[key]); + } + + var abort = function() { xhr.abort() }; + if (global.onbeforeunload !== undefined) + browser.Event.on(global, 'beforeunload', abort); + + xhr.onreadystatechange = function() { + if (!xhr || xhr.readyState !== 4) return; + + var replies = null, + status = xhr.status, + text = xhr.responseText, + successful = (status >= 200 && status < 300) || status === 304 || status === 1223; + + if (global.onbeforeunload !== undefined) + browser.Event.detach(global, 'beforeunload', abort); + + xhr.onreadystatechange = function() {}; + xhr = null; + + if (!successful) return self._handleError(messages); + + try { + replies = JSON.parse(text); + } catch (error) {} + + if (replies) + self._receive(replies); + else + self._handleError(messages); + }; + + xhr.send(this.encode(messages)); + return xhr; } +}), { + isUsable: function(dispatcher, endpoint, callback, context) { + var usable = (navigator.product === 'ReactNative') + || URI.isSameOrigin(endpoint); - if (options.userId) { - payload['user_id'] = options.userId; + callback.call(context, usable); } +}); - var token = jwt.sign(payload, apiSecret, { - algorithm: 'HS256', - noTimestamp: noTimestamp - }); - return token; -}; +module.exports = XHR; -exports.isJWTSignature = function (signature) { - /** - * check if token is a valid JWT token - * @method isJWTSignature - * @memberof signing - * @private - * @param {string} signature - Signature to check - * @return {boolean} - */ - var token = signature.split(' ')[1] || signature; - return JWS_REGEX.test(token) && !!headerFromJWS(token); -}; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 12 */ +/* 18 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = { - commonElement: function(lista, listb) { - for (var i = 0, n = lista.length; i < n; i++) { - if (this.indexOf(listb, lista[i]) !== -1) - return lista[i]; - } - return null; +var Class = __webpack_require__(1); + +module.exports = Class({ + initialize: function() { + this._index = {}; }, - indexOf: function(list, needle) { - if (list.indexOf) return list.indexOf(needle); + add: function(item) { + var key = (item.id !== undefined) ? item.id : item; + if (this._index.hasOwnProperty(key)) return false; + this._index[key] = item; + return true; + }, - for (var i = 0, n = list.length; i < n; i++) { - if (list[i] === needle) return i; + forEach: function(block, context) { + for (var key in this._index) { + if (this._index.hasOwnProperty(key)) + block.call(context, this._index[key]); } - return -1; }, - map: function(object, callback, context) { - if (object.map) return object.map(callback, context); - var result = []; - - if (object instanceof Array) { - for (var i = 0, n = object.length; i < n; i++) { - result.push(callback.call(context || null, object[i], i)); - } - } else { - for (var key in object) { - if (!object.hasOwnProperty(key)) continue; - result.push(callback.call(context || null, key, object[key])); - } + isEmpty: function() { + for (var key in this._index) { + if (this._index.hasOwnProperty(key)) return false; } - return result; + return true; }, - filter: function(array, callback, context) { - if (array.filter) return array.filter(callback, context); - var result = []; - for (var i = 0, n = array.length; i < n; i++) { - if (callback.call(context || null, array[i], i)) - result.push(array[i]); + member: function(item) { + for (var key in this._index) { + if (this._index[key] === item) return true; } - return result; + return false; }, - asyncEach: function(list, iterator, callback, context) { - var n = list.length, - i = -1, - calls = 0, - looping = false; - - var iterate = function() { - calls -= 1; - i += 1; - if (i === n) return callback && callback.call(context); - iterator(list[i], resume); - }; - - var loop = function() { - if (looping) return; - looping = true; - while (calls > 0) iterate(); - looping = false; - }; + remove: function(item) { + var key = (item.id !== undefined) ? item.id : item; + var removed = this._index[key]; + delete this._index[key]; + return removed; + }, - var resume = function() { - calls += 1; - loop(); - }; - resume(); + toArray: function() { + var array = []; + this.forEach(function(item) { array.push(item) }); + return array; } -}; +}); /***/ }), -/* 13 */ +/* 19 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(global) { - -var Event = { - _registry: [], - on: function(element, eventName, callback, context) { - var wrapped = function() { callback.call(context) }; - if (element.addEventListener) - element.addEventListener(eventName, wrapped, false); - else - element.attachEvent('on' + eventName, wrapped); +module.exports = {}; - this._registry.push({ - _element: element, - _type: eventName, - _callback: callback, - _context: context, - _handler: wrapped - }); - }, - detach: function(element, eventName, callback, context) { - var i = this._registry.length, register; - while (i--) { - register = this._registry[i]; +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { - if ((element && element !== register._element) || - (eventName && eventName !== register._type) || - (callback && callback !== register._callback) || - (context && context !== register._context)) - continue; +"use strict"; - if (register._element.removeEventListener) - register._element.removeEventListener(register._type, register._handler, false); - else - register._element.detachEvent('on' + register._type, register._handler); - - this._registry.splice(i,1); - register = null; - } - } -}; - -if (global.onunload !== undefined) - Event.on(global, 'unload', Event.detach, Event); module.exports = { - Event: Event + CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/, + CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/, + ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/, + VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/ }; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 14 */ +/* 21 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(0), - EventEmitter = __webpack_require__(42); +var Class = __webpack_require__(1), + extend = __webpack_require__(0), + Publisher = __webpack_require__(11), + Grammar = __webpack_require__(20); -var Publisher = { - countListeners: function(eventType) { - return this.listeners(eventType).length; +var Channel = Class({ + initialize: function(name) { + this.id = this.name = name; }, - 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); + push: function(message) { + this.trigger('message', message); }, - 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]); - } + isUnused: function() { + return this.countListeners('message') === 0; } -}; +}); -extend(Publisher, EventEmitter.prototype); -Publisher.trigger = Publisher.emit; +extend(Channel.prototype, Publisher); -module.exports = Publisher; +extend(Channel, { + HANDSHAKE: '/meta/handshake', + CONNECT: '/meta/connect', + SUBSCRIBE: '/meta/subscribe', + UNSUBSCRIBE: '/meta/unsubscribe', + DISCONNECT: '/meta/disconnect', + META: 'meta', + SERVICE: 'service', -/***/ }), -/* 15 */ -/***/ (function(module, exports, __webpack_require__) { + expand: function(name) { + var segments = this.parse(name), + channels = ['/**', name]; -"use strict"; + 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)); + } -var copyObject = function(object) { - var clone, i, key; - if (object instanceof Array) { - clone = []; - i = object.length; - while (i--) clone[i] = copyObject(object[i]); - return clone; - } else if (typeof object === 'object') { - clone = (object === null) ? null : {}; - for (key in object) clone[key] = copyObject(object[key]); - return clone; - } else { - return object; - } -}; + return channels; + }, -module.exports = copyObject; + 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); + }, -/***/ }), -/* 16 */ -/***/ (function(module, exports) { + unparse: function(segments) { + return '/' + segments.join('/'); + }, -// Browser Request -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + isMeta: function(name) { + var segments = this.parse(name); + return segments ? (segments[0] === this.META) : null; + }, -var XHR = XMLHttpRequest -if (!XHR) throw new Error('missing XMLHttpRequest') -request.log = { - 'trace': noop, 'debug': noop, 'info': noop, 'warn': noop, 'error': noop -} + isService: function(name) { + var segments = this.parse(name); + return segments ? (segments[0] === this.SERVICE) : null; + }, -var DEFAULT_TIMEOUT = 3 * 60 * 1000 // 3 minutes + isSubscribable: function(name) { + if (!this.isValid(name)) return null; + return !this.isMeta(name) && !this.isService(name); + }, -// -// PolyFill -// + Set: Class({ + initialize: function() { + this._channels = {}; + }, -// Production steps of ECMA-262, Edition 5, 15.4.4.18 -// Reference: http://es5.github.io/#x15.4.4.18 -if (!Array.prototype.forEach) { + getKeys: function() { + var keys = []; + for (var key in this._channels) keys.push(key); + return keys; + }, - Array.prototype.forEach = function(callback, thisArg) { + remove: function(name) { + delete this._channels[name]; + }, - var T, k; + hasSubscription: function(name) { + return this._channels.hasOwnProperty(name); + }, - if (this === null) { - throw new TypeError(' this is null or not defined'); - } + 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); + } + }, - // 1. Let O be the result of calling toObject() passing the - // |this| value as the argument. - var O = Object(this); + unsubscribe: function(name, subscription) { + var channel = this._channels[name]; + if (!channel) return false; + channel.unbind('message', subscription); - // 2. Let lenValue be the result of calling the Get() internal - // method of O with the argument "length". - // 3. Let len be toUint32(lenValue). - var len = O.length >>> 0; + if (channel.isUnused()) { + this.remove(name); + return true; + } else { + return false; + } + }, - // 4. If isCallable(callback) is false, throw a TypeError exception. - // See: http://es5.github.com/#x9.11 - if (typeof callback !== "function") { - throw new TypeError(callback + ' is not a function'); - } + distributeMessage: function(message) { + var channels = Channel.expand(message.channel); - // 5. If thisArg was supplied, let T be thisArg; else let - // T be undefined. - if (arguments.length > 1) { - T = thisArg; + for (var i = 0, n = channels.length; i < n; i++) { + var channel = this._channels[channels[i]]; + if (channel) channel.trigger('message', message); + } } + }) +}); - // 6. Let k be 0 - k = 0; +module.exports = Channel; - // 7. Repeat, while k < len - while (k < len) { - var kValue; +/***/ }), +/* 22 */ +/***/ (function(module, exports) { - // a. Let Pk be ToString(k). - // This is implicit for LHS operands of the in operator - // b. Let kPresent be the result of calling the HasProperty - // internal method of O with argument Pk. - // This step can be combined with c - // c. If kPresent is true, then - if (k in O) { +module.exports = { + VERSION: '1.2.4', - // i. Let kValue be the result of calling the Get internal - // method of O with argument Pk. - kValue = O[k]; + BAYEUX_VERSION: '1.0', + ID_LENGTH: 160, + JSONP_CALLBACK: 'jsonpcallback', + CONNECTION_TYPES: ['long-polling', 'cross-origin-long-polling', 'callback-polling', 'websocket', 'eventsource', 'in-process'], - // ii. Call the Call internal method of callback with T as - // the this value and argument list containing kValue, k, and O. - callback.call(T, kValue, k, O); - } - // d. Increase k by 1. - k++; - } - // 8. return undefined - }; -} + MANDATORY_CONNECTION_TYPES: ['long-polling', 'callback-polling', 'in-process'] +}; -// -// request -// -function request(options, callback) { - // The entry-point to the API: prep the options object and pass the real work to run_xhr. - if(typeof callback !== 'function') - throw new Error('Bad callback given: ' + callback) +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +// rawAsap provides everything we need except exception management. +var rawAsap = __webpack_require__(46); +// 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; + } +}; + + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var errors = __webpack_require__(5); + +var validFeedSlugRe = /^[\w]+$/; +var validUserIdRe = /^[\w-]+$/; + +function validateFeedId(feedId) { + /* + * Validate that the feedId matches the spec user:1 + */ + var parts = feedId.split(':'); + + if (parts.length !== 2) { + throw new errors.FeedError('Invalid feedId, expected something like user:1 got ' + feedId); + } + + var feedSlug = parts[0]; + var userId = parts[1]; + validateFeedSlug(feedSlug); + validateUserId(userId); + return feedId; +} + +exports.validateFeedId = validateFeedId; + +function validateFeedSlug(feedSlug) { + /* + * Validate that the feedSlug matches \w + */ + var valid = validFeedSlugRe.test(feedSlug); + + if (!valid) { + throw new errors.FeedError('Invalid feedSlug, please use letters, numbers or _: ' + feedSlug); + } + + return feedSlug; +} + +exports.validateFeedSlug = validateFeedSlug; + +function validateUserId(userId) { + /* + * Validate the userId matches \w + */ + var valid = validUserIdRe.test(userId); + + if (!valid) { + throw new errors.FeedError('Invalid userId, please use letters, numbers, - or _: ' + userId); + } + + return userId; +} + +exports.validateUserId = validateUserId; + +function rfc3986(str) { + return str.replace(/[!'()*]/g, function (c) { + return '%' + c.charCodeAt(0).toString(16).toUpperCase(); + }); +} + +exports.rfc3986 = rfc3986; + +/***/ }), +/* 25 */ +/***/ (function(module, exports) { + +// Browser Request +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var XHR = XMLHttpRequest +if (!XHR) throw new Error('missing XMLHttpRequest') +request.log = { + 'trace': noop, 'debug': noop, 'info': noop, 'warn': noop, 'error': noop +} + +var DEFAULT_TIMEOUT = 3 * 60 * 1000 // 3 minutes + +// +// PolyFill +// + +// Production steps of ECMA-262, Edition 5, 15.4.4.18 +// Reference: http://es5.github.io/#x15.4.4.18 +if (!Array.prototype.forEach) { + + Array.prototype.forEach = function(callback, thisArg) { + + var T, k; + + if (this === null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Let O be the result of calling toObject() passing the + // |this| value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get() internal + // method of O with the argument "length". + // 3. Let len be toUint32(lenValue). + var len = O.length >>> 0; + + // 4. If isCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + // 5. If thisArg was supplied, let T be thisArg; else let + // T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Let k be 0 + k = 0; + + // 7. Repeat, while k < len + while (k < len) { + + var kValue; + + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty + // internal method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + + // i. Let kValue be the result of calling the Get internal + // method of O with argument Pk. + kValue = O[k]; + + // ii. Call the Call internal method of callback with T as + // the this value and argument list containing kValue, k, and O. + callback.call(T, kValue, k, O); + } + // d. Increase k by 1. + k++; + } + // 8. return undefined + }; +} + +// +// request +// + +function request(options, callback) { + // The entry-point to the API: prep the options object and pass the real work to run_xhr. + if(typeof callback !== 'function') + throw new Error('Bad callback given: ' + callback) if(!options) throw new Error('No options given') @@ -1968,2383 +2475,1221 @@ module.exports = request; /***/ }), -/* 17 */ +/* 26 */ +/***/ (function(module, exports) { + +/* (ignored) */ + +/***/ }), +/* 27 */ +/***/ (function(module, exports) { + +/* (ignored) */ + +/***/ }), +/* 28 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var errors = __webpack_require__(9); +var Class = __webpack_require__(1), + extend = __webpack_require__(0), + Deferrable = __webpack_require__(6); -var validFeedSlugRe = /^[\w]+$/; -var validUserIdRe = /^[\w-]+$/; +var Subscription = Class({ + initialize: function(client, channels, callback, context) { + this._client = client; + this._channels = channels; + this._callback = callback; + this._context = context; + this._cancelled = false; + }, -function validateFeedId(feedId) { - /* - * Validate that the feedId matches the spec user:1 - */ - var parts = feedId.split(':'); + withChannel: function(callback, context) { + this._withChannel = [callback, context]; + return this; + }, - if (parts.length !== 2) { - throw new errors.FeedError('Invalid feedId, expected something like user:1 got ' + feedId); - } + apply: function(context, args) { + var message = args[0]; - var feedSlug = parts[0]; - var userId = parts[1]; - validateFeedSlug(feedSlug); - validateUserId(userId); - return feedId; -} + if (this._callback) + this._callback.call(this._context, message.data); -exports.validateFeedId = validateFeedId; + if (this._withChannel) + this._withChannel[0].call(this._withChannel[1], message.channel, message.data); + }, -function validateFeedSlug(feedSlug) { - /* - * Validate that the feedSlug matches \w - */ - var valid = validFeedSlugRe.test(feedSlug); + cancel: function() { + if (this._cancelled) return; + this._client.unsubscribe(this._channels, this); + this._cancelled = true; + }, - if (!valid) { - throw new errors.FeedError('Invalid feedSlug, please use letters, numbers or _: ' + feedSlug); + unsubscribe: function() { + this.cancel(); } +}); - return feedSlug; -} +extend(Subscription.prototype, Deferrable); -exports.validateFeedSlug = validateFeedSlug; +module.exports = Subscription; -function validateUserId(userId) { - /* - * Validate the userId matches \w - */ - var valid = validUserIdRe.test(userId); - if (!valid) { - throw new errors.FeedError('Invalid userId, please use letters, numbers, - or _: ' + userId); - } +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { - return userId; -} +"use strict"; -exports.validateUserId = validateUserId; -function rfc3986(str) { - return str.replace(/[!'()*]/g, function (c) { - return '%' + c.charCodeAt(0).toString(16).toUpperCase(); - }); -} +var Class = __webpack_require__(1), + Deferrable = __webpack_require__(6); + +module.exports = Class(Deferrable); -exports.rfc3986 = rfc3986; /***/ }), -/* 18 */ +/* 30 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -// rawAsap provides everything we need except exception management. -var rawAsap = __webpack_require__(36); -// 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); +var extend = __webpack_require__(0), + Logging = __webpack_require__(8); -function throwFirstError() { - if (pendingErrors.length) { - throw pendingErrors.shift(); - } -} +var Extensible = { + addExtension: function(extension) { + this._extensions = this._extensions || []; + this._extensions.push(extension); + if (extension.added) extension.added(this); + }, -/** - * 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(); + removeExtension: function(extension) { + if (!this._extensions) return; + var i = this._extensions.length; + while (i--) { + if (this._extensions[i] !== extension) continue; + this._extensions.splice(i,1); + if (extension.removed) extension.removed(this); } - 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; - } -}; + pipeThroughExtensions: function(stage, message, request, callback, context) { + this.debug('Passing through ? extensions: ?', stage, message); + if (!this._extensions) return callback.call(context, message); + var extensions = this._extensions.slice(); -/***/ }), -/* 19 */ -/***/ (function(module, exports) { + var pipe = function(message) { + if (!message) return callback.call(context, message); -module.exports = { - VERSION: '1.2.4', + var extension = extensions.shift(); + if (!extension) return callback.call(context, message); - BAYEUX_VERSION: '1.0', - ID_LENGTH: 160, - JSONP_CALLBACK: 'jsonpcallback', - CONNECTION_TYPES: ['long-polling', 'cross-origin-long-polling', 'callback-polling', 'websocket', 'eventsource', 'in-process'], + var fn = extension[stage]; + if (!fn) return pipe(message); - MANDATORY_CONNECTION_TYPES: ['long-polling', 'callback-polling', 'in-process'] + if (fn.length >= 3) extension[stage](message, request, pipe); + else extension[stage](message, pipe); + }; + pipe(message); + } }; +extend(Extensible, Logging); + +module.exports = Extensible; + /***/ }), -/* 20 */ +/* 31 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Class = __webpack_require__(1), - extend = __webpack_require__(0), - Publisher = __webpack_require__(14), - Grammar = __webpack_require__(21); - -var Channel = Class({ - initialize: function(name) { - this.id = this.name = name; - }, +var Class = __webpack_require__(1), + Grammar = __webpack_require__(20); - push: function(message) { - this.trigger('message', message); +var Error = Class({ + initialize: function(code, params, message) { + this.code = code; + this.params = Array.prototype.slice.call(params); + this.message = message; }, - isUnused: function() { - return this.countListeners('message') === 0; + toString: function() { + return this.code + ':' + + this.params.join(',') + ':' + + this.message; } }); -extend(Channel.prototype, Publisher); +Error.parse = function(message) { + message = message || ''; + if (!Grammar.ERROR.test(message)) return new Error(null, [], message); -extend(Channel, { - HANDSHAKE: '/meta/handshake', - CONNECT: '/meta/connect', - SUBSCRIBE: '/meta/subscribe', - UNSUBSCRIBE: '/meta/unsubscribe', - DISCONNECT: '/meta/disconnect', + var parts = message.split(':'), + code = parseInt(parts[0]), + params = parts[1].split(','), + message = parts[2]; - META: 'meta', - SERVICE: 'service', + return new Error(code, params, message); +}; - expand: function(name) { - var segments = this.parse(name), - channels = ['/**', name]; +// 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'] +}; - var copy = segments.slice(); - copy[copy.length - 1] = '*'; - channels.push(this.unparse(copy)); +for (var name in errors) + (function(name) { + Error[name] = function() { + return new Error(errors[name][0], arguments, errors[name][1]).toString(); + }; + })(name); - 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); - }, +module.exports = Error; - parse: function(name) { - if (!this.isValid(name)) return null; - return name.split('/').slice(1); - }, - unparse: function(segments) { - return '/' + segments.join('/'); - }, +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { - isMeta: function(name) { - var segments = this.parse(name); - return segments ? (segments[0] === this.META) : null; - }, +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { - isService: function(name) { - var segments = this.parse(name); - return segments ? (segments[0] === this.SERVICE) : null; - }, +var Class = __webpack_require__(1), + URI = __webpack_require__(3), + copyObject = __webpack_require__(10), + extend = __webpack_require__(0), + toJSON = __webpack_require__(7), + Transport = __webpack_require__(4); - isSubscribable: function(name) { - if (!this.isValid(name)) return null; - return !this.isMeta(name) && !this.isService(name); +var JSONP = extend(Class(Transport, { + encode: function(messages) { + var url = copyObject(this.endpoint); + url.query.message = toJSON(messages); + url.query.jsonp = '__jsonp' + JSONP._cbCount + '__'; + return URI.stringify(url); }, - Set: Class({ - initialize: function() { - this._channels = {}; - }, + request: function(messages) { + var head = document.getElementsByTagName('head')[0], + script = document.createElement('script'), + callbackName = JSONP.getCallbackName(), + endpoint = copyObject(this.endpoint), + self = this; - getKeys: function() { - var keys = []; - for (var key in this._channels) keys.push(key); - return keys; - }, + endpoint.query.message = toJSON(messages); + endpoint.query.jsonp = callbackName; - remove: function(name) { - delete this._channels[name]; - }, + var cleanup = function() { + if (!global[callbackName]) return false; + global[callbackName] = undefined; + try { delete global[callbackName] } catch (error) {} + script.parentNode.removeChild(script); + }; - hasSubscription: function(name) { - return this._channels.hasOwnProperty(name); - }, + global[callbackName] = function(replies) { + cleanup(); + self._receive(replies); + }; - 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); - } - }, + script.type = 'text/javascript'; + script.src = URI.stringify(endpoint); + head.appendChild(script); - unsubscribe: function(name, subscription) { - var channel = this._channels[name]; - if (!channel) return false; - channel.unbind('message', subscription); + script.onerror = function() { + cleanup(); + self._handleError(messages); + }; - if (channel.isUnused()) { - this.remove(name); - return true; - } else { - return false; - } - }, + return {abort: cleanup}; + } +}), { + _cbCount: 0, - distributeMessage: function(message) { - var channels = Channel.expand(message.channel); + getCallbackName: function() { + this._cbCount += 1; + return '__jsonp' + this._cbCount + '__'; + }, - for (var i = 0, n = channels.length; i < n; i++) { - var channel = this._channels[channels[i]]; - if (channel) channel.trigger('message', message); - } - } - }) + isUsable: function(dispatcher, endpoint, callback, context) { + callback.call(context, true); + } }); -module.exports = Channel; +module.exports = JSONP; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 21 */ +/* 33 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* WEBPACK VAR INJECTION */(function(global) { +var Class = __webpack_require__(1), + Set = __webpack_require__(18), + URI = __webpack_require__(3), + extend = __webpack_require__(0), + toJSON = __webpack_require__(7), + Transport = __webpack_require__(4); -module.exports = { - CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/, - CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/, - ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/, - VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/ -}; - +var CORS = extend(Class(Transport, { + encode: function(messages) { + return 'message=' + encodeURIComponent(toJSON(messages)); + }, -/***/ }), -/* 22 */ -/***/ (function(module, exports, __webpack_require__) { + request: function(messages) { + var xhrClass = global.XDomainRequest ? XDomainRequest : XMLHttpRequest, + xhr = new xhrClass(), + id = ++CORS._id, + headers = this._dispatcher.headers, + self = this, + key; -"use strict"; + xhr.open('POST', URI.stringify(this.endpoint), true); + if (xhr.setRequestHeader) { + xhr.setRequestHeader('Pragma', 'no-cache'); + for (key in headers) { + if (!headers.hasOwnProperty(key)) continue; + xhr.setRequestHeader(key, headers[key]); + } + } -module.exports = {}; + var cleanUp = function() { + if (!xhr) return false; + CORS._pending.remove(id); + xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onprogress = null; + xhr = null; + }; + xhr.onload = function() { + var replies; + try { replies = JSON.parse(xhr.responseText) } catch (error) {} -/***/ }), -/* 23 */ -/***/ (function(module, exports, __webpack_require__) { + cleanUp(); -"use strict"; + if (replies) + self._receive(replies); + else + self._handleError(messages); + }; + xhr.onerror = xhr.ontimeout = function() { + cleanUp(); + self._handleError(messages); + }; -var Class = __webpack_require__(1); + xhr.onprogress = function() {}; -module.exports = Class({ - initialize: function() { - this._index = {}; - }, + if (xhrClass === global.XDomainRequest) + CORS._pending.add({id: id, xhr: xhr}); - add: function(item) { - var key = (item.id !== undefined) ? item.id : item; - if (this._index.hasOwnProperty(key)) return false; - this._index[key] = item; - return true; - }, + xhr.send(this.encode(messages)); + return xhr; + } +}), { + _id: 0, + _pending: new Set(), - forEach: function(block, context) { - for (var key in this._index) { - if (this._index.hasOwnProperty(key)) - block.call(context, this._index[key]); - } - }, + isUsable: function(dispatcher, endpoint, callback, context) { + if (URI.isSameOrigin(endpoint)) + return callback.call(context, false); - isEmpty: function() { - for (var key in this._index) { - if (this._index.hasOwnProperty(key)) return false; - } - return true; - }, + if (global.XDomainRequest) + return callback.call(context, endpoint.protocol === location.protocol); - member: function(item) { - for (var key in this._index) { - if (this._index[key] === item) return true; + if (global.XMLHttpRequest) { + var xhr = new XMLHttpRequest(); + return callback.call(context, xhr.withCredentials !== undefined); } - return false; - }, - - remove: function(item) { - var key = (item.id !== undefined) ? item.id : item; - var removed = this._index[key]; - delete this._index[key]; - return removed; - }, - - toArray: function() { - var array = []; - this.forEach(function(item) { array.push(item) }); - return array; + return callback.call(context, false); } }); +module.exports = CORS; + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 24 */ +/* 34 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(global) { -var Class = __webpack_require__(1), - URI = __webpack_require__(3), - browser = __webpack_require__(13), - extend = __webpack_require__(0), - toJSON = __webpack_require__(7), - Transport = __webpack_require__(4); - -var XHR = extend(Class(Transport, { - encode: function(messages) { - return toJSON(messages); - }, - - request: function(messages) { - var href = this.endpoint.href, - self = this, - xhr; - - // Prefer XMLHttpRequest over ActiveXObject if they both exist - if (global.XMLHttpRequest) { - xhr = new XMLHttpRequest(); - } else if (global.ActiveXObject) { - xhr = new ActiveXObject('Microsoft.XMLHTTP'); - } else { - return this._handleError(messages); - } - - xhr.open('POST', href, true); - xhr.setRequestHeader('Content-Type', 'application/json'); - xhr.setRequestHeader('Pragma', 'no-cache'); - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - - var headers = this._dispatcher.headers; - for (var key in headers) { - if (!headers.hasOwnProperty(key)) continue; - xhr.setRequestHeader(key, headers[key]); - } +var Class = __webpack_require__(1), + URI = __webpack_require__(3), + copyObject = __webpack_require__(10), + extend = __webpack_require__(0), + Deferrable = __webpack_require__(6), + Transport = __webpack_require__(4), + XHR = __webpack_require__(17); - var abort = function() { xhr.abort() }; - if (global.onbeforeunload !== undefined) - browser.Event.on(global, 'beforeunload', abort); +var EventSource = extend(Class(Transport, { + initialize: function(dispatcher, endpoint) { + Transport.prototype.initialize.call(this, dispatcher, endpoint); + if (!global.EventSource) return this.setDeferredStatus('failed'); - xhr.onreadystatechange = function() { - if (!xhr || xhr.readyState !== 4) return; + this._xhr = new XHR(dispatcher, endpoint); - var replies = null, - status = xhr.status, - text = xhr.responseText, - successful = (status >= 200 && status < 300) || status === 304 || status === 1223; + endpoint = copyObject(endpoint); + endpoint.pathname += '/' + dispatcher.clientId; - if (global.onbeforeunload !== undefined) - browser.Event.detach(global, 'beforeunload', abort); + var socket = new global.EventSource(URI.stringify(endpoint)), + self = this; - xhr.onreadystatechange = function() {}; - xhr = null; + socket.onopen = function() { + self._everConnected = true; + self.setDeferredStatus('succeeded'); + }; - if (!successful) return self._handleError(messages); + socket.onerror = function() { + if (self._everConnected) { + self._handleError([]); + } else { + self.setDeferredStatus('failed'); + socket.close(); + } + }; - try { - replies = JSON.parse(text); - } catch (error) {} + socket.onmessage = function(event) { + var replies; + try { replies = JSON.parse(event.data) } catch (error) {} if (replies) self._receive(replies); else - self._handleError(messages); + self._handleError([]); }; - xhr.send(this.encode(messages)); - return xhr; + this._socket = socket; + }, + + close: function() { + if (!this._socket) return; + this._socket.onopen = this._socket.onerror = this._socket.onmessage = null; + this._socket.close(); + delete this._socket; + }, + + isUsable: function(callback, context) { + this.callback(function() { callback.call(context, true) }); + this.errback(function() { callback.call(context, false) }); + }, + + encode: function(messages) { + return this._xhr.encode(messages); + }, + + request: function(messages) { + return this._xhr.request(messages); } + }), { isUsable: function(dispatcher, endpoint, callback, context) { - var usable = (navigator.product === 'ReactNative') - || URI.isSameOrigin(endpoint); + var id = dispatcher.clientId; + if (!id) return callback.call(context, false); - callback.call(context, usable); + XHR.isUsable(dispatcher, endpoint, function(usable) { + if (!usable) return callback.call(context, false); + this.create(dispatcher, endpoint).isUsable(callback, context); + }, this); + }, + + create: function(dispatcher, endpoint) { + var sockets = dispatcher.transports.eventsource = dispatcher.transports.eventsource || {}, + id = dispatcher.clientId; + + var url = copyObject(endpoint); + url.pathname += '/' + (id || ''); + url = URI.stringify(url); + + sockets[url] = sockets[url] || new this(dispatcher, endpoint); + return sockets[url]; } }); -module.exports = XHR; +extend(EventSource.prototype, Deferrable); + +module.exports = EventSource; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 25 */ +/* 35 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* WEBPACK VAR INJECTION */(function(global) { +var WS = global.MozWebSocket || global.WebSocket; -var extend = __webpack_require__(0); - -var Scheduler = function(message, options) { - this.message = message; - this.options = options; - this.attempts = 0; +module.exports = { + create: function(url, protocols, options) { + if (typeof WS !== 'function') return null; + return new WS(url); + } }; -extend(Scheduler.prototype, { - getTimeout: function() { - return this.options.timeout; - }, +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) - getInterval: function() { - return this.options.interval; - }, +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { - isDeliverable: function() { - var attempts = this.options.attempts, - made = this.attempts, - deadline = this.options.deadline, - now = new Date().getTime(); +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { - if (attempts !== undefined && made >= attempts) - return false; +var Class = __webpack_require__(1), + Promise = __webpack_require__(9), + Set = __webpack_require__(18), + URI = __webpack_require__(3), + browser = __webpack_require__(12), + copyObject = __webpack_require__(10), + extend = __webpack_require__(0), + toJSON = __webpack_require__(7), + ws = __webpack_require__(35), + Deferrable = __webpack_require__(6), + Transport = __webpack_require__(4); - if (deadline !== undefined && now > deadline) - return false; +var WebSocket = extend(Class(Transport, { + UNCONNECTED: 1, + CONNECTING: 2, + CONNECTED: 3, - return true; - }, + batching: false, - send: function() { - this.attempts += 1; + isUsable: function(callback, context) { + this.callback(function() { callback.call(context, true) }); + this.errback(function() { callback.call(context, false) }); + this.connect(); }, - succeed: function() {}, + request: function(messages) { + this._pending = this._pending || new Set(); + for (var i = 0, n = messages.length; i < n; i++) this._pending.add(messages[i]); - fail: function() {}, + var self = this; - abort: function() {} -}); + var promise = new Promise(function(resolve, reject) { + self.callback(function(socket) { + if (!socket || socket.readyState !== 1) return; + socket.send(toJSON(messages)); + resolve(socket); + }); -module.exports = Scheduler; + self.connect(); + }); + return { + abort: function() { promise.then(function(ws) { ws.close() }) } + }; + }, -/***/ }), -/* 26 */ -/***/ (function(module, exports, __webpack_require__) { + connect: function() { + if (WebSocket._unloaded) return; -"use strict"; -/* WEBPACK VAR INJECTION */(function(process) { + this._state = this._state || this.UNCONNECTED; + if (this._state !== this.UNCONNECTED) return; + this._state = this.CONNECTING; -/** - * @module stream - * @author Thierry Schellenbach - * BSD License - */ -var StreamClient = __webpack_require__(27); + var socket = this._createSocket(); + if (!socket) return this.setDeferredStatus('failed'); -var errors = __webpack_require__(9); + var self = this; -var signing = __webpack_require__(11); + socket.onopen = function() { + if (socket.headers) self._storeCookies(socket.headers['set-cookie']); + self._socket = socket; + self._state = self.CONNECTED; + self._everConnected = true; + self._ping(); + self.setDeferredStatus('succeeded', socket); + }; -var request = __webpack_require__(16); + var closed = false; + socket.onclose = socket.onerror = function() { + if (closed) return; + closed = true; -function connect(apiKey, apiSecret, appId, options) { - /** - * Create StreamClient - * @method connect - * @param {string} apiKey API key - * @param {string} [apiSecret] API secret (only use this on the server) - * @param {string} [appId] Application identifier - * @param {object} [options] Additional options - * @param {string} [options.location] Datacenter location - * @return {StreamClient} StreamClient - * @example Basic usage - * stream.connect(apiKey, apiSecret); - * @example or if you want to be able to subscribe and listen - * stream.connect(apiKey, apiSecret, appId); - * @example or on Heroku - * stream.connect(streamURL); - * @example where streamURL looks like - * "https://thierry:pass@gestream.io/?app=1" - */ - if (typeof process !== 'undefined' && process.env.STREAM_URL && !apiKey) { - var parts = /https\:\/\/(\w+)\:(\w+)\@([\w-]*).*\?app_id=(\d+)/.exec(process.env.STREAM_URL); // eslint-disable-line no-useless-escape + var wasConnected = (self._state === self.CONNECTED); + socket.onopen = socket.onclose = socket.onerror = socket.onmessage = null; - apiKey = parts[1]; - apiSecret = parts[2]; - var location = parts[3]; - appId = parts[4]; + delete self._socket; + self._state = self.UNCONNECTED; + self.removeTimeout('ping'); - if (options === undefined) { - options = {}; - } + var pending = self._pending ? self._pending.toArray() : []; + delete self._pending; - if (location !== 'getstream' && location !== 'stream-io-api') { - options.location = location; - } - } + if (wasConnected || self._everConnected) { + self.setDeferredStatus('unknown'); + self._handleError(pending, wasConnected); + } else { + self.setDeferredStatus('failed'); + } + }; - return new StreamClient(apiKey, apiSecret, appId, options); -} + socket.onmessage = function(event) { + var replies; + try { replies = JSON.parse(event.data) } catch (error) {} -module.exports.connect = connect; -module.exports.errors = errors; -module.exports.request = request; -module.exports.signing = signing; -module.exports.Client = StreamClient; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(10))) + if (!replies) return; -/***/ }), -/* 27 */ -/***/ (function(module, exports, __webpack_require__) { + replies = [].concat(replies); -"use strict"; -/* WEBPACK VAR INJECTION */(function(process) { + for (var i = 0, n = replies.length; i < n; i++) { + if (replies[i].successful === undefined) continue; + self._pending.remove(replies[i]); + } + self._receive(replies); + }; + }, -var Collections = __webpack_require__(28); + close: function() { + if (!this._socket) return; + this._socket.close(); + }, -var Personalization = __webpack_require__(29); + _createSocket: function() { + var url = WebSocket.getSocketUrl(this.endpoint), + headers = this._dispatcher.headers, + extensions = this._dispatcher.wsExtensions, + cookie = this._getCookies(), + tls = this._dispatcher.tls, + options = {extensions: extensions, headers: headers, proxy: this._proxy, tls: tls}; -var request = __webpack_require__(16); + if (cookie !== '') options.headers['Cookie'] = cookie; -var StreamFeed = __webpack_require__(30); + return ws.create(url, [], options); + }, -var signing = __webpack_require__(11); + _ping: function() { + if (!this._socket || this._socket.readyState !== 1) return; + this._socket.send('[]'); + this.addTimeout('ping', this._dispatcher.timeout / 2, this._ping, this); + } -var errors = __webpack_require__(9); +}), { + PROTOCOLS: { + 'http:': 'ws:', + 'https:': 'wss:' + }, -var utils = __webpack_require__(17); + create: function(dispatcher, endpoint) { + var sockets = dispatcher.transports.websocket = dispatcher.transports.websocket || {}; + sockets[endpoint.href] = sockets[endpoint.href] || new this(dispatcher, endpoint); + return sockets[endpoint.href]; + }, -var BatchOperations = __webpack_require__(34); + getSocketUrl: function(endpoint) { + endpoint = copyObject(endpoint); + endpoint.protocol = this.PROTOCOLS[endpoint.protocol]; + return URI.stringify(endpoint); + }, -var Promise = __webpack_require__(35); + isUsable: function(dispatcher, endpoint, callback, context) { + this.create(dispatcher, endpoint).isUsable(callback, context); + } +}); -var qs = __webpack_require__(37); +extend(WebSocket.prototype, Deferrable); -var url = __webpack_require__(38); +if (browser.Event && global.onbeforeunload !== undefined) + browser.Event.on(global, 'beforeunload', function() { WebSocket._unloaded = true }); -var Faye = __webpack_require__(39); -/** - * @callback requestCallback - * @param {object} [errors] - * @param {object} response - * @param {object} body - */ +module.exports = WebSocket; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) -var StreamClient = function StreamClient() { - /** - * Client to connect to Stream api - * @class StreamClient - */ - this.initialize.apply(this, arguments); -}; +/***/ }), +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { -StreamClient.prototype = { - baseUrl: 'https://api.stream-io-api.com/api/', - baseAnalyticsUrl: 'https://analytics.stream-io-api.com/analytics/', - initialize: function initialize(apiKey, apiSecret, appId, options) { - /** - * Initialize a client - * @method intialize - * @memberof StreamClient.prototype - * @param {string} apiKey - the api key - * @param {string} [apiSecret] - the api secret - * @param {string} [appId] - id of the app - * @param {object} [options] - additional options - * @param {string} [options.location] - which data center to use - * @param {boolean} [options.expireTokens=false] - whether to use a JWT timestamp field (i.e. iat) - * @example initialize is not directly called by via stream.connect, ie: - * stream.connect(apiKey, apiSecret) - * @example secret is optional and only used in server side mode - * stream.connect(apiKey, null, appId); - */ - this.apiKey = apiKey; - this.apiSecret = apiSecret; - this.appId = appId; - this.options = options || {}; - this.version = this.options.version || 'v1.0'; - this.fayeUrl = this.options.fayeUrl || 'https://faye.getstream.io/faye'; - this.fayeClient = null; - this.request = request; // track a source name for the api calls, ie get started or databrowser +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { - this.group = this.options.group || 'unspecified'; // track subscriptions made on feeds created by this client +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); + }, - this.subscriptions = {}; - this.expireTokens = this.options.expireTokens ? this.options.expireTokens : false; // which data center to use + removeTimeout: function(name) { + this._timeouts = this._timeouts || {}; + var timeout = this._timeouts[name]; + if (!timeout) return; + global.clearTimeout(timeout); + delete this._timeouts[name]; + }, - this.location = this.options.location; - this.baseUrl = this.getBaseUrl(); + removeAllTimeouts: function() { + this._timeouts = this._timeouts || {}; + for (var name in this._timeouts) this.removeTimeout(name); + } +}; - if (typeof process !== 'undefined' && process.env.LOCAL_FAYE) { - this.fayeUrl = 'http://localhost:9999/faye/'; - } +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) - if (typeof process !== 'undefined' && process.env.STREAM_ANALYTICS_BASE_URL) { - this.baseAnalyticsUrl = process.env.STREAM_ANALYTICS_BASE_URL; - } +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { - this.handlers = {}; - this.browser = typeof window !== 'undefined'; - this.node = !this.browser; +"use strict"; - if (!this.browser) { - var http = __webpack_require__(55); - var https = __webpack_require__(56); +var Transport = __webpack_require__(4); - var httpsAgent = new https.Agent({ - keepAlive: true, - keepAliveMsecs: 3000 - }); - var httpAgent = new http.Agent({ - keepAlive: true, - keepAliveMsecs: 3000 - }); - this.requestAgent = this.baseUrl.startsWith('https://') ? httpsAgent : httpAgent; // setup personalization and collections +Transport.register('websocket', __webpack_require__(36)); +Transport.register('eventsource', __webpack_require__(34)); +Transport.register('long-polling', __webpack_require__(17)); +Transport.register('cross-origin-long-polling', __webpack_require__(33)); +Transport.register('callback-polling', __webpack_require__(32)); - this.personalizationToken = signing.JWTScopeToken(this.apiSecret, 'personalization', '*', { - userId: '*', - feedId: '*', - expireTokens: this.expireTokens - }); - this.collectionsToken = signing.JWTScopeToken(this.apiSecret, 'collections', '*', { - userId: '*', - feedId: '*', - expireTokens: this.expireTokens - }); - this.personalization = new Personalization(this); - this.collections = new Collections(this); - } - /* istanbul ignore next */ +module.exports = Transport; - if (this.browser && this.apiSecret) { - throw new errors.FeedError('You are publicly sharing your App Secret. Do not expose the App Secret in browsers, "native" mobile apps, or other non-trusted environments.'); - } - }, - getBaseUrl: function getBaseUrl(serviceName) { - if (!serviceName) { - serviceName = 'api'; - } +/***/ }), +/* 39 */ +/***/ (function(module, exports, __webpack_require__) { - var url = this.baseUrl; +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { - if (serviceName != 'api') { - url = 'https://' + serviceName + '.stream-io-api.com/' + serviceName + '/'; - } +var Class = __webpack_require__(1), + URI = __webpack_require__(3), + cookies = __webpack_require__(19), + extend = __webpack_require__(0), + Logging = __webpack_require__(8), + Publisher = __webpack_require__(11), + Transport = __webpack_require__(38), + Scheduler = __webpack_require__(16); - if (this.location) { - var protocol = this.options.protocol || 'https'; - url = protocol + '://' + this.location + '-' + serviceName + '.stream-io-api.com/' + serviceName + '/'; - } +var Dispatcher = Class({ className: 'Dispatcher', + MAX_REQUEST_SIZE: 2048, + DEFAULT_RETRY: 5, - if (typeof process !== 'undefined' && process.env.LOCAL) { - url = 'http://localhost:8000/' + serviceName + '/'; - } + UP: 1, + DOWN: 2, - var urlEnvironmentKey; + initialize: function(client, endpoint, options) { + this._client = client; + this.endpoint = URI.parse(endpoint); + this._alternates = options.endpoints || {}; - if (serviceName == 'api') { - urlEnvironmentKey = 'STREAM_BASE_URL'; - } else { - urlEnvironmentKey = 'STREAM_' + serviceName.toUpperCase() + '_URL'; - } + 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 = []; - if (typeof process !== 'undefined' && process.env[urlEnvironmentKey]) { - url = process.env[urlEnvironmentKey]; + 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]); } - return url; - }, - on: function on(event, callback) { - /** - * Support for global event callbacks - * This is useful for generic error and loading handling - * @method on - * @memberof StreamClient.prototype - * @param {string} event - Name of the event - * @param {function} callback - Function that is called when the event fires - * @example - * client.on('request', callback); - * client.on('response', callback); - */ - this.handlers[event] = callback; - }, - off: function off(key) { - /** - * Remove one or more event handlers - * @method off - * @memberof StreamClient.prototype - * @param {string} [key] - Name of the handler - * @example - * client.off() removes all handlers - * client.off(name) removes the specified handler - */ - if (key === undefined) { - this.handlers = {}; - } else { - delete this.handlers[key]; - } - }, - send: function send() { - /** - * Call the given handler with the arguments - * @method send - * @memberof StreamClient.prototype - * @access private - */ - var args = Array.prototype.slice.call(arguments); - var key = args[0]; - args = args.slice(1); + this.tls = options.tls || {}; + this.tls.ca = this.tls.ca || options.ca; - if (this.handlers[key]) { - this.handlers[key].apply(this, args); - } + for (var type in this._alternates) + this._alternates[type] = URI.parse(this._alternates[type]); + + this.maxRequestSize = this.MAX_REQUEST_SIZE; }, - wrapPromiseTask: function wrapPromiseTask(cb, fulfill, reject) { - /** - * Wrap a task to be used as a promise - * @method wrapPromiseTask - * @memberof StreamClient.prototype - * @private - * @param {requestCallback} cb - * @param {function} fulfill - * @param {function} reject - * @return {function} - */ - var client = this; - var callback = this.wrapCallback(cb); - return function task(error, response, body) { - if (error) { - reject(new errors.StreamApiError(error, body, response)); - } else if (!/^2/.test('' + response.statusCode)) { - reject(new errors.StreamApiError(JSON.stringify(body) + ' with HTTP status code ' + response.statusCode, body, response)); - } else { - fulfill(body); - } - callback.call(client, error, response, body); - }; + endpointFor: function(connectionType) { + return this._alternates[connectionType] || this.endpoint; }, - wrapCallback: function wrapCallback(cb) { - /** - * Wrap callback for HTTP request - * @method wrapCallBack - * @memberof StreamClient.prototype - * @access private - */ - var client = this; - function callback() { - // first hit the global callback, subsequently forward - var args = Array.prototype.slice.call(arguments); - var sendArgs = ['response'].concat(args); - client.send.apply(client, sendArgs); + addWebsocketExtension: function(extension) { + this.wsExtensions.push(extension); + }, - if (cb !== undefined) { - cb.apply(client, args); - } - } + disable: function(feature) { + this._disabled.push(feature); + }, - return callback; + setHeader: function(name, value) { + this.headers[name] = value; }, - userAgent: function userAgent() { - /** - * Get the current user agent - * @method userAgent - * @memberof StreamClient.prototype - * @return {string} current user agent - */ - var description = this.node ? 'node' : 'browser'; // TODO: get the version here in a way which works in both and browserify - var version = 'unknown'; - return 'stream-javascript-client-' + description + '-' + version; + close: function() { + var transport = this._transport; + delete this._transport; + if (transport) transport.close(); }, - getReadOnlyToken: function getReadOnlyToken(feedSlug, userId) { - /** - * Returns a token that allows only read operations - * - * @method getReadOnlyToken - * @memberof StreamClient.prototype - * @param {string} feedSlug - The feed slug to get a read only token for - * @param {string} userId - The user identifier - * @return {string} token - * @example - * client.getReadOnlyToken('user', '1'); - */ - return this.feed(feedSlug, userId).getReadOnlyToken(); + + getConnectionTypes: function() { + return Transport.getConnectionTypes(); }, - getReadWriteToken: function getReadWriteToken(feedSlug, userId) { - /** - * Returns a token that allows read and write operations - * - * @method getReadWriteToken - * @memberof StreamClient.prototype - * @param {string} feedSlug - The feed slug to get a read only token for - * @param {string} userId - The user identifier - * @return {string} token - * @example - * client.getReadWriteToken('user', '1'); - */ - return this.feed(feedSlug, userId).getReadWriteToken(); + + selectTransport: function(transportTypes) { + Transport.get(this, transportTypes, this._disabled, function(transport) { + this.debug('Selected ? transport for ?', transport.connectionType, URI.stringify(transport.endpoint)); + + if (transport === this._transport) return; + if (this._transport) this._transport.close(); + + this._transport = transport; + this.connectionType = transport.connectionType; + }, this); }, - feed: function feed(feedSlug, userId, token, siteId, options) { - /** - * Returns a feed object for the given feed id and token - * @method feed - * @memberof StreamClient.prototype - * @param {string} feedSlug - The feed slug - * @param {string} userId - The user identifier - * @param {string} [token] - The token - * @param {string} [siteId] - The site identifier - * @param {object} [options] - Additional function options - * @param {boolean} [options.readOnly] - A boolean indicating whether to generate a read only token for this feed - * @return {StreamFeed} - * @example - * client.feed('user', '1', 'token2'); - */ + + sendMessage: function(message, timeout, options) { options = options || {}; - if (!feedSlug || !userId) { - throw new errors.FeedError('Please provide a feed slug and user id, ie client.feed("user", "1")'); - } + var id = message.id, + attempts = options.attempts, + deadline = options.deadline && new Date().getTime() + (options.deadline * 1000), + envelope = this._envelopes[id], + scheduler; - if (feedSlug.indexOf(':') !== -1) { - throw new errors.FeedError('Please initialize the feed using client.feed("user", "1") not client.feed("user:1")'); + if (!envelope) { + scheduler = new this._scheduler(message, {timeout: timeout, interval: this.retry, attempts: attempts, deadline: deadline}); + envelope = this._envelopes[id] = {message: message, scheduler: scheduler}; } - utils.validateFeedSlug(feedSlug); - utils.validateUserId(userId); // raise an error if there is no token - - if (!this.apiSecret && !token) { - throw new errors.FeedError('Missing token, in client side mode please provide a feed secret'); - } // create the token in server side mode + this._sendEnvelope(envelope); + }, + _sendEnvelope: function(envelope) { + if (!this._transport) return; + if (envelope.request || envelope.timer) return; - if (this.apiSecret && !token) { - var feedId = '' + feedSlug + userId; // use scoped token if read-only access is necessary + var message = envelope.message, + scheduler = envelope.scheduler, + self = this; - token = options.readOnly ? this.getReadOnlyToken(feedSlug, userId) : signing.sign(this.apiSecret, feedId); + if (!scheduler.isDeliverable()) { + scheduler.abort(); + delete this._envelopes[message.id]; + return; } - var feed = new StreamFeed(this, feedSlug, userId, token, siteId); - return feed; - }, - enrichUrl: function enrichUrl(relativeUrl, serviceName) { - /** - * Combines the base url with version and the relative url - * @method enrichUrl - * @memberof StreamClient.prototype - * @private - * @param {string} relativeUrl - */ - if (!serviceName) { - serviceName = 'api'; - } + envelope.timer = global.setTimeout(function() { + self.handleError(message); + }, scheduler.getTimeout() * 1000); - var base_url = this.getBaseUrl(serviceName); - var url = base_url + this.version + '/' + relativeUrl; - return url; + scheduler.send(); + envelope.request = this._transport.sendMessage(message); }, - enrichKwargs: function enrichKwargs(kwargs) { - /** - * Adds the API key and the signature - * @method enrichKwargs - * @memberof StreamClient.prototype - * @param {object} kwargs - * @private - */ - kwargs.url = this.enrichUrl(kwargs.url, kwargs.serviceName); - if (kwargs.qs === undefined) { - kwargs.qs = {}; - } + handleResponse: function(reply) { + var envelope = this._envelopes[reply.id]; - if (!this.browser) { - kwargs.agent = this.requestAgent; + if (reply.successful !== undefined && envelope) { + envelope.scheduler.succeed(); + delete this._envelopes[reply.id]; + global.clearTimeout(envelope.timer); } - kwargs.qs['api_key'] = this.apiKey; - kwargs.qs.location = this.group; - kwargs.json = true; - var signature = kwargs.signature || this.signature; - kwargs.headers = {}; // auto-detect authentication type and set HTTP headers accordingly + this.trigger('message', reply); - if (signing.isJWTSignature(signature)) { - kwargs.headers['stream-auth-type'] = 'jwt'; - signature = signature.split(' ').reverse()[0]; - } else { - kwargs.headers['stream-auth-type'] = 'simple'; - } - - kwargs.headers.Authorization = signature; - kwargs.headers['X-Stream-Client'] = this.userAgent(); // Make sure withCredentials is not enabled, different browser - // fallbacks handle it differently by default (meteor) - - kwargs.withCredentials = false; - return kwargs; - }, - signActivity: function signActivity(activity) { - /** - * We automatically sign the to parameter when in server side mode - * @method signActivities - * @memberof StreamClient.prototype - * @private - * @param {object} [activity] Activity to sign - */ - return this.signActivities([activity])[0]; + if (this._state === this.UP) return; + this._state = this.UP; + this._client.trigger('transport:up'); }, - signActivities: function signActivities(activities) { - /** - * We automatically sign the to parameter when in server side mode - * @method signActivities - * @memberof StreamClient.prototype - * @private - * @param {array} Activities - */ - if (!this.apiSecret) { - return activities; - } - for (var i = 0; i < activities.length; i++) { - var activity = activities[i]; - var to = activity.to || []; - var signedTo = []; + handleError: function(message, immediate) { + var envelope = this._envelopes[message.id], + request = envelope && envelope.request, + self = this; - for (var j = 0; j < to.length; j++) { - var feedId = to[j]; - var feedSlug = feedId.split(':')[0]; - var userId = feedId.split(':')[1]; - var token = this.feed(feedSlug, userId).token; - var signedFeed = feedId + ' ' + token; - signedTo.push(signedFeed); - } + if (!request) return; - activity.to = signedTo; - } + request.then(function(req) { + if (req && req.abort) req.abort(); + }); - return activities; - }, - getFayeAuthorization: function getFayeAuthorization() { - /** - * Get the authorization middleware to use Faye with getstream.io - * @method getFayeAuthorization - * @memberof StreamClient.prototype - * @private - * @return {object} Faye authorization middleware - */ - var apiKey = this.apiKey, - self = this; - return { - incoming: function incoming(message, callback) { - callback(message); - }, - outgoing: function outgoing(message, callback) { - if (message.subscription && self.subscriptions[message.subscription]) { - var subscription = self.subscriptions[message.subscription]; - message.ext = { - 'user_id': subscription.userId, - 'api_key': apiKey, - 'signature': subscription.token - }; - } + var scheduler = envelope.scheduler; + scheduler.fail(); - callback(message); - } - }; - }, - getFayeClient: function getFayeClient() { - /** - * Returns this client's current Faye client - * @method getFayeClient - * @memberof StreamClient.prototype - * @private - * @return {object} Faye client - */ - if (this.fayeClient === null) { - this.fayeClient = new Faye.Client(this.fayeUrl); - var authExtension = this.getFayeAuthorization(); - this.fayeClient.addExtension(authExtension); - } + global.clearTimeout(envelope.timer); + envelope.request = envelope.timer = null; - return this.fayeClient; - }, - get: function get(kwargs, cb) { - /** - * Shorthand function for get request - * @method get - * @memberof StreamClient.prototype - * @private - * @param {object} kwargs - * @param {requestCallback} cb Callback to call on completion - * @return {Promise} Promise object - */ - return new Promise(function (fulfill, reject) { - this.send('request', 'get', kwargs, cb); - kwargs = this.enrichKwargs(kwargs); - kwargs.method = 'GET'; - kwargs.gzip = true; - var callback = this.wrapPromiseTask(cb, fulfill, reject); - this.request(kwargs, callback); - }.bind(this)); - }, - post: function post(kwargs, cb) { - /** - * Shorthand function for post request - * @method post - * @memberof StreamClient.prototype - * @private - * @param {object} kwargs - * @param {requestCallback} cb Callback to call on completion - * @return {Promise} Promise object - */ - return new Promise(function (fulfill, reject) { - this.send('request', 'post', kwargs, cb); - kwargs = this.enrichKwargs(kwargs); - kwargs.method = 'POST'; - kwargs.gzip = true; - var callback = this.wrapPromiseTask(cb, fulfill, reject); - this.request(kwargs, callback); - }.bind(this)); - }, - 'delete': function _delete(kwargs, cb) { - /** - * Shorthand function for delete request - * @method delete - * @memberof StreamClient.prototype - * @private - * @param {object} kwargs - * @param {requestCallback} cb Callback to call on completion - * @return {Promise} Promise object - */ - return new Promise(function (fulfill, reject) { - this.send('request', 'delete', kwargs, cb); - kwargs = this.enrichKwargs(kwargs); - kwargs.gzip = true; - kwargs.method = 'DELETE'; - var callback = this.wrapPromiseTask(cb, fulfill, reject); - this.request(kwargs, callback); - }.bind(this)); - }, - updateActivities: function updateActivities(activities, callback) { - /** - * Updates all supplied activities on the getstream-io api - * @since 3.1.0 - * @param {array} activities list of activities to update - * @return {Promise} - */ - if (!(activities instanceof Array)) { - throw new TypeError('The activities argument should be an Array'); + if (immediate) { + this._sendEnvelope(envelope); + } else { + envelope.timer = global.setTimeout(function() { + envelope.timer = null; + self._sendEnvelope(envelope); + }, scheduler.getInterval() * 1000); } - var authToken = signing.JWTScopeToken(this.apiSecret, 'activities', '*', { - feedId: '*', - expireTokens: this.expireTokens - }); - var data = { - activities: activities - }; - return this.post({ - url: 'activities/', - body: data, - signature: authToken - }, callback); - }, - updateActivity: function updateActivity(activity, callback) { - /** - * Updates one activity on the getstream-io api - * @since 3.1.0 - * @param {object} activity The activity to update - * @return {Promise} - */ - return this.updateActivities([activity], callback); - }, - getActivities: function getActivities(params, callback) { - /** - * Retrieve activities by ID or foreign ID and time - * @since 3.19.0 - * @param {object} params object containing either the list of activity IDs as {ids: ['...', ...]} or foreign IDs and time as {foreignIDTimes: [{foreignID: ..., time: ...}, ...]} - * @return {Promise} - */ - var qs = {}; + if (this._state === this.DOWN) return; + this._state = this.DOWN; + this._client.trigger('transport:down'); + } +}); - if (params.ids) { - var ids = params.ids; +Dispatcher.create = function(client, endpoint, options) { + return new Dispatcher(client, endpoint, options); +}; - if (!(ids instanceof Array)) { - throw new TypeError('The ids argument should be an Array'); - } +extend(Dispatcher.prototype, Publisher); +extend(Dispatcher.prototype, Logging); - qs['ids'] = ids.join(','); - } else if (params.foreignIDTimes) { - var list = params.foreignIDTimes; +module.exports = Dispatcher; - if (!(list instanceof Array)) { - throw new TypeError('The foreignIDTimes argument should be an Array'); - } +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) - var foreignIDs = []; - var timestamps = []; +/***/ }), +/* 40 */ +/***/ (function(module, exports) { - for (var i in list) { - if (!(list[i] instanceof Object)) { - throw new TypeError('foreignIDTimes elements should be Objects'); - } +/* +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: - foreignIDs.push(list[i].foreignID); - timestamps.push(list[i].time); - } +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - qs['foreign_ids'] = foreignIDs.join(','); - qs['timestamps'] = timestamps.join(','); - } else { - throw new TypeError('Missing ids or foreignIDTimes params'); +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +var isArray = typeof Array.isArray === 'function' + ? Array.isArray + : function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]' + } +; +function indexOf (xs, x) { + if (xs.indexOf) return xs.indexOf(x); + for (var i = 0; i < xs.length; i++) { + if (x === xs[i]) return i; } + return -1; +} - var authToken = signing.JWTScopeToken(this.apiSecret, 'activities', '*', { - feedId: '*', - expireTokens: this.expireTokens - }); - return this.get({ - url: 'activities/', - qs: qs, - signature: authToken - }, callback); +function EventEmitter() {} +module.exports = EventEmitter; + +EventEmitter.prototype.emit = function(type) { + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events || !this._events.error || + (isArray(this._events.error) && !this._events.error.length)) + { + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } } -}; -if (qs) { - StreamClient.prototype.createRedirectUrl = function (targetUrl, userId, events) { - /** - * Creates a redirect url for tracking the given events in the context of - * an email using Stream's analytics platform. Learn more at - * getstream.io/personalization - * @method createRedirectUrl - * @memberof StreamClient.prototype - * @param {string} targetUrl Target url - * @param {string} userId User id to track - * @param {array} events List of events to track - * @return {string} The redirect url - */ - var uri = url.parse(targetUrl); + if (!this._events) return false; + var handler = this._events[type]; + if (!handler) return false; - if (!(uri.host || uri.hostname && uri.port) && !uri.isUnix) { - throw new errors.MissingSchemaError('Invalid URI: "' + url.format(uri) + '"'); + if (typeof handler == 'function') { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + var args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); } + return true; - var authToken = signing.JWTScopeToken(this.apiSecret, 'redirect_and_track', '*', { - userId: "*", - expireTokens: this.expireTokens - }); - var analyticsUrl = this.baseAnalyticsUrl + 'redirect/'; - var kwargs = { - 'auth_type': 'jwt', - 'authorization': authToken, - 'url': targetUrl, - 'api_key': this.apiKey, - 'events': JSON.stringify(events) - }; - var qString = utils.rfc3986(qs.stringify(kwargs, null, null, {})); - return analyticsUrl + '?' + qString; - }; -} // If we are in a node environment and batchOperations is available add the methods to the prototype of StreamClient - + } else if (isArray(handler)) { + var args = Array.prototype.slice.call(arguments, 1); -if (BatchOperations) { - for (var key in BatchOperations) { - if (BatchOperations.hasOwnProperty(key)) { - StreamClient.prototype[key] = BatchOperations[key]; + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); } + return true; + + } else { + return false; } -} +}; -module.exports = StreamClient; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(10))) +// EventEmitter is defined in src/node_events.cc +// EventEmitter.prototype.emit() is also defined there. +EventEmitter.prototype.addListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('addListener only takes instances of Function'); + } -/***/ }), -/* 28 */ -/***/ (function(module, exports, __webpack_require__) { + if (!this._events) this._events = {}; -"use strict"; + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit('newListener', type, listener); + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } else if (isArray(this._events[type])) { + // If we've already got an array, just append. + this._events[type].push(listener); + } else { + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + } -var Collections = function Collections() { - /** - * Manage api calls for collections - * The collection object contains convenience functions such as upsert, select and delete. - * @class Collections - */ - this.initialize.apply(this, arguments); + return this; }; -Collections.prototype = { - initialize: function initialize(client) { - this.client = client; - }, - upsert: function upsert(collectionName, data, callback) { - /** - * Upsert one or more items within a collection. - * - * @method upsert - * @memberof Collections.prototype - * @param {object} collectionName - The name of the collection - * @param {object or array} data - A single json object or an array of objects - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object. - */ - var last = arguments[arguments.length - 1]; // callback is always the last argument +EventEmitter.prototype.on = EventEmitter.prototype.addListener; - callback = last.call ? last : undefined; +EventEmitter.prototype.once = function(type, listener) { + var self = this; + self.on(type, function g() { + self.removeListener(type, g); + listener.apply(this, arguments); + }); - if (!Array.isArray(data)) { - data = [data]; - } + return this; +}; - var data_json = { - data: {} - }; - data_json['data'][collectionName] = data; - return this.client.post({ - url: 'meta/', - serviceName: 'api', - body: data_json, - signature: this.client.collectionsToken - }, callback); - }, - select: function select(collectionName, ids, callback) { - /** - * Select all objects with ids from the collection. - * - * @method select - * @memberof Collections.prototype - * @param {object} collectionName - The name of the collection - * @param {object or array} ids - A single json object or an array of objects - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object. - */ - var last = arguments[arguments.length - 1]; // callback is always the last argument +EventEmitter.prototype.removeListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('removeListener only takes instances of Function'); + } - callback = last.call ? last : undefined; + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events || !this._events[type]) return this; - if (!Array.isArray(ids)) { - ids = [ids]; - } + var list = this._events[type]; - var params = { - foreign_ids: ids.map(function (id) { - return collectionName + ":" + id; - }).join(',') - }; - return this.client.get({ - url: 'meta/', - serviceName: 'api', - qs: params, - signature: this.client.collectionsToken - }, callback); - }, - delete: function _delete(collectionName, ids, callback) { - /** - * Remove all objects by id from the collection. - * - * @method delete - * @memberof Collections.prototype - * @param {object} collectionName - The name of the collection - * @param {object or array} ids - A single json object or an array of objects - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object. - */ - var last = arguments[arguments.length - 1]; // callback is always the last argument + if (isArray(list)) { + var i = indexOf(list, listener); + if (i < 0) return this; + list.splice(i, 1); + if (list.length == 0) + delete this._events[type]; + } else if (this._events[type] === listener) { + delete this._events[type]; + } - callback = last.call ? last : undefined; + return this; +}; - if (!Array.isArray(ids)) { - ids = [ids]; - } +EventEmitter.prototype.removeAllListeners = function(type) { + if (arguments.length === 0) { + this._events = {}; + return this; + } - ids = ids.map(function (id) { - return id.toString(); - }).join(','); - var params = { - collection_name: collectionName, - ids: ids - }; - return this.client.delete({ - url: 'meta/', - serviceName: 'api', - qs: params, - signature: this.client.collectionsToken - }, callback); + // does not use listeners(), so no side effect of creating _events[type] + if (type && this._events && this._events[type]) this._events[type] = null; + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + if (!this._events) this._events = {}; + if (!this._events[type]) this._events[type] = []; + if (!isArray(this._events[type])) { + this._events[type] = [this._events[type]]; } + return this._events[type]; }; -module.exports = Collections; + /***/ }), -/* 29 */ +/* 41 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Personalization = function Personalization() { - /** - * Manage api calls for personalization - * The collection object contains convenience functions such as get, post, delete - * @class Personalization - */ - this.initialize.apply(this, arguments); +var array = __webpack_require__(13); + +module.exports = function(options, validKeys) { + for (var key in options) { + if (array.indexOf(validKeys, key) < 0) + throw new Error('Unrecognized option: ' + key); + } }; -Personalization.prototype = { - /** - * Initialize the Personalization object - * - * @method intialize - * @memberof Personalization.prototype - * @param {StreamClient} client - The stream client - */ - initialize: function initialize(client) { - this.client = client; - }, - get: function get(resource, options, callback) { - /** - * Get personalized activities for this feed - * - * @method get - * @memberof Personalization.prototype - * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" - * @param {object} options Additional options - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object. Personalized feed - * @example client.personalization.get('follow_recommendations', {foo: 'bar', baz: 'qux'}, cb) - */ - var last = arguments[arguments.length - 1]; // callback is always the last argument - callback = last.call ? last : undefined; +/***/ }), +/* 42 */ +/***/ (function(module, exports, __webpack_require__) { - if (!options || options.call) { - options = {}; - } +"use strict"; +/* WEBPACK VAR INJECTION */(function(global) { - return this.client.get({ - url: resource + '/', - serviceName: 'personalization', - qs: options, - signature: this.client.personalizationToken - }, callback); - }, - post: function post(resource, options, data, callback) { - /** - * Post data to personalization endpoint - * - * @method post - * @memberof Personalization.prototype - * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" - * @param {object} options - Additional options - * @param {object} data - Data to send in the payload - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object. Data that was posted if successful, or an error. - * @example client.personalization.post('follow_recommendations', {foo: 'bar', baz: 'qux'}, cb) - */ - var last = arguments[arguments.length - 1]; // callback is always the last argument +var asap = __webpack_require__(23), + Class = __webpack_require__(1), + Promise = __webpack_require__(9), + URI = __webpack_require__(3), + array = __webpack_require__(13), + browser = __webpack_require__(12), + constants = __webpack_require__(22), + extend = __webpack_require__(0), + validateOptions = __webpack_require__(41), + Deferrable = __webpack_require__(6), + Logging = __webpack_require__(8), + Publisher = __webpack_require__(11), + Channel = __webpack_require__(21), + Dispatcher = __webpack_require__(39), + Error = __webpack_require__(31), + Extensible = __webpack_require__(30), + Publication = __webpack_require__(29), + Subscription = __webpack_require__(28); - callback = last.call ? last : undefined; +var Client = Class({ className: 'Client', + UNCONNECTED: 1, + CONNECTING: 2, + CONNECTED: 3, + DISCONNECTED: 4, - if (!options || options.call) { - options = {}; - } + HANDSHAKE: 'handshake', + RETRY: 'retry', + NONE: 'none', - if (!data || data.call) { - data = {}; - } + CONNECTION_TIMEOUT: 60, - return this.client.post({ - url: resource + '/', - serviceName: 'personalization', - qs: options, - body: data, - signature: this.client.personalizationToken - }, callback); + 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); }, - delete: function _delete(resource, options, callback) { - /** - * Delete metadata or activites - * - * @method delete - * @memberof Personalization.prototype - * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" - * @param {object} options - Additional options - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object. Data that was deleted if successful, or an error. - * @example client.personalization.delete('follow_recommendations', {foo: 'bar', baz: 'qux'}, cb) - */ - var last = arguments[arguments.length - 1]; // callback is always the last argument - callback = last.call ? last : undefined; + addWebsocketExtension: function(extension) { + return this._dispatcher.addWebsocketExtension(extension); + }, - if (!options || options.call) { - options = {}; - } + disable: function(feature) { + return this._dispatcher.disable(feature); + }, - return this.client.delete({ - url: resource + '/', - serviceName: 'personalization', - qs: options, - signature: this.client.personalizationToken - }, callback); - } -}; -module.exports = Personalization; + setHeader: function(name, value) { + return this._dispatcher.setHeader(name, value); + }, -/***/ }), -/* 30 */ -/***/ (function(module, exports, __webpack_require__) { + // 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; -"use strict"; + this._state = this.CONNECTING; + var self = this; + this.info('Initiating handshake with ?', URI.stringify(this._dispatcher.endpoint)); + this._dispatcher.selectTransport(constants.MANDATORY_CONNECTION_TYPES); -function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + this._sendMessage({ + channel: Channel.HANDSHAKE, + version: constants.BAYEUX_VERSION, + supportedConnectionTypes: this._dispatcher.getConnectionTypes() -var errors = __webpack_require__(9); + }, {}, function(response) { -var utils = __webpack_require__(17); + if (response.successful) { + this._state = this.CONNECTED; + this._dispatcher.clientId = response.clientId; -var signing = __webpack_require__(11); + this._dispatcher.selectTransport(response.supportedConnectionTypes); -var StreamFeed = function StreamFeed() { - /** - * Manage api calls for specific feeds - * The feed object contains convenience functions such add activity, remove activity etc - * @class StreamFeed - */ - this.initialize.apply(this, arguments); -}; + this.info('Handshake successful: ?', this._dispatcher.clientId); -StreamFeed.prototype = { - initialize: function initialize(client, feedSlug, userId, token) { - /** - * Initialize a feed object - * @method intialize - * @memberof StreamFeed.prototype - * @param {StreamClient} client - The stream client this feed is constructed from - * @param {string} feedSlug - The feed slug - * @param {string} userId - The user id - * @param {string} [token] - The authentication token - */ - this.client = client; - this.slug = feedSlug; - this.userId = userId; - this.id = this.slug + ':' + this.userId; - this.token = token; - this.feedUrl = this.id.replace(':', '/'); - this.feedTogether = this.id.replace(':', ''); - this.signature = this.feedTogether + ' ' + this.token; // faye setup - - this.notificationChannel = 'site-' + this.client.appId + '-feed-' + this.feedTogether; - }, - addActivity: function addActivity(activity, callback) { - /** - * Adds the given activity to the feed and - * calls the specified callback - * @method addActivity - * @memberof StreamFeed.prototype - * @param {object} activity - The activity to add - * @param {requestCallback} callback - Callback to call on completion - * @return {Promise} Promise object - */ - activity = this.client.signActivity(activity); - return this.client.post({ - url: 'feed/' + this.feedUrl + '/', - body: activity, - signature: this.signature - }, callback); - }, - removeActivity: function removeActivity(activityId, callback) { - /** - * Removes the activity by activityId - * @method removeActivity - * @memberof StreamFeed.prototype - * @param {string} activityId Identifier of activity to remove - * @param {requestCallback} callback Callback to call on completion - * @return {Promise} Promise object - * @example - * feed.removeActivity(activityId); - * @example - * feed.removeActivity({'foreignId': foreignId}); - */ - var identifier = activityId.foreignId ? activityId.foreignId : activityId; - var params = {}; - - if (activityId.foreignId) { - params['foreign_id'] = '1'; - } - - return this.client['delete']({ - url: 'feed/' + this.feedUrl + '/' + identifier + '/', - qs: params, - signature: this.signature - }, callback); - }, - addActivities: function addActivities(activities, callback) { - /** - * Adds the given activities to the feed and calls the specified callback - * @method addActivities - * @memberof StreamFeed.prototype - * @param {Array} activities Array of activities to add - * @param {requestCallback} callback Callback to call on completion - * @return {Promise} XHR request object - */ - activities = this.client.signActivities(activities); - var data = { - activities: activities - }; - var xhr = this.client.post({ - url: 'feed/' + this.feedUrl + '/', - body: data, - signature: this.signature - }, callback); - return xhr; - }, - follow: function follow(targetSlug, targetUserId, options, callback) { - /** - * Follows the given target feed - * @method follow - * @memberof StreamFeed.prototype - * @param {string} targetSlug Slug of the target feed - * @param {string} targetUserId User identifier of the target feed - * @param {object} options Additional options - * @param {number} options.activityCopyLimit Limit the amount of activities copied over on follow - * @param {requestCallback} callback Callback to call on completion - * @return {Promise} Promise object - * @example feed.follow('user', '1'); - * @example feed.follow('user', '1', callback); - * @example feed.follow('user', '1', options, callback); - */ - utils.validateFeedSlug(targetSlug); - utils.validateUserId(targetUserId); - var activityCopyLimit; - var last = arguments[arguments.length - 1]; // callback is always the last argument - - callback = last.call ? last : undefined; - var target = targetSlug + ':' + targetUserId; // check for additional options - - if (options && !options.call) { - if (typeof options.limit !== "undefined" && options.limit !== null) { - activityCopyLimit = options.limit; - } - } - - var body = { - target: target - }; - - if (typeof activityCopyLimit !== "undefined" && activityCopyLimit !== null) { - body['activity_copy_limit'] = activityCopyLimit; - } - - return this.client.post({ - url: 'feed/' + this.feedUrl + '/following/', - body: body, - signature: this.signature - }, callback); - }, - unfollow: function unfollow(targetSlug, targetUserId, optionsOrCallback, callback) { - /** - * Unfollow the given feed - * @method unfollow - * @memberof StreamFeed.prototype - * @param {string} targetSlug Slug of the target feed - * @param {string} targetUserId [description] - * @param {requestCallback|object} optionsOrCallback - * @param {boolean} optionOrCallback.keepHistory when provided the activities from target - * feed will not be kept in the feed - * @param {requestCallback} callback Callback to call on completion - * @return {object} XHR request object - * @example feed.unfollow('user', '2', callback); - */ - var options = {}, - qs = {}; - if (typeof optionsOrCallback === 'function') callback = optionsOrCallback; - if (_typeof(optionsOrCallback) === 'object') options = optionsOrCallback; - if (typeof options.keepHistory === 'boolean' && options.keepHistory) qs['keep_history'] = '1'; - utils.validateFeedSlug(targetSlug); - utils.validateUserId(targetUserId); - var targetFeedId = targetSlug + ':' + targetUserId; - var xhr = this.client['delete']({ - url: 'feed/' + this.feedUrl + '/following/' + targetFeedId + '/', - qs: qs, - signature: this.signature - }, callback); - return xhr; - }, - following: function following(options, callback) { - /** - * List which feeds this feed is following - * @method following - * @memberof StreamFeed.prototype - * @param {object} options Additional options - * @param {string} options.filter Filter to apply on search operation - * @param {requestCallback} callback Callback to call on completion - * @return {Promise} Promise object - * @example feed.following({limit:10, filter: ['user:1', 'user:2']}, callback); - */ - if (options !== undefined && options.filter) { - options.filter = options.filter.join(','); - } - - return this.client.get({ - url: 'feed/' + this.feedUrl + '/following/', - qs: options, - signature: this.signature - }, callback); - }, - followers: function followers(options, callback) { - /** - * List the followers of this feed - * @method followers - * @memberof StreamFeed.prototype - * @param {object} options Additional options - * @param {string} options.filter Filter to apply on search operation - * @param {requestCallback} callback Callback to call on completion - * @return {Promise} Promise object - * @example - * feed.followers({limit:10, filter: ['user:1', 'user:2']}, callback); - */ - if (options !== undefined && options.filter) { - options.filter = options.filter.join(','); - } - - return this.client.get({ - url: 'feed/' + this.feedUrl + '/followers/', - qs: options, - signature: this.signature - }, callback); - }, - get: function get(options, callback) { - /** - * Reads the feed - * @method get - * @memberof StreamFeed.prototype - * @param {object} options Additional options - * @param {requestCallback} callback Callback to call on completion - * @return {Promise} Promise object - * @example feed.get({limit: 10, id_lte: 'activity-id'}) - * @example feed.get({limit: 10, mark_seen: true}) - */ - if (options && options['mark_read'] && options['mark_read'].join) { - options['mark_read'] = options['mark_read'].join(','); - } - - if (options && options['mark_seen'] && options['mark_seen'].join) { - options['mark_seen'] = options['mark_seen'].join(','); - } - - return this.client.get({ - url: 'feed/' + this.feedUrl + '/', - qs: options, - signature: this.signature - }, callback); - }, - getFayeClient: function getFayeClient() { - /** - * Returns the current faye client object - * @method getFayeClient - * @memberof StreamFeed.prototype - * @access private - * @return {object} Faye client - */ - return this.client.getFayeClient(); - }, - subscribe: function subscribe(callback) { - /** - * Subscribes to any changes in the feed, return a promise - * @method subscribe - * @memberof StreamFeed.prototype - * @param {function} callback Callback to call on completion - * @return {Promise} Promise object - * @example - * feed.subscribe(callback).then(function(){ - * console.log('we are now listening to changes'); - * }); - */ - if (!this.client.appId) { - throw new errors.SiteError('Missing app id, which is needed to subscribe, use var client = stream.connect(key, secret, appId);'); - } - - var subscription = this.getFayeClient().subscribe('/' + this.notificationChannel, callback); - this.client.subscriptions['/' + this.notificationChannel] = { - token: this.token, - userId: this.notificationChannel, - fayeSubscription: subscription - }; - return subscription; - }, - unsubscribe: function unsubscribe() { - /** - * Cancel updates created via feed.subscribe() - * @return void - */ - var streamSubscription = this.client.subscriptions['/' + this.notificationChannel]; - - if (streamSubscription) { - delete this.client.subscriptions['/' + this.notificationChannel]; - streamSubscription.fayeSubscription.cancel(); - } - }, - getReadOnlyToken: function getReadOnlyToken() { - /** - * Returns a token that allows only read operations - * - * @method getReadOnlyToken - * @memberof StreamClient.prototype - * @param {string} feedSlug - The feed slug to get a read only token for - * @param {string} userId - The user identifier - * @return {string} token - * @example - * client.getReadOnlyToken('user', '1'); - */ - var feedId = '' + this.slug + this.userId; - return signing.JWTScopeToken(this.client.apiSecret, '*', 'read', { - feedId: feedId, - expireTokens: this.client.expireTokens - }); - }, - getReadWriteToken: function getReadWriteToken() { - /** - * Returns a token that allows read and write operations - * - * @method getReadWriteToken - * @memberof StreamClient.prototype - * @param {string} feedSlug - The feed slug to get a read only token for - * @param {string} userId - The user identifier - * @return {string} token - * @example - * client.getReadWriteToken('user', '1'); - */ - var feedId = '' + this.slug + this.userId; - return signing.JWTScopeToken(this.client.apiSecret, '*', '*', { - feedId: feedId, - expireTokens: this.client.expireTokens - }); - }, - updateActivityToTargets: function updateActivityToTargets(foreign_id, time, new_targets, added_targets, removed_targets) { - /** - * Updates an activity's "to" fields - * @since 3.10.0 - * @param {string} foreign_id The foreign_id of the activity to update - * @param {string} time The time of the activity to update - * @param {array} new_targets Set the new "to" targets for the activity - will remove old targets - * @param {array} added_targets Add these new targets to the activity - * @param {array} removed_targets Remove these targets from the activity - */ - if (!foreign_id) { - throw new Error('Missing `foreign_id` parameter!'); - } else if (!time) { - throw new Error('Missing `time` parameter!'); - } - - if (!new_targets && !added_targets && !removed_targets) { - throw new Error('Requires you to provide at least one parameter for `new_targets`, `added_targets`, or `removed_targets` - example: `updateActivityToTargets("foreignID:1234", new Date(), [new_targets...], [added_targets...], [removed_targets...])`'); - } - - if (new_targets) { - if (added_targets || removed_targets) { - throw new Error("Can't include add_targets or removed_targets if you're also including new_targets"); - } - } - - if (added_targets && removed_targets) { - // brute force - iterate through added, check to see if removed contains that element - for (var i = 0; i < added_targets.length; i++) { - // would normally use Array.prototype.includes here, but it's not supported in Node.js v4 :( - for (var j = 0; j < removed_targets.length; j++) { - if (removed_targets[j] == added_targets[i]) { - throw new Error("Can't have the same feed ID in added_targets and removed_targets."); - } - } - } - } - - var body = { - foreign_id: foreign_id, - time: time - }; - - if (new_targets) { - body['new_targets'] = new_targets; - } - - if (added_targets) { - body['added_targets'] = added_targets; - } - - if (removed_targets) { - body['removed_targets'] = removed_targets; - } - - return this.client.post({ - url: 'feed_targets/' + this.feedUrl + '/activity_to_targets/', - signature: this.signature, - body: body - }); - } -}; -module.exports = StreamFeed; - -/***/ }), -/* 31 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 32 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { - -;(function () { - - var object = - true ? exports : - undefined; // #31: ExtendScript - - var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - - function InvalidCharacterError(message) { - this.message = message; - } - InvalidCharacterError.prototype = new Error; - InvalidCharacterError.prototype.name = 'InvalidCharacterError'; - - // encoder - // [https://gist.github.com/999166] by [https://github.com/nignag] - object.btoa || ( - object.btoa = function (input) { - var str = String(input); - for ( - // initialize result and counter - var block, charCode, idx = 0, map = chars, output = ''; - // if the next str index does not exist: - // change the mapping table to "=" - // check if d has no fractional digits - str.charAt(idx | 0) || (map = '=', idx % 1); - // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 - output += map.charAt(63 & block >> 8 - idx % 1 * 8) - ) { - charCode = str.charCodeAt(idx += 3/4); - if (charCode > 0xFF) { - throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."); - } - block = block << 8 | charCode; - } - return output; - }); - - // decoder - // [https://gist.github.com/1020396] by [https://github.com/atk] - object.atob || ( - object.atob = function (input) { - var str = String(input).replace(/[=]+$/, ''); // #31: ExtendScript bad parse of /= - if (str.length % 4 == 1) { - throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded."); - } - for ( - // initialize result and counters - var bc = 0, bs, buffer, idx = 0, output = ''; - // get next character - buffer = str.charAt(idx++); - // character found in table? initialize bit storage and add its ascii value; - ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, - // and if not first of each 4 characters, - // convert the first 8 bits to one ascii character - bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 - ) { - // try to find character in table (0-63, not found => -1) - buffer = chars.indexOf(buffer); - } - return output; - }); - -}()); - - -/***/ }), -/* 34 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 35 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var Promise = __webpack_require__(5); - -module.exports = Promise; - -/***/ }), -/* 36 */ -/***/ (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__(2))) - -/***/ }), -/* 37 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 38 */ -/***/ (function(module, exports) { - -/* (ignored) */ - -/***/ }), -/* 39 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var constants = __webpack_require__(19), - Logging = __webpack_require__(6); - -var Faye = { - VERSION: constants.VERSION, - - Client: __webpack_require__(40), - Scheduler: __webpack_require__(25) -}; - -Logging.wrapper = Faye; - -module.exports = Faye; - - -/***/ }), -/* 40 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { - -var asap = __webpack_require__(18), - Class = __webpack_require__(1), - Promise = __webpack_require__(5), - URI = __webpack_require__(3), - array = __webpack_require__(12), - browser = __webpack_require__(13), - constants = __webpack_require__(19), - extend = __webpack_require__(0), - validateOptions = __webpack_require__(41), - Deferrable = __webpack_require__(8), - Logging = __webpack_require__(6), - Publisher = __webpack_require__(14), - Channel = __webpack_require__(20), - Dispatcher = __webpack_require__(43), - Error = __webpack_require__(51), - Extensible = __webpack_require__(52), - Publication = __webpack_require__(53), - Subscription = __webpack_require__(54); - -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 ?', URI.stringify(this._dispatcher.endpoint)); - 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) }); + this.subscribe(this._channels.getKeys(), true); + if (callback) asap(function() { callback.call(context) }); } else { this.info('Handshake unsuccessful'); @@ -4611,1091 +3956,1818 @@ module.exports = Client; /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) /***/ }), -/* 41 */ +/* 43 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var array = __webpack_require__(12); +var constants = __webpack_require__(22), + Logging = __webpack_require__(8); -module.exports = function(options, validKeys) { - for (var key in options) { - if (array.indexOf(validKeys, key) < 0) - throw new Error('Unrecognized option: ' + key); - } +var Faye = { + VERSION: constants.VERSION, + + Client: __webpack_require__(42), + Scheduler: __webpack_require__(16) }; +Logging.wrapper = Faye; + +module.exports = Faye; + /***/ }), -/* 42 */ +/* 44 */ /***/ (function(module, exports) { -/* -Copyright Joyent, Inc. and other Node contributors. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: +/* (ignored) */ -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +/***/ }), +/* 45 */ +/***/ (function(module, exports) { -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ +/* (ignored) */ -var isArray = typeof Array.isArray === 'function' - ? Array.isArray - : function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]' - } -; -function indexOf (xs, x) { - if (xs.indexOf) return xs.indexOf(x); - for (var i = 0; i < xs.length; i++) { - if (x === xs[i]) return i; +/***/ }), +/* 46 */ +/***/ (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; } - return -1; + // Equivalent to push, but avoids a function call. + queue[queue.length] = task; } -function EventEmitter() {} -module.exports = EventEmitter; +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; -EventEmitter.prototype.emit = function(type) { - // If there is no 'error' event listener then throw. - if (type === 'error') { - if (!this._events || !this._events.error || - (isArray(this._events.error) && !this._events.error.length)) - { - if (arguments[1] instanceof Error) { - throw arguments[1]; // Unhandled 'error' event - } else { - throw new Error("Uncaught, unspecified 'error' event."); - } - return false; +// 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; +} - if (!this._events) return false; - var handler = this._events[type]; - if (!handler) return 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 - if (typeof handler == 'function') { - switch (arguments.length) { - // fast cases - case 1: - handler.call(this); - break; - case 2: - handler.call(this, arguments[1]); - break; - case 3: - handler.call(this, arguments[1], arguments[2]); - break; - // slower - default: - var args = Array.prototype.slice.call(arguments, 1); - handler.apply(this, args); - } - return true; +// 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. - } else if (isArray(handler)) { - var args = Array.prototype.slice.call(arguments, 1); +/* globals self */ +var scope = typeof global !== "undefined" ? global : self; +var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; - var listeners = handler.slice(); - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].apply(this, args); - } - return true; +// 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); - } else { - return false; - } -}; +// 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 -// EventEmitter is defined in src/node_events.cc -// EventEmitter.prototype.emit() is also defined there. -EventEmitter.prototype.addListener = function(type, listener) { - if ('function' !== typeof listener) { - throw new Error('addListener only takes instances of Function'); - } +// 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. - if (!this._events) this._events = {}; +// function makeRequestCallFromMessageChannel(callback) { +// var channel = new MessageChannel(); +// channel.port1.onmessage = callback; +// return function requestCall() { +// channel.port2.postMessage(0); +// }; +// } - // To avoid recursion in the case that type == "newListeners"! Before - // adding it to the listeners, first emit "newListeners". - this.emit('newListener', type, listener); +// 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. - if (!this._events[type]) { - // Optimize the case of one listener. Don't need the extra array object. - this._events[type] = listener; - } else if (isArray(this._events[type])) { - // If we've already got an array, just append. - this._events[type].push(listener); - } else { - // Adding the second element, need to change to array. - this._events[type] = [this._events[type], listener]; - } +// function makeRequestCallFromSetImmediate(callback) { +// return function requestCall() { +// setImmediate(callback); +// }; +// } - return this; -}; +// 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. -EventEmitter.prototype.on = EventEmitter.prototype.addListener; +// `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. -EventEmitter.prototype.once = function(type, listener) { - var self = this; - self.on(type, function g() { - self.removeListener(type, g); - listener.apply(this, arguments); - }); +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); - return this; -}; + function handleTimer() { + // Whichever timer succeeds will cancel both timers and + // execute the callback. + clearTimeout(timeoutHandle); + clearInterval(intervalHandle); + callback(); + } + }; +} -EventEmitter.prototype.removeListener = function(type, listener) { - if ('function' !== typeof listener) { - throw new Error('removeListener only takes instances of Function'); - } +// This is for `asap.js` only. +// Its name will be periodically randomized to break any code that depends on +// its existence. +rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer; - // does not use listeners(), so no side effect of creating _events[type] - if (!this._events || !this._events[type]) return this; +// 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 - var list = this._events[type]; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) - if (isArray(list)) { - var i = indexOf(list, listener); - if (i < 0) return this; - list.splice(i, 1); - if (list.length == 0) - delete this._events[type]; - } else if (this._events[type] === listener) { - delete this._events[type]; - } +/***/ }), +/* 47 */ +/***/ (function(module, exports, __webpack_require__) { - return this; -}; +"use strict"; -EventEmitter.prototype.removeAllListeners = function(type) { - if (arguments.length === 0) { - this._events = {}; - return this; - } - // does not use listeners(), so no side effect of creating _events[type] - if (type && this._events && this._events[type]) this._events[type] = null; - return this; -}; +var Promise = __webpack_require__(9); -EventEmitter.prototype.listeners = function(type) { - if (!this._events) this._events = {}; - if (!this._events[type]) this._events[type] = []; - if (!isArray(this._events[type])) { - this._events[type] = [this._events[type]]; - } - return this._events[type]; -}; +module.exports = Promise; + +/***/ }), +/* 48 */ +/***/ (function(module, exports) { +/* (ignored) */ /***/ }), -/* 43 */ +/* 49 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { +;(function () { -var Class = __webpack_require__(1), - URI = __webpack_require__(3), - cookies = __webpack_require__(22), - extend = __webpack_require__(0), - Logging = __webpack_require__(6), - Publisher = __webpack_require__(14), - Transport = __webpack_require__(44), - Scheduler = __webpack_require__(25); + var object = + true ? exports : + undefined; // #31: ExtendScript -var Dispatcher = Class({ className: 'Dispatcher', - MAX_REQUEST_SIZE: 2048, - DEFAULT_RETRY: 5, + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - UP: 1, - DOWN: 2, + function InvalidCharacterError(message) { + this.message = message; + } + InvalidCharacterError.prototype = new Error; + InvalidCharacterError.prototype.name = 'InvalidCharacterError'; - initialize: function(client, endpoint, options) { - this._client = client; - this.endpoint = URI.parse(endpoint); - this._alternates = options.endpoints || {}; + // encoder + // [https://gist.github.com/999166] by [https://github.com/nignag] + object.btoa || ( + object.btoa = function (input) { + var str = String(input); + for ( + // initialize result and counter + var block, charCode, idx = 0, map = chars, output = ''; + // if the next str index does not exist: + // change the mapping table to "=" + // check if d has no fractional digits + str.charAt(idx | 0) || (map = '=', idx % 1); + // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 + output += map.charAt(63 & block >> 8 - idx % 1 * 8) + ) { + charCode = str.charCodeAt(idx += 3/4); + if (charCode > 0xFF) { + throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."); + } + block = block << 8 | charCode; + } + return output; + }); - 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 = []; + // decoder + // [https://gist.github.com/1020396] by [https://github.com/atk] + object.atob || ( + object.atob = function (input) { + var str = String(input).replace(/[=]+$/, ''); // #31: ExtendScript bad parse of /= + if (str.length % 4 == 1) { + throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded."); + } + for ( + // initialize result and counters + var bc = 0, bs, buffer, idx = 0, output = ''; + // get next character + buffer = str.charAt(idx++); + // character found in table? initialize bit storage and add its ascii value; + ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, + // and if not first of each 4 characters, + // convert the first 8 bits to one ascii character + bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 + ) { + // try to find character in table (0-63, not found => -1) + buffer = chars.indexOf(buffer); + } + return output; + }); - 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; +/***/ }), +/* 50 */ +/***/ (function(module, exports) { - for (var type in this._alternates) - this._alternates[type] = URI.parse(this._alternates[type]); +/* (ignored) */ - this.maxRequestSize = this.MAX_REQUEST_SIZE; - }, +/***/ }), +/* 51 */ +/***/ (function(module, exports) { - endpointFor: function(connectionType) { - return this._alternates[connectionType] || this.endpoint; - }, +/* (ignored) */ - addWebsocketExtension: function(extension) { - this.wsExtensions.push(extension); - }, +/***/ }), +/* 52 */ +/***/ (function(module, exports, __webpack_require__) { - disable: function(feature) { - this._disabled.push(feature); - }, +"use strict"; - setHeader: function(name, value) { - this.headers[name] = value; - }, - close: function() { - var transport = this._transport; - delete this._transport; - if (transport) transport.close(); - }, +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } - getConnectionTypes: function() { - return Transport.getConnectionTypes(); - }, +var errors = __webpack_require__(5); - selectTransport: function(transportTypes) { - Transport.get(this, transportTypes, this._disabled, function(transport) { - this.debug('Selected ? transport for ?', transport.connectionType, URI.stringify(transport.endpoint)); +var utils = __webpack_require__(24); - if (transport === this._transport) return; - if (this._transport) this._transport.close(); +var signing = __webpack_require__(14); - this._transport = transport; - this.connectionType = transport.connectionType; - }, this); - }, +var StreamFeed = function StreamFeed() { + /** + * Manage api calls for specific feeds + * The feed object contains convenience functions such add activity, remove activity etc + * @class StreamFeed + */ + this.initialize.apply(this, arguments); +}; - sendMessage: function(message, timeout, options) { - options = options || {}; +StreamFeed.prototype = { + initialize: function initialize(client, feedSlug, userId, token) { + /** + * Initialize a feed object + * @method intialize + * @memberof StreamFeed.prototype + * @param {StreamClient} client - The stream client this feed is constructed from + * @param {string} feedSlug - The feed slug + * @param {string} userId - The user id + * @param {string} [token] - The authentication token + */ + this.client = client; + this.slug = feedSlug; + this.userId = userId; + this.id = this.slug + ':' + this.userId; + this.token = token; + this.feedUrl = this.id.replace(':', '/'); + this.feedTogether = this.id.replace(':', ''); + this.signature = this.feedTogether + ' ' + this.token; // faye setup - var id = message.id, - attempts = options.attempts, - deadline = options.deadline && new Date().getTime() + (options.deadline * 1000), - envelope = this._envelopes[id], - scheduler; + this.notificationChannel = 'site-' + this.client.appId + '-feed-' + this.feedTogether; + }, + addActivity: function addActivity(activity, callback) { + /** + * Adds the given activity to the feed and + * calls the specified callback + * @method addActivity + * @memberof StreamFeed.prototype + * @param {object} activity - The activity to add + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object + */ + activity = this.client.signActivity(activity); + return this.client.post({ + url: 'feed/' + this.feedUrl + '/', + body: activity, + signature: this.signature + }, callback); + }, + removeActivity: function removeActivity(activityId, callback) { + /** + * Removes the activity by activityId + * @method removeActivity + * @memberof StreamFeed.prototype + * @param {string} activityId Identifier of activity to remove + * @param {requestCallback} callback Callback to call on completion + * @return {Promise} Promise object + * @example + * feed.removeActivity(activityId); + * @example + * feed.removeActivity({'foreignId': foreignId}); + */ + var identifier = activityId.foreignId ? activityId.foreignId : activityId; + var params = {}; - if (!envelope) { - scheduler = new this._scheduler(message, {timeout: timeout, interval: this.retry, attempts: attempts, deadline: deadline}); - envelope = this._envelopes[id] = {message: message, scheduler: scheduler}; + if (activityId.foreignId) { + params['foreign_id'] = '1'; } - this._sendEnvelope(envelope); + return this.client['delete']({ + url: 'feed/' + this.feedUrl + '/' + identifier + '/', + qs: params, + signature: this.signature + }, callback); }, + addActivities: function addActivities(activities, callback) { + /** + * Adds the given activities to the feed and calls the specified callback + * @method addActivities + * @memberof StreamFeed.prototype + * @param {Array} activities Array of activities to add + * @param {requestCallback} callback Callback to call on completion + * @return {Promise} XHR request object + */ + activities = this.client.signActivities(activities); + var data = { + activities: activities + }; + var xhr = this.client.post({ + url: 'feed/' + this.feedUrl + '/', + body: data, + signature: this.signature + }, callback); + return xhr; + }, + follow: function follow(targetSlug, targetUserId, options, callback) { + /** + * Follows the given target feed + * @method follow + * @memberof StreamFeed.prototype + * @param {string} targetSlug Slug of the target feed + * @param {string} targetUserId User identifier of the target feed + * @param {object} options Additional options + * @param {number} options.activityCopyLimit Limit the amount of activities copied over on follow + * @param {requestCallback} callback Callback to call on completion + * @return {Promise} Promise object + * @example feed.follow('user', '1'); + * @example feed.follow('user', '1', callback); + * @example feed.follow('user', '1', options, callback); + */ + utils.validateFeedSlug(targetSlug); + utils.validateUserId(targetUserId); + var activityCopyLimit; + var last = arguments[arguments.length - 1]; // callback is always the last argument - _sendEnvelope: function(envelope) { - if (!this._transport) return; - if (envelope.request || envelope.timer) return; - - var message = envelope.message, - scheduler = envelope.scheduler, - self = this; + callback = last.call ? last : undefined; + var target = targetSlug + ':' + targetUserId; // check for additional options - if (!scheduler.isDeliverable()) { - scheduler.abort(); - delete this._envelopes[message.id]; - return; + if (options && !options.call) { + if (typeof options.limit !== "undefined" && options.limit !== null) { + activityCopyLimit = options.limit; + } } - envelope.timer = global.setTimeout(function() { - self.handleError(message); - }, scheduler.getTimeout() * 1000); + var body = { + target: target + }; - scheduler.send(); - envelope.request = this._transport.sendMessage(message); + if (typeof activityCopyLimit !== "undefined" && activityCopyLimit !== null) { + body['activity_copy_limit'] = activityCopyLimit; + } + + return this.client.post({ + url: 'feed/' + this.feedUrl + '/following/', + body: body, + signature: this.signature + }, callback); + }, + unfollow: function unfollow(targetSlug, targetUserId, optionsOrCallback, callback) { + /** + * Unfollow the given feed + * @method unfollow + * @memberof StreamFeed.prototype + * @param {string} targetSlug Slug of the target feed + * @param {string} targetUserId [description] + * @param {requestCallback|object} optionsOrCallback + * @param {boolean} optionOrCallback.keepHistory when provided the activities from target + * feed will not be kept in the feed + * @param {requestCallback} callback Callback to call on completion + * @return {object} XHR request object + * @example feed.unfollow('user', '2', callback); + */ + var options = {}, + qs = {}; + if (typeof optionsOrCallback === 'function') callback = optionsOrCallback; + if (_typeof(optionsOrCallback) === 'object') options = optionsOrCallback; + if (typeof options.keepHistory === 'boolean' && options.keepHistory) qs['keep_history'] = '1'; + utils.validateFeedSlug(targetSlug); + utils.validateUserId(targetUserId); + var targetFeedId = targetSlug + ':' + targetUserId; + var xhr = this.client['delete']({ + url: 'feed/' + this.feedUrl + '/following/' + targetFeedId + '/', + qs: qs, + signature: this.signature + }, callback); + return xhr; }, + following: function following(options, callback) { + /** + * List which feeds this feed is following + * @method following + * @memberof StreamFeed.prototype + * @param {object} options Additional options + * @param {string} options.filter Filter to apply on search operation + * @param {requestCallback} callback Callback to call on completion + * @return {Promise} Promise object + * @example feed.following({limit:10, filter: ['user:1', 'user:2']}, callback); + */ + if (options !== undefined && options.filter) { + options.filter = options.filter.join(','); + } - handleResponse: function(reply) { - var envelope = this._envelopes[reply.id]; + return this.client.get({ + url: 'feed/' + this.feedUrl + '/following/', + qs: options, + signature: this.signature + }, callback); + }, + followers: function followers(options, callback) { + /** + * List the followers of this feed + * @method followers + * @memberof StreamFeed.prototype + * @param {object} options Additional options + * @param {string} options.filter Filter to apply on search operation + * @param {requestCallback} callback Callback to call on completion + * @return {Promise} Promise object + * @example + * feed.followers({limit:10, filter: ['user:1', 'user:2']}, callback); + */ + if (options !== undefined && options.filter) { + options.filter = options.filter.join(','); + } - if (reply.successful !== undefined && envelope) { - envelope.scheduler.succeed(); - delete this._envelopes[reply.id]; - global.clearTimeout(envelope.timer); + return this.client.get({ + url: 'feed/' + this.feedUrl + '/followers/', + qs: options, + signature: this.signature + }, callback); + }, + get: function get(options, callback) { + /** + * Reads the feed + * @method get + * @memberof StreamFeed.prototype + * @param {object} options Additional options + * @param {requestCallback} callback Callback to call on completion + * @return {Promise} Promise object + * @example feed.get({limit: 10, id_lte: 'activity-id'}) + * @example feed.get({limit: 10, mark_seen: true}) + */ + if (options && options['mark_read'] && options['mark_read'].join) { + options['mark_read'] = options['mark_read'].join(','); } - this.trigger('message', reply); + if (options && options['mark_seen'] && options['mark_seen'].join) { + options['mark_seen'] = options['mark_seen'].join(','); + } - if (this._state === this.UP) return; - this._state = this.UP; - this._client.trigger('transport:up'); + return this.client.get({ + url: 'feed/' + this.feedUrl + '/', + qs: options, + signature: this.signature + }, callback); }, - - 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); + getFayeClient: function getFayeClient() { + /** + * Returns the current faye client object + * @method getFayeClient + * @memberof StreamFeed.prototype + * @access private + * @return {object} Faye client + */ + return this.client.getFayeClient(); + }, + subscribe: function subscribe(callback) { + /** + * Subscribes to any changes in the feed, return a promise + * @method subscribe + * @memberof StreamFeed.prototype + * @param {function} callback Callback to call on completion + * @return {Promise} Promise object + * @example + * feed.subscribe(callback).then(function(){ + * console.log('we are now listening to changes'); + * }); + */ + if (!this.client.appId) { + throw new errors.SiteError('Missing app id, which is needed to subscribe, use var client = stream.connect(key, secret, appId);'); } - 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); -}; - -extend(Dispatcher.prototype, Publisher); -extend(Dispatcher.prototype, Logging); - -module.exports = Dispatcher; - -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) - -/***/ }), -/* 44 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var Transport = __webpack_require__(4); - -Transport.register('websocket', __webpack_require__(46)); -Transport.register('eventsource', __webpack_require__(48)); -Transport.register('long-polling', __webpack_require__(24)); -Transport.register('cross-origin-long-polling', __webpack_require__(49)); -Transport.register('callback-polling', __webpack_require__(50)); - -module.exports = Transport; - - -/***/ }), -/* 45 */ -/***/ (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); + var subscription = this.getFayeClient().subscribe('/' + this.notificationChannel, callback); + this.client.subscriptions['/' + this.notificationChannel] = { + token: this.token, + userId: this.notificationChannel, + fayeSubscription: subscription + }; + return subscription; }, + unsubscribe: function unsubscribe() { + /** + * Cancel updates created via feed.subscribe() + * @return void + */ + var streamSubscription = this.client.subscriptions['/' + this.notificationChannel]; - removeTimeout: function(name) { - this._timeouts = this._timeouts || {}; - var timeout = this._timeouts[name]; - if (!timeout) return; - global.clearTimeout(timeout); - delete this._timeouts[name]; + if (streamSubscription) { + delete this.client.subscriptions['/' + this.notificationChannel]; + streamSubscription.fayeSubscription.cancel(); + } }, - - removeAllTimeouts: function() { - this._timeouts = this._timeouts || {}; - for (var name in this._timeouts) this.removeTimeout(name); - } -}; - -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) - -/***/ }), -/* 46 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { - -var Class = __webpack_require__(1), - Promise = __webpack_require__(5), - Set = __webpack_require__(23), - URI = __webpack_require__(3), - browser = __webpack_require__(13), - copyObject = __webpack_require__(15), - extend = __webpack_require__(0), - toJSON = __webpack_require__(7), - ws = __webpack_require__(47), - Deferrable = __webpack_require__(8), - Transport = __webpack_require__(4); - -var WebSocket = extend(Class(Transport, { - UNCONNECTED: 1, - CONNECTING: 2, - CONNECTED: 3, - - batching: false, - - isUsable: function(callback, context) { - this.callback(function() { callback.call(context, true) }); - this.errback(function() { callback.call(context, false) }); - this.connect(); + getReadOnlyToken: function getReadOnlyToken() { + /** + * Returns a token that allows only read operations + * + * @method getReadOnlyToken + * @memberof StreamClient.prototype + * @param {string} feedSlug - The feed slug to get a read only token for + * @param {string} userId - The user identifier + * @return {string} token + * @example + * client.getReadOnlyToken('user', '1'); + */ + var feedId = '' + this.slug + this.userId; + return signing.JWTScopeToken(this.client.apiSecret, '*', 'read', { + feedId: feedId, + expireTokens: this.client.expireTokens + }); }, + getReadWriteToken: function getReadWriteToken() { + /** + * Returns a token that allows read and write operations + * + * @method getReadWriteToken + * @memberof StreamClient.prototype + * @param {string} feedSlug - The feed slug to get a read only token for + * @param {string} userId - The user identifier + * @return {string} token + * @example + * client.getReadWriteToken('user', '1'); + */ + var feedId = '' + this.slug + this.userId; + return signing.JWTScopeToken(this.client.apiSecret, '*', '*', { + feedId: feedId, + expireTokens: this.client.expireTokens + }); + }, + updateActivityToTargets: function updateActivityToTargets(foreign_id, time, new_targets, added_targets, removed_targets) { + /** + * Updates an activity's "to" fields + * @since 3.10.0 + * @param {string} foreign_id The foreign_id of the activity to update + * @param {string} time The time of the activity to update + * @param {array} new_targets Set the new "to" targets for the activity - will remove old targets + * @param {array} added_targets Add these new targets to the activity + * @param {array} removed_targets Remove these targets from the activity + */ + if (!foreign_id) { + throw new Error('Missing `foreign_id` parameter!'); + } else if (!time) { + throw new Error('Missing `time` parameter!'); + } - request: function(messages) { - this._pending = this._pending || new Set(); - for (var i = 0, n = messages.length; i < n; i++) this._pending.add(messages[i]); - - var self = this; + if (!new_targets && !added_targets && !removed_targets) { + throw new Error('Requires you to provide at least one parameter for `new_targets`, `added_targets`, or `removed_targets` - example: `updateActivityToTargets("foreignID:1234", new Date(), [new_targets...], [added_targets...], [removed_targets...])`'); + } - var promise = new Promise(function(resolve, reject) { - self.callback(function(socket) { - if (!socket || socket.readyState !== 1) return; - socket.send(toJSON(messages)); - resolve(socket); - }); + if (new_targets) { + if (added_targets || removed_targets) { + throw new Error("Can't include add_targets or removed_targets if you're also including new_targets"); + } + } - self.connect(); - }); + if (added_targets && removed_targets) { + // brute force - iterate through added, check to see if removed contains that element + for (var i = 0; i < added_targets.length; i++) { + // would normally use Array.prototype.includes here, but it's not supported in Node.js v4 :( + for (var j = 0; j < removed_targets.length; j++) { + if (removed_targets[j] == added_targets[i]) { + throw new Error("Can't have the same feed ID in added_targets and removed_targets."); + } + } + } + } - return { - abort: function() { promise.then(function(ws) { ws.close() }) } + var body = { + foreign_id: foreign_id, + time: time }; - }, - - connect: function() { - if (WebSocket._unloaded) return; - this._state = this._state || this.UNCONNECTED; - if (this._state !== this.UNCONNECTED) return; - this._state = this.CONNECTING; + if (new_targets) { + body['new_targets'] = new_targets; + } - var socket = this._createSocket(); - if (!socket) return this.setDeferredStatus('failed'); + if (added_targets) { + body['added_targets'] = added_targets; + } - var self = this; + if (removed_targets) { + body['removed_targets'] = removed_targets; + } - socket.onopen = function() { - if (socket.headers) self._storeCookies(socket.headers['set-cookie']); - self._socket = socket; - self._state = self.CONNECTED; - self._everConnected = true; - self._ping(); - self.setDeferredStatus('succeeded', socket); - }; + return this.client.post({ + url: 'feed_targets/' + this.feedUrl + '/activity_to_targets/', + signature: this.signature, + body: body + }); + } +}; +module.exports = StreamFeed; - var closed = false; - socket.onclose = socket.onerror = function() { - if (closed) return; - closed = true; +/***/ }), +/* 53 */ +/***/ (function(module, exports, __webpack_require__) { - var wasConnected = (self._state === self.CONNECTED); - socket.onopen = socket.onclose = socket.onerror = socket.onmessage = null; +"use strict"; - delete self._socket; - self._state = self.UNCONNECTED; - self.removeTimeout('ping'); - var pending = self._pending ? self._pending.toArray() : []; - delete self._pending; +var errors = __webpack_require__(5); - if (wasConnected || self._everConnected) { - self.setDeferredStatus('unknown'); - self._handleError(pending, wasConnected); - } else { - self.setDeferredStatus('failed'); - } - }; +var Personalization = function Personalization() { + /** + * Manage api calls for personalization + * The collection object contains convenience functions such as get, post, delete + * @class Personalization + */ + this.initialize.apply(this, arguments); +}; - socket.onmessage = function(event) { - var replies; - try { replies = JSON.parse(event.data) } catch (error) {} +Personalization.prototype = { + /** + * Initialize the Personalization object + * + * @method intialize + * @memberof Personalization.prototype + * @param {StreamClient} client - The stream client + */ + initialize: function initialize(client) { + this.client = client; + }, + get: function get(resource, options, callback) { + /** + * Get personalized activities for this feed + * + * @method get + * @memberof Personalization.prototype + * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" + * @param {object} options Additional options + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object. Personalized feed + * @example client.personalization.get('follow_recommendations', {foo: 'bar', baz: 'qux'}, cb) + */ + var last = arguments[arguments.length - 1]; // callback is always the last argument - if (!replies) return; + callback = last.call ? last : undefined; - replies = [].concat(replies); + if (!options || options.call) { + options = {}; + } - for (var i = 0, n = replies.length; i < n; i++) { - if (replies[i].successful === undefined) continue; - self._pending.remove(replies[i]); - } - self._receive(replies); - }; + return this.client.get({ + url: resource + '/', + serviceName: 'personalization', + qs: options, + signature: this.client.getPersonalizationToken() + }, callback); }, + post: function post(resource, options, data, callback) { + /** + * Post data to personalization endpoint + * + * @method post + * @memberof Personalization.prototype + * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" + * @param {object} options - Additional options + * @param {object} data - Data to send in the payload + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object. Data that was posted if successful, or an error. + * @example client.personalization.post('follow_recommendations', {foo: 'bar', baz: 'qux'}, cb) + */ + var last = arguments[arguments.length - 1]; // callback is always the last argument - close: function() { - if (!this._socket) return; - this._socket.close(); - }, + callback = last.call ? last : undefined; - _createSocket: function() { - var url = WebSocket.getSocketUrl(this.endpoint), - headers = this._dispatcher.headers, - extensions = this._dispatcher.wsExtensions, - cookie = this._getCookies(), - tls = this._dispatcher.tls, - options = {extensions: extensions, headers: headers, proxy: this._proxy, tls: tls}; + if (!options || options.call) { + options = {}; + } - if (cookie !== '') options.headers['Cookie'] = cookie; + if (!data || data.call) { + data = {}; + } - return ws.create(url, [], options); + return this.client.post({ + url: resource + '/', + serviceName: 'personalization', + qs: options, + body: data, + signature: this.client.getPersonalizationToken() + }, callback); }, + delete: function _delete(resource, options, callback) { + /** + * Delete metadata or activites + * + * @method delete + * @memberof Personalization.prototype + * @param {object} resource - personalized resource endpoint i.e "follow_recommendations" + * @param {object} options - Additional options + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object. Data that was deleted if successful, or an error. + * @example client.personalization.delete('follow_recommendations', {foo: 'bar', baz: 'qux'}, cb) + */ + var last = arguments[arguments.length - 1]; // callback is always the last argument - _ping: function() { - if (!this._socket || this._socket.readyState !== 1) return; - this._socket.send('[]'); - this.addTimeout('ping', this._dispatcher.timeout / 2, this._ping, this); + callback = last.call ? last : undefined; + + if (!options || options.call) { + options = {}; + } + + return this.client.delete({ + url: resource + '/', + serviceName: 'personalization', + qs: options, + signature: this.client.getPersonalizationToken() + }, callback); } +}; +module.exports = Personalization; -}), { - PROTOCOLS: { - 'http:': 'ws:', - 'https:': 'wss:' - }, +/***/ }), +/* 54 */ +/***/ (function(module, exports, __webpack_require__) { - create: function(dispatcher, endpoint) { - var sockets = dispatcher.transports.websocket = dispatcher.transports.websocket || {}; - sockets[endpoint.href] = sockets[endpoint.href] || new this(dispatcher, endpoint); - return sockets[endpoint.href]; - }, +"use strict"; - getSocketUrl: function(endpoint) { - endpoint = copyObject(endpoint); - endpoint.protocol = this.PROTOCOLS[endpoint.protocol]; - return URI.stringify(endpoint); + +var errors = __webpack_require__(5); + +var Collections = function Collections() { + /** + * Manage api calls for collections + * The collection object contains convenience functions such as upsert, select and delete. + * @class Collections + */ + this.initialize.apply(this, arguments); +}; + +Collections.prototype = { + initialize: function initialize(client) { + this.client = client; }, + upsert: function upsert(collectionName, data, callback) { + /** + * Upsert one or more items within a collection. + * + * @method upsert + * @memberof Collections.prototype + * @param {object} collectionName - The name of the collection + * @param {object or array} data - A single json object or an array of objects + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object. + */ + var last = arguments[arguments.length - 1]; // callback is always the last argument - isUsable: function(dispatcher, endpoint, callback, context) { - this.create(dispatcher, endpoint).isUsable(callback, context); - } -}); + callback = last.call ? last : undefined; -extend(WebSocket.prototype, Deferrable); + if (!Array.isArray(data)) { + data = [data]; + } -if (browser.Event && global.onbeforeunload !== undefined) - browser.Event.on(global, 'beforeunload', function() { WebSocket._unloaded = true }); + var data_json = { + data: {} + }; + data_json['data'][collectionName] = data; + return this.client.post({ + url: 'meta/', + serviceName: 'api', + body: data_json, + signature: this.client.getCollectionsToken() + }, callback); + }, + select: function select(collectionName, ids, callback) { + /** + * Select all objects with ids from the collection. + * + * @method select + * @memberof Collections.prototype + * @param {object} collectionName - The name of the collection + * @param {object or array} ids - A single json object or an array of objects + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object. + */ + var last = arguments[arguments.length - 1]; // callback is always the last argument -module.exports = WebSocket; + callback = last.call ? last : undefined; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) + if (!Array.isArray(ids)) { + ids = [ids]; + } -/***/ }), -/* 47 */ -/***/ (function(module, exports, __webpack_require__) { + var params = { + foreign_ids: ids.map(function (id) { + return collectionName + ":" + id; + }).join(',') + }; + return this.client.get({ + url: 'meta/', + serviceName: 'api', + qs: params, + signature: this.client.getCollectionsToken() + }, callback); + }, + delete: function _delete(collectionName, ids, callback) { + /** + * Remove all objects by id from the collection. + * + * @method delete + * @memberof Collections.prototype + * @param {object} collectionName - The name of the collection + * @param {object or array} ids - A single json object or an array of objects + * @param {requestCallback} callback - Callback to call on completion + * @return {Promise} Promise object. + */ + var last = arguments[arguments.length - 1]; // callback is always the last argument -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { + callback = last.call ? last : undefined; -var WS = global.MozWebSocket || global.WebSocket; + if (!Array.isArray(ids)) { + ids = [ids]; + } -module.exports = { - create: function(url, protocols, options) { - if (typeof WS !== 'function') return null; - return new WS(url); + ids = ids.map(function (id) { + return id.toString(); + }).join(','); + var params = { + collection_name: collectionName, + ids: ids + }; + return this.client.delete({ + url: 'meta/', + serviceName: 'api', + qs: params, + signature: this.client.getCollectionsToken() + }, callback); } }; - -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) +module.exports = Collections; /***/ }), -/* 48 */ +/* 55 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(global) { +/* WEBPACK VAR INJECTION */(function(process) { -var Class = __webpack_require__(1), - URI = __webpack_require__(3), - copyObject = __webpack_require__(15), - extend = __webpack_require__(0), - Deferrable = __webpack_require__(8), - Transport = __webpack_require__(4), - XHR = __webpack_require__(24); +var Collections = __webpack_require__(54); -var EventSource = extend(Class(Transport, { - initialize: function(dispatcher, endpoint) { - Transport.prototype.initialize.call(this, dispatcher, endpoint); - if (!global.EventSource) return this.setDeferredStatus('failed'); +var Personalization = __webpack_require__(53); - this._xhr = new XHR(dispatcher, endpoint); +var request = __webpack_require__(25); - endpoint = copyObject(endpoint); - endpoint.pathname += '/' + dispatcher.clientId; +var StreamFeed = __webpack_require__(52); - var socket = new global.EventSource(URI.stringify(endpoint)), - self = this; +var signing = __webpack_require__(14); - socket.onopen = function() { - self._everConnected = true; - self.setDeferredStatus('succeeded'); - }; +var errors = __webpack_require__(5); - socket.onerror = function() { - if (self._everConnected) { - self._handleError([]); - } else { - self.setDeferredStatus('failed'); - socket.close(); - } - }; +var utils = __webpack_require__(24); - socket.onmessage = function(event) { - var replies; - try { replies = JSON.parse(event.data) } catch (error) {} +var BatchOperations = __webpack_require__(48); - if (replies) - self._receive(replies); - else - self._handleError([]); - }; +var Promise = __webpack_require__(47); - this._socket = socket; - }, +var qs = __webpack_require__(45); - close: function() { - if (!this._socket) return; - this._socket.onopen = this._socket.onerror = this._socket.onmessage = null; - this._socket.close(); - delete this._socket; - }, +var url = __webpack_require__(44); - isUsable: function(callback, context) { - this.callback(function() { callback.call(context, true) }); - this.errback(function() { callback.call(context, false) }); - }, +var Faye = __webpack_require__(43); +/** + * @callback requestCallback + * @param {object} [errors] + * @param {object} response + * @param {object} body + */ - encode: function(messages) { - return this._xhr.encode(messages); - }, - request: function(messages) { - return this._xhr.request(messages); - } +var StreamClient = function StreamClient() { + /** + * Client to connect to Stream api + * @class StreamClient + */ + this.initialize.apply(this, arguments); +}; -}), { - isUsable: function(dispatcher, endpoint, callback, context) { - var id = dispatcher.clientId; - if (!id) return callback.call(context, false); +StreamClient.prototype = { + baseUrl: 'https://api.stream-io-api.com/api/', + baseAnalyticsUrl: 'https://analytics.stream-io-api.com/analytics/', + initialize: function initialize(apiKey, apiSecret, appId, options) { + /** + * Initialize a client + * @method intialize + * @memberof StreamClient.prototype + * @param {string} apiKey - the api key + * @param {string} [apiSecret] - the api secret + * @param {string} [appId] - id of the app + * @param {object} [options] - additional options + * @param {string} [options.location] - which data center to use + * @param {boolean} [options.expireTokens=false] - whether to use a JWT timestamp field (i.e. iat) + * @example initialize is not directly called by via stream.connect, ie: + * stream.connect(apiKey, apiSecret) + * @example secret is optional and only used in server side mode + * stream.connect(apiKey, null, appId); + */ + this.apiKey = apiKey; + this.apiSecret = apiSecret; + this.appId = appId; + this.options = options || {}; + this.version = this.options.version || 'v1.0'; + this.fayeUrl = this.options.fayeUrl || 'https://faye.getstream.io/faye'; + this.fayeClient = null; + this.request = request; // track a source name for the api calls, ie get started or databrowser - XHR.isUsable(dispatcher, endpoint, function(usable) { - if (!usable) return callback.call(context, false); - this.create(dispatcher, endpoint).isUsable(callback, context); - }, this); - }, + this.group = this.options.group || 'unspecified'; // track subscriptions made on feeds created by this client - create: function(dispatcher, endpoint) { - var sockets = dispatcher.transports.eventsource = dispatcher.transports.eventsource || {}, - id = dispatcher.clientId; + this.subscriptions = {}; + this.expireTokens = this.options.expireTokens ? this.options.expireTokens : false; // which data center to use - var url = copyObject(endpoint); - url.pathname += '/' + (id || ''); - url = URI.stringify(url); + this.location = this.options.location; + this.baseUrl = this.getBaseUrl(); - sockets[url] = sockets[url] || new this(dispatcher, endpoint); - return sockets[url]; - } -}); + if (typeof process !== 'undefined' && process.env.LOCAL_FAYE) { + this.fayeUrl = 'http://localhost:9999/faye/'; + } -extend(EventSource.prototype, Deferrable); + if (typeof process !== 'undefined' && process.env.STREAM_ANALYTICS_BASE_URL) { + this.baseAnalyticsUrl = process.env.STREAM_ANALYTICS_BASE_URL; + } -module.exports = EventSource; + this.handlers = {}; + this.browser = typeof window !== 'undefined'; + this.node = !this.browser; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) + if (!this.browser) { + var http = __webpack_require__(27); -/***/ }), -/* 49 */ -/***/ (function(module, exports, __webpack_require__) { + var https = __webpack_require__(26); -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { + var httpsAgent = new https.Agent({ + keepAlive: true, + keepAliveMsecs: 3000 + }); + var httpAgent = new http.Agent({ + keepAlive: true, + keepAliveMsecs: 3000 + }); + this.requestAgent = this.baseUrl.startsWith('https://') ? httpsAgent : httpAgent; + } -var Class = __webpack_require__(1), - Set = __webpack_require__(23), - URI = __webpack_require__(3), - extend = __webpack_require__(0), - toJSON = __webpack_require__(7), - Transport = __webpack_require__(4); + this.personalization = new Personalization(this); + this.collections = new Collections(this); + /* istanbul ignore next */ -var CORS = extend(Class(Transport, { - encode: function(messages) { - return 'message=' + encodeURIComponent(toJSON(messages)); + if (this.browser && this.apiSecret) { + throw new errors.FeedError('You are publicly sharing your App Secret. Do not expose the App Secret in browsers, "native" mobile apps, or other non-trusted environments.'); + } }, - - request: function(messages) { - var xhrClass = global.XDomainRequest ? XDomainRequest : XMLHttpRequest, - xhr = new xhrClass(), - id = ++CORS._id, - headers = this._dispatcher.headers, - self = this, - key; - - xhr.open('POST', URI.stringify(this.endpoint), true); - - if (xhr.setRequestHeader) { - xhr.setRequestHeader('Pragma', 'no-cache'); - for (key in headers) { - if (!headers.hasOwnProperty(key)) continue; - xhr.setRequestHeader(key, headers[key]); - } + getPersonalizationToken: function getPersonalizationToken() { + if (this._personalizationToken) { + return this._personalizationToken; } - var cleanUp = function() { - if (!xhr) return false; - CORS._pending.remove(id); - xhr.onload = xhr.onerror = xhr.ontimeout = xhr.onprogress = null; - xhr = null; - }; + if (this.apiSecret) { + this._personalizationToken = signing.JWTScopeToken(this.apiSecret, 'personalization', '*', { + userId: '*', + feedId: '*', + expireTokens: this.expireTokens + }); + } else { + throw new errors.SiteError('Missing secret, which is needed to perform signed requests, use var client = stream.connect(key, secret);'); + } - xhr.onload = function() { - var replies; - try { replies = JSON.parse(xhr.responseText) } catch (error) {} + return this._personalizationToken; + }, + getCollectionsToken: function getCollectionsToken() { + if (this._collectionsToken) { + return this._collectionsToken; + } - cleanUp(); + if (this.apiSecret) { + this._collectionsToken = signing.JWTScopeToken(this.apiSecret, 'collections', '*', { + userId: '*', + feedId: '*', + expireTokens: this.expireTokens + }); + } else { + throw new errors.SiteError('Missing secret, which is needed to perform signed requests, use var client = stream.connect(key, secret);'); + } - if (replies) - self._receive(replies); - else - self._handleError(messages); - }; + return this._collectionsToken; + }, + getBaseUrl: function getBaseUrl(serviceName) { + if (!serviceName) { + serviceName = 'api'; + } - xhr.onerror = xhr.ontimeout = function() { - cleanUp(); - self._handleError(messages); - }; + var url = this.baseUrl; - xhr.onprogress = function() {}; + if (serviceName != 'api') { + url = 'https://' + serviceName + '.stream-io-api.com/' + serviceName + '/'; + } - if (xhrClass === global.XDomainRequest) - CORS._pending.add({id: id, xhr: xhr}); + if (this.location) { + var protocol = this.options.protocol || 'https'; + url = protocol + '://' + this.location + '-' + serviceName + '.stream-io-api.com/' + serviceName + '/'; + } - xhr.send(this.encode(messages)); - return xhr; - } -}), { - _id: 0, - _pending: new Set(), + if (typeof process !== 'undefined' && process.env.LOCAL) { + url = 'http://localhost:8000/' + serviceName + '/'; + } - isUsable: function(dispatcher, endpoint, callback, context) { - if (URI.isSameOrigin(endpoint)) - return callback.call(context, false); + var urlEnvironmentKey; - if (global.XDomainRequest) - return callback.call(context, endpoint.protocol === location.protocol); + if (serviceName == 'api') { + urlEnvironmentKey = 'STREAM_BASE_URL'; + } else { + urlEnvironmentKey = 'STREAM_' + serviceName.toUpperCase() + '_URL'; + } - if (global.XMLHttpRequest) { - var xhr = new XMLHttpRequest(); - return callback.call(context, xhr.withCredentials !== undefined); + if (typeof process !== 'undefined' && process.env[urlEnvironmentKey]) { + url = process.env[urlEnvironmentKey]; } - return callback.call(context, false); - } -}); -module.exports = CORS; + return url; + }, + on: function on(event, callback) { + /** + * Support for global event callbacks + * This is useful for generic error and loading handling + * @method on + * @memberof StreamClient.prototype + * @param {string} event - Name of the event + * @param {function} callback - Function that is called when the event fires + * @example + * client.on('request', callback); + * client.on('response', callback); + */ + this.handlers[event] = callback; + }, + off: function off(key) { + /** + * Remove one or more event handlers + * @method off + * @memberof StreamClient.prototype + * @param {string} [key] - Name of the handler + * @example + * client.off() removes all handlers + * client.off(name) removes the specified handler + */ + if (key === undefined) { + this.handlers = {}; + } else { + delete this.handlers[key]; + } + }, + send: function send() { + /** + * Call the given handler with the arguments + * @method send + * @memberof StreamClient.prototype + * @access private + */ + var args = Array.prototype.slice.call(arguments); + var key = args[0]; + args = args.slice(1); -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) + if (this.handlers[key]) { + this.handlers[key].apply(this, args); + } + }, + wrapPromiseTask: function wrapPromiseTask(cb, fulfill, reject) { + /** + * Wrap a task to be used as a promise + * @method wrapPromiseTask + * @memberof StreamClient.prototype + * @private + * @param {requestCallback} cb + * @param {function} fulfill + * @param {function} reject + * @return {function} + */ + var client = this; + var callback = this.wrapCallback(cb); + return function task(error, response, body) { + if (error) { + reject(new errors.StreamApiError(error, body, response)); + } else if (!/^2/.test('' + response.statusCode)) { + reject(new errors.StreamApiError(JSON.stringify(body) + ' with HTTP status code ' + response.statusCode, body, response)); + } else { + fulfill(body); + } -/***/ }), -/* 50 */ -/***/ (function(module, exports, __webpack_require__) { + callback.call(client, error, response, body); + }; + }, + wrapCallback: function wrapCallback(cb) { + /** + * Wrap callback for HTTP request + * @method wrapCallBack + * @memberof StreamClient.prototype + * @access private + */ + var client = this; -"use strict"; -/* WEBPACK VAR INJECTION */(function(global) { + function callback() { + // first hit the global callback, subsequently forward + var args = Array.prototype.slice.call(arguments); + var sendArgs = ['response'].concat(args); + client.send.apply(client, sendArgs); -var Class = __webpack_require__(1), - URI = __webpack_require__(3), - copyObject = __webpack_require__(15), - extend = __webpack_require__(0), - toJSON = __webpack_require__(7), - Transport = __webpack_require__(4); + if (cb !== undefined) { + cb.apply(client, args); + } + } -var JSONP = extend(Class(Transport, { - encode: function(messages) { - var url = copyObject(this.endpoint); - url.query.message = toJSON(messages); - url.query.jsonp = '__jsonp' + JSONP._cbCount + '__'; - return URI.stringify(url); + return callback; }, + userAgent: function userAgent() { + /** + * Get the current user agent + * @method userAgent + * @memberof StreamClient.prototype + * @return {string} current user agent + */ + var description = this.node ? 'node' : 'browser'; // TODO: get the version here in a way which works in both and browserify - request: function(messages) { - var head = document.getElementsByTagName('head')[0], - script = document.createElement('script'), - callbackName = JSONP.getCallbackName(), - endpoint = copyObject(this.endpoint), - self = this; + var version = 'unknown'; + return 'stream-javascript-client-' + description + '-' + version; + }, + getReadOnlyToken: function getReadOnlyToken(feedSlug, userId) { + /** + * Returns a token that allows only read operations + * + * @method getReadOnlyToken + * @memberof StreamClient.prototype + * @param {string} feedSlug - The feed slug to get a read only token for + * @param {string} userId - The user identifier + * @return {string} token + * @example + * client.getReadOnlyToken('user', '1'); + */ + return this.feed(feedSlug, userId).getReadOnlyToken(); + }, + getReadWriteToken: function getReadWriteToken(feedSlug, userId) { + /** + * Returns a token that allows read and write operations + * + * @method getReadWriteToken + * @memberof StreamClient.prototype + * @param {string} feedSlug - The feed slug to get a read only token for + * @param {string} userId - The user identifier + * @return {string} token + * @example + * client.getReadWriteToken('user', '1'); + */ + return this.feed(feedSlug, userId).getReadWriteToken(); + }, + feed: function feed(feedSlug, userId, token, siteId, options) { + /** + * Returns a feed object for the given feed id and token + * @method feed + * @memberof StreamClient.prototype + * @param {string} feedSlug - The feed slug + * @param {string} userId - The user identifier + * @param {string} [token] - The token + * @param {string} [siteId] - The site identifier + * @param {object} [options] - Additional function options + * @param {boolean} [options.readOnly] - A boolean indicating whether to generate a read only token for this feed + * @return {StreamFeed} + * @example + * client.feed('user', '1', 'token2'); + */ + options = options || {}; - endpoint.query.message = toJSON(messages); - endpoint.query.jsonp = callbackName; + if (!feedSlug || !userId) { + throw new errors.FeedError('Please provide a feed slug and user id, ie client.feed("user", "1")'); + } - var cleanup = function() { - if (!global[callbackName]) return false; - global[callbackName] = undefined; - try { delete global[callbackName] } catch (error) {} - script.parentNode.removeChild(script); - }; + if (feedSlug.indexOf(':') !== -1) { + throw new errors.FeedError('Please initialize the feed using client.feed("user", "1") not client.feed("user:1")'); + } - global[callbackName] = function(replies) { - cleanup(); - self._receive(replies); - }; + utils.validateFeedSlug(feedSlug); + utils.validateUserId(userId); // raise an error if there is no token - script.type = 'text/javascript'; - script.src = URI.stringify(endpoint); - head.appendChild(script); + if (!this.apiSecret && !token) { + throw new errors.FeedError('Missing token, in client side mode please provide a feed secret'); + } // create the token in server side mode - script.onerror = function() { - cleanup(); - self._handleError(messages); - }; - return {abort: cleanup}; - } -}), { - _cbCount: 0, + if (this.apiSecret && !token) { + var feedId = '' + feedSlug + userId; // use scoped token if read-only access is necessary - getCallbackName: function() { - this._cbCount += 1; - return '__jsonp' + this._cbCount + '__'; - }, + token = options.readOnly ? this.getReadOnlyToken(feedSlug, userId) : signing.sign(this.apiSecret, feedId); + } - isUsable: function(dispatcher, endpoint, callback, context) { - callback.call(context, true); - } -}); + var feed = new StreamFeed(this, feedSlug, userId, token, siteId); + return feed; + }, + enrichUrl: function enrichUrl(relativeUrl, serviceName) { + /** + * Combines the base url with version and the relative url + * @method enrichUrl + * @memberof StreamClient.prototype + * @private + * @param {string} relativeUrl + */ + if (!serviceName) { + serviceName = 'api'; + } -module.exports = JSONP; + var base_url = this.getBaseUrl(serviceName); + var url = base_url + this.version + '/' + relativeUrl; + return url; + }, + enrichKwargs: function enrichKwargs(kwargs) { + /** + * Adds the API key and the signature + * @method enrichKwargs + * @memberof StreamClient.prototype + * @param {object} kwargs + * @private + */ + kwargs.url = this.enrichUrl(kwargs.url, kwargs.serviceName); -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2))) + if (kwargs.qs === undefined) { + kwargs.qs = {}; + } -/***/ }), -/* 51 */ -/***/ (function(module, exports, __webpack_require__) { + if (!this.browser) { + kwargs.agent = this.requestAgent; + } -"use strict"; + kwargs.qs['api_key'] = this.apiKey; + kwargs.qs.location = this.group; + kwargs.json = true; + var signature = kwargs.signature || this.signature; + kwargs.headers = {}; // auto-detect authentication type and set HTTP headers accordingly + if (signing.isJWTSignature(signature)) { + kwargs.headers['stream-auth-type'] = 'jwt'; + signature = signature.split(' ').reverse()[0]; + } else { + kwargs.headers['stream-auth-type'] = 'simple'; + } -var Class = __webpack_require__(1), - Grammar = __webpack_require__(21); + kwargs.headers.Authorization = signature; + kwargs.headers['X-Stream-Client'] = this.userAgent(); // Make sure withCredentials is not enabled, different browser + // fallbacks handle it differently by default (meteor) -var Error = Class({ - initialize: function(code, params, message) { - this.code = code; - this.params = Array.prototype.slice.call(params); - this.message = message; + kwargs.withCredentials = false; + return kwargs; + }, + signActivity: function signActivity(activity) { + /** + * We automatically sign the to parameter when in server side mode + * @method signActivities + * @memberof StreamClient.prototype + * @private + * @param {object} [activity] Activity to sign + */ + return this.signActivities([activity])[0]; }, + signActivities: function signActivities(activities) { + /** + * We automatically sign the to parameter when in server side mode + * @method signActivities + * @memberof StreamClient.prototype + * @private + * @param {array} Activities + */ + if (!this.apiSecret) { + return activities; + } - toString: function() { - return this.code + ':' + - this.params.join(',') + ':' + - this.message; - } -}); + for (var i = 0; i < activities.length; i++) { + var activity = activities[i]; + var to = activity.to || []; + var signedTo = []; -Error.parse = function(message) { - message = message || ''; - if (!Grammar.ERROR.test(message)) return new Error(null, [], message); + for (var j = 0; j < to.length; j++) { + var feedId = to[j]; + var feedSlug = feedId.split(':')[0]; + var userId = feedId.split(':')[1]; + var token = this.feed(feedSlug, userId).token; + var signedFeed = feedId + ' ' + token; + signedTo.push(signedFeed); + } - var parts = message.split(':'), - code = parseInt(parts[0]), - params = parts[1].split(','), - message = parts[2]; + activity.to = signedTo; + } - return new Error(code, params, message); -}; + return activities; + }, + getFayeAuthorization: function getFayeAuthorization() { + /** + * Get the authorization middleware to use Faye with getstream.io + * @method getFayeAuthorization + * @memberof StreamClient.prototype + * @private + * @return {object} Faye authorization middleware + */ + var apiKey = this.apiKey, + self = this; + return { + incoming: function incoming(message, callback) { + callback(message); + }, + outgoing: function outgoing(message, callback) { + if (message.subscription && self.subscriptions[message.subscription]) { + var subscription = self.subscriptions[message.subscription]; + message.ext = { + 'user_id': subscription.userId, + 'api_key': apiKey, + 'signature': subscription.token + }; + } -// 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'] -}; + callback(message); + } + }; + }, + getFayeClient: function getFayeClient() { + /** + * Returns this client's current Faye client + * @method getFayeClient + * @memberof StreamClient.prototype + * @private + * @return {object} Faye client + */ + if (this.fayeClient === null) { + this.fayeClient = new Faye.Client(this.fayeUrl); + var authExtension = this.getFayeAuthorization(); + this.fayeClient.addExtension(authExtension); + } -for (var name in errors) - (function(name) { - Error[name] = function() { - return new Error(errors[name][0], arguments, errors[name][1]).toString(); + return this.fayeClient; + }, + get: function get(kwargs, cb) { + /** + * Shorthand function for get request + * @method get + * @memberof StreamClient.prototype + * @private + * @param {object} kwargs + * @param {requestCallback} cb Callback to call on completion + * @return {Promise} Promise object + */ + return new Promise(function (fulfill, reject) { + this.send('request', 'get', kwargs, cb); + kwargs = this.enrichKwargs(kwargs); + kwargs.method = 'GET'; + kwargs.gzip = true; + var callback = this.wrapPromiseTask(cb, fulfill, reject); + this.request(kwargs, callback); + }.bind(this)); + }, + post: function post(kwargs, cb) { + /** + * Shorthand function for post request + * @method post + * @memberof StreamClient.prototype + * @private + * @param {object} kwargs + * @param {requestCallback} cb Callback to call on completion + * @return {Promise} Promise object + */ + return new Promise(function (fulfill, reject) { + this.send('request', 'post', kwargs, cb); + kwargs = this.enrichKwargs(kwargs); + kwargs.method = 'POST'; + kwargs.gzip = true; + var callback = this.wrapPromiseTask(cb, fulfill, reject); + this.request(kwargs, callback); + }.bind(this)); + }, + 'delete': function _delete(kwargs, cb) { + /** + * Shorthand function for delete request + * @method delete + * @memberof StreamClient.prototype + * @private + * @param {object} kwargs + * @param {requestCallback} cb Callback to call on completion + * @return {Promise} Promise object + */ + return new Promise(function (fulfill, reject) { + this.send('request', 'delete', kwargs, cb); + kwargs = this.enrichKwargs(kwargs); + kwargs.gzip = true; + kwargs.method = 'DELETE'; + var callback = this.wrapPromiseTask(cb, fulfill, reject); + this.request(kwargs, callback); + }.bind(this)); + }, + updateActivities: function updateActivities(activities, callback) { + /** + * Updates all supplied activities on the getstream-io api + * @since 3.1.0 + * @param {array} activities list of activities to update + * @return {Promise} + */ + if (!(activities instanceof Array)) { + throw new TypeError('The activities argument should be an Array'); + } + + var authToken = signing.JWTScopeToken(this.apiSecret, 'activities', '*', { + feedId: '*', + expireTokens: this.expireTokens + }); + var data = { + activities: activities }; - })(name); + return this.post({ + url: 'activities/', + body: data, + signature: authToken + }, callback); + }, + updateActivity: function updateActivity(activity, callback) { + /** + * Updates one activity on the getstream-io api + * @since 3.1.0 + * @param {object} activity The activity to update + * @return {Promise} + */ + return this.updateActivities([activity], callback); + }, + getActivities: function getActivities(params, callback) { + /** + * Retrieve activities by ID or foreign ID and time + * @since 3.19.0 + * @param {object} params object containing either the list of activity IDs as {ids: ['...', ...]} or foreign IDs and time as {foreignIDTimes: [{foreignID: ..., time: ...}, ...]} + * @return {Promise} + */ + var qs = {}; -module.exports = Error; + if (params.ids) { + var ids = params.ids; + if (!(ids instanceof Array)) { + throw new TypeError('The ids argument should be an Array'); + } -/***/ }), -/* 52 */ -/***/ (function(module, exports, __webpack_require__) { + qs['ids'] = ids.join(','); + } else if (params.foreignIDTimes) { + var list = params.foreignIDTimes; -"use strict"; + if (!(list instanceof Array)) { + throw new TypeError('The foreignIDTimes argument should be an Array'); + } + var foreignIDs = []; + var timestamps = []; -var extend = __webpack_require__(0), - Logging = __webpack_require__(6); + for (var i in list) { + if (!(list[i] instanceof Object)) { + throw new TypeError('foreignIDTimes elements should be Objects'); + } -var Extensible = { - addExtension: function(extension) { - this._extensions = this._extensions || []; - this._extensions.push(extension); - if (extension.added) extension.added(this); - }, + foreignIDs.push(list[i].foreignID); + timestamps.push(list[i].time); + } - removeExtension: function(extension) { - if (!this._extensions) return; - var i = this._extensions.length; - while (i--) { - if (this._extensions[i] !== extension) continue; - this._extensions.splice(i,1); - if (extension.removed) extension.removed(this); + qs['foreign_ids'] = foreignIDs.join(','); + qs['timestamps'] = timestamps.join(','); + } else { + throw new TypeError('Missing ids or foreignIDTimes params'); } - }, - - pipeThroughExtensions: function(stage, message, request, callback, context) { - this.debug('Passing through ? extensions: ?', stage, message); - if (!this._extensions) return callback.call(context, message); - var extensions = this._extensions.slice(); + var authToken = signing.JWTScopeToken(this.apiSecret, 'activities', '*', { + feedId: '*', + expireTokens: this.expireTokens + }); + return this.get({ + url: 'activities/', + qs: qs, + signature: authToken + }, callback); + }, + activityPartialUpdate: function activityPartialUpdate(data, callback) { + /** + * Update a single activity with partial operations. + * @since 3.20.0 + * @param {object} data object containing either the ID or the foreign ID and time of the activity and the operations to issue as set:{...} and unset:[...]. + * @return {Promise} + * @example + * client.activityPartialUpdate({ + * id: "54a60c1e-4ee3-494b-a1e3-50c06acb5ed4", + * set: { + * "product.price": 19.99, + * "shares": { + * "facebook": "...", + * "twitter": "...", + * } + * }, + * unset: [ + * "daily_likes", + * "popularity" + * ] + * }) + * @example + * client.activityPartialUpdate({ + * foreignID: "product:123", + * time: "2016-11-10T13:20:00.000000", + * set: { + * ... + * }, + * unset: [ + * ... + * ] + * }) + */ + if (data.foreignID) { + data['foreign_id'] = data.foreignID; + delete data.foreignID; + } - var pipe = function(message) { - if (!message) return callback.call(context, message); + if (data.id === undefined && (data.foreign_id === undefined || data.time === undefined)) { + throw new TypeError('Missing id or foreign ID and time'); + } - var extension = extensions.shift(); - if (!extension) return callback.call(context, message); + if (data.set && !(data.set instanceof Object)) { + throw new TypeError('set field should be an Object'); + } - var fn = extension[stage]; - if (!fn) return pipe(message); + if (data.unset && !(data.unset instanceof Array)) { + throw new TypeError('unset field should be an Array'); + } - if (fn.length >= 3) extension[stage](message, request, pipe); - else extension[stage](message, pipe); - }; - pipe(message); + var authToken = signing.JWTScopeToken(this.apiSecret, 'activities', '*', { + feedId: '*', + expireTokens: this.expireTokens + }); + return this.post({ + url: 'activity/', + body: data, + signature: authToken + }, callback); } }; -extend(Extensible, Logging); - -module.exports = Extensible; - - -/***/ }), -/* 53 */ -/***/ (function(module, exports, __webpack_require__) { +if (qs) { + StreamClient.prototype.createRedirectUrl = function (targetUrl, userId, events) { + /** + * Creates a redirect url for tracking the given events in the context of + * an email using Stream's analytics platform. Learn more at + * getstream.io/personalization + * @method createRedirectUrl + * @memberof StreamClient.prototype + * @param {string} targetUrl Target url + * @param {string} userId User id to track + * @param {array} events List of events to track + * @return {string} The redirect url + */ + var uri = url.parse(targetUrl); -"use strict"; + if (!(uri.host || uri.hostname && uri.port) && !uri.isUnix) { + throw new errors.MissingSchemaError('Invalid URI: "' + url.format(uri) + '"'); + } + var authToken = signing.JWTScopeToken(this.apiSecret, 'redirect_and_track', '*', { + userId: "*", + expireTokens: this.expireTokens + }); + var analyticsUrl = this.baseAnalyticsUrl + 'redirect/'; + var kwargs = { + 'auth_type': 'jwt', + 'authorization': authToken, + 'url': targetUrl, + 'api_key': this.apiKey, + 'events': JSON.stringify(events) + }; + var qString = utils.rfc3986(qs.stringify(kwargs, null, null, {})); + return analyticsUrl + '?' + qString; + }; +} // If we are in a node environment and batchOperations is available add the methods to the prototype of StreamClient -var Class = __webpack_require__(1), - Deferrable = __webpack_require__(8); -module.exports = Class(Deferrable); +if (BatchOperations) { + for (var key in BatchOperations) { + if (BatchOperations.hasOwnProperty(key)) { + StreamClient.prototype[key] = BatchOperations[key]; + } + } +} +module.exports = StreamClient; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(15))) /***/ }), -/* 54 */ +/* 56 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* WEBPACK VAR INJECTION */(function(process) { +/** + * @module stream + * @author Thierry Schellenbach + * BSD License + */ +var StreamClient = __webpack_require__(55); -var Class = __webpack_require__(1), - extend = __webpack_require__(0), - Deferrable = __webpack_require__(8); - -var Subscription = Class({ - initialize: function(client, channels, callback, context) { - this._client = client; - this._channels = channels; - this._callback = callback; - this._context = context; - this._cancelled = false; - }, +var errors = __webpack_require__(5); - withChannel: function(callback, context) { - this._withChannel = [callback, context]; - return this; - }, +var signing = __webpack_require__(14); - apply: function(context, args) { - var message = args[0]; +var request = __webpack_require__(25); - if (this._callback) - this._callback.call(this._context, message.data); +function connect(apiKey, apiSecret, appId, options) { + /** + * Create StreamClient + * @method connect + * @param {string} apiKey API key + * @param {string} [apiSecret] API secret (only use this on the server) + * @param {string} [appId] Application identifier + * @param {object} [options] Additional options + * @param {string} [options.location] Datacenter location + * @return {StreamClient} StreamClient + * @example Basic usage + * stream.connect(apiKey, apiSecret); + * @example or if you want to be able to subscribe and listen + * stream.connect(apiKey, apiSecret, appId); + * @example or on Heroku + * stream.connect(streamURL); + * @example where streamURL looks like + * "https://thierry:pass@gestream.io/?app=1" + */ + if (typeof process !== 'undefined' && process.env.STREAM_URL && !apiKey) { + var parts = /https\:\/\/(\w+)\:(\w+)\@([\w-]*).*\?app_id=(\d+)/.exec(process.env.STREAM_URL); // eslint-disable-line no-useless-escape - if (this._withChannel) - this._withChannel[0].call(this._withChannel[1], message.channel, message.data); - }, + apiKey = parts[1]; + apiSecret = parts[2]; + var location = parts[3]; + appId = parts[4]; - cancel: function() { - if (this._cancelled) return; - this._client.unsubscribe(this._channels, this); - this._cancelled = true; - }, + if (options === undefined) { + options = {}; + } - unsubscribe: function() { - this.cancel(); + if (location !== 'getstream' && location !== 'stream-io-api') { + options.location = location; + } } -}); - -extend(Subscription.prototype, Deferrable); - -module.exports = Subscription; - - -/***/ }), -/* 55 */ -/***/ (function(module, exports) { - -/* (ignored) */ -/***/ }), -/* 56 */ -/***/ (function(module, exports) { + return new StreamClient(apiKey, apiSecret, appId, options); +} -/* (ignored) */ +module.exports.connect = connect; +module.exports.errors = errors; +module.exports.request = request; +module.exports.signing = signing; +module.exports.Client = StreamClient; +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(15))) /***/ }) /******/ ]); diff --git a/dist/js_min/getstream.js b/dist/js_min/getstream.js index 85093be8..6e462aac 100644 --- a/dist/js_min/getstream.js +++ b/dist/js_min/getstream.js @@ -1 +1 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.stream=e():t.stream=e()}(window,function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(i,r,function(e){return t[e]}.bind(null,r));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="dist/",n(n.s=26)}([function(t,e,n){"use strict";t.exports=function(t,e,n){if(!e)return t;for(var i in e)e.hasOwnProperty(i)&&(t.hasOwnProperty(i)&&!1===n||t[i]!==e[i]&&(t[i]=e[i]));return t}},function(t,e,n){"use strict";var i=n(0);t.exports=function(t,e){"function"!=typeof t&&(e=t,t=Object);var n=function(){return this.initialize&&this.initialize.apply(this,arguments)||this},r=function(){};return r.prototype=t.prototype,n.prototype=new r,i(n.prototype,e),n}},function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){"use strict";t.exports={isURI:function(t){return t&&t.protocol&&t.host&&t.path},isSameOrigin:function(t){return t.protocol===location.protocol&&t.hostname===location.hostname&&t.port===location.port},parse:function(t){if("string"!=typeof t)return t;var e,n,i,r,s,o,a={},c=function(e,n){t=t.replace(n,function(t){return a[e]=t,""}),a[e]=a[e]||""};for(c("protocol",/^[a-z]+\:/i),c("host",/^\/\/[^\/\?#]+/),/^\//.test(t)||a.host||(t=location.pathname.replace(/[^\/]*$/,"")+t),c("pathname",/^[^\?#]*/),c("search",/^\?[^#]*/),c("hash",/^#.*/),a.protocol=a.protocol||location.protocol,a.host?(a.host=a.host.substr(2),e=a.host.split(":"),a.hostname=e[0],a.port=e[1]||""):(a.host=location.host,a.hostname=location.hostname,a.port=location.port),a.pathname=a.pathname||"/",a.path=a.pathname+a.search,o={},r=0,s=(i=(n=a.search.replace(/^\?/,""))?n.split("&"):[]).length;r1&&this._connectMessage&&(this._connectMessage.advice={timeout:0}),this._resolvePromise(this.request(this._outbox)),this._connectMessage=null,this._outbox=[]},_flushLargeBatch:function(){if(!(this.encode(this._outbox).length1&&(i=o[r]),i=i||o["CGI_"+s]):(i=o[r]||o[s])&&!o[r]&&console.warn("The environment variable "+s+" is discouraged. Use "+r+"."),i}}}}),{get:function(t,e,n,i,r){var s=t.endpoint;a.asyncEach(this._transports,function(s,o){var c=s[0],u=s[1],h=t.endpointFor(c);return a.indexOf(n,c)>=0?o():a.indexOf(e,c)<0?(u.isUsable(t,h,function(){}),o()):void u.isUsable(t,h,function(e){if(!e)return o();var n=u.hasOwnProperty("create")?u.create(t,h):new u(t,h);i.call(r,n)})},function(){throw new Error("Could not find a usable connection type for "+o.stringify(s))})},register:function(t,e){this._transports.push([t,e]),e.prototype.connectionType=t},getConnectionTypes:function(){return a.map(this._transports,function(t){return t[0]})},_transports:[]});c(f.prototype,u),c(f.prototype,h),t.exports=f}).call(this,n(10))},function(t,e,n){"use strict";var i=n(18),r=function(t){return t},s=function(t){throw t},o=function(t){if(this._state=0,this._onFulfilled=[],this._onRejected=[],"function"==typeof t){var e=this;t(function(t){l(e,t)},function(t){d(e,t)})}};o.prototype.then=function(t,e){var n=new o;return a(this,t,n),c(this,e,n),n},o.prototype.catch=function(t){return this.then(null,t)};var a=function(t,e,n){"function"!=typeof e&&(e=r);var i=function(t){u(e,t,n)};0===t._state?t._onFulfilled.push(i):1===t._state&&i(t._value)},c=function(t,e,n){"function"!=typeof e&&(e=s);var i=function(t){u(e,t,n)};0===t._state?t._onRejected.push(i):2===t._state&&i(t._reason)},u=function(t,e,n){i(function(){h(t,e,n)})},h=function(t,e,n){var i;try{i=t(e)}catch(t){return d(n,t)}i===n?d(n,new TypeError("Recursive promise chain detected")):l(n,i)},l=function(t,e){var n,i,r=!1;try{if(n=typeof e,"function"!=typeof(i=null!==e&&("function"===n||"object"===n)&&e.then))return f(t,e);i.call(e,function(e){r^(r=!0)&&l(t,e)},function(e){r^(r=!0)&&d(t,e)})}catch(e){if(!(r^(r=!0)))return;d(t,e)}},f=function(t,e){if(0===t._state){t._state=1,t._value=e,t._onRejected=[];for(var n,i=t._onFulfilled;n=i.shift();)n(e)}},d=function(t,e){if(0===t._state){t._state=2,t._reason=e,t._onFulfilled=[];for(var n,i=t._onRejected;n=i.shift();)n(e)}};o.resolve=function(t){return new o(function(e,n){e(t)})},o.reject=function(t){return new o(function(e,n){n(t)})},o.all=function(t){return new o(function(e,n){var i,r=[],s=t.length;if(0===s)return e(r);for(i=0;i1)for(var n=1;n0;)c();a=!1}}()};u()}}},function(t,e,n){"use strict";(function(e){var n={_registry:[],on:function(t,e,n,i){var r=function(){n.call(i)};t.addEventListener?t.addEventListener(e,r,!1):t.attachEvent("on"+e,r),this._registry.push({_element:t,_type:e,_callback:n,_context:i,_handler:r})},detach:function(t,e,n,i){for(var r,s=this._registry.length;s--;)r=this._registry[s],t&&t!==r._element||e&&e!==r._type||n&&n!==r._callback||i&&i!==r._context||(r._element.removeEventListener?r._element.removeEventListener(r._type,r._handler,!1):r._element.detachEvent("on"+r._type,r._handler),this._registry.splice(s,1),r=null)}};void 0!==e.onunload&&n.on(e,"unload",n.detach,n),t.exports={Event:n}}).call(this,n(2))},function(t,e,n){"use strict";var i={countListeners:function(t){return this.listeners(t).length},bind:function(t,e,n){var i=Array.prototype.slice,r=function(){e.apply(n,i.call(arguments))};return this._listeners=this._listeners||[],this._listeners.push([t,e,n,r]),this.on(t,r)},unbind:function(t,e,n){this._listeners=this._listeners||[];for(var i,r=this._listeners.length;r--;)(i=this._listeners[r])[0]===t&&(!e||i[1]===e&&i[2]===n)&&(this._listeners.splice(r,1),this.removeListener(t,i[3]))}};n(0)(i,n(42).prototype),i.trigger=i.emit,t.exports=i},function(t,e,n){"use strict";var i=function(t){var e,n,r;if(t instanceof Array){for(e=[],n=t.length;n--;)e[n]=i(t[n]);return e}if("object"==typeof t){for(r in e=null===t?null:{},t)e[r]=i(t[r]);return e}return t};t.exports=i},function(t,e){var n=XMLHttpRequest;if(!n)throw new Error("missing XMLHttpRequest");i.log={trace:s,debug:s,info:s,warn:s,error:s};function i(t,e){if("function"!=typeof e)throw new Error("Bad callback given: "+e);if(!t)throw new Error("No options given");var a=t.onResponse;if((t="string"==typeof t?{uri:t}:JSON.parse(JSON.stringify(t))).onResponse=a,t.verbose&&(i.log=function(){var t,e,n={},i=["trace","debug","info","warn","error"];for(e=0;e>18&63,s=c>>12&63,o=c>>6&63,a=63&c,d[l++]=u.charAt(r)+u.charAt(s)+u.charAt(o)+u.charAt(a)}while(h>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(arguments.length>1&&(n=e),i=0;i299)&&i.error){for(var r in t=new Error("CouchDB error: "+(i.error.reason||i.error.error)),i)t[r]=i[r];return e(t,n,i)}return e(t,n,i)})},t.exports=i},function(t,e,n){"use strict";var i=n(9),r=/^[\w]+$/,s=/^[\w-]+$/;function o(t){if(!r.test(t))throw new i.FeedError("Invalid feedSlug, please use letters, numbers or _: "+t);return t}function a(t){if(!s.test(t))throw new i.FeedError("Invalid userId, please use letters, numbers, - or _: "+t);return t}e.validateFeedId=function(t){var e=t.split(":");if(2!==e.length)throw new i.FeedError("Invalid feedId, expected something like user:1 got "+t);var n=e[0],r=e[1];return o(n),a(r),t},e.validateFeedSlug=o,e.validateUserId=a,e.rfc3986=function(t){return t.replace(/[!'()*]/g,function(t){return"%"+t.charCodeAt(0).toString(16).toUpperCase()})}},function(t,e,n){"use strict";var i=n(36),r=[],s=[],o=i.makeRequestCallFromTimer(function(){if(s.length)throw s.shift()});function a(t){var e;(e=r.length?r.pop():new c).task=t,i(e)}function c(){this.task=null}t.exports=a,c.prototype.call=function(){try{this.task.call()}catch(t){a.onerror?a.onerror(t):(s.push(t),o())}finally{this.task=null,r[r.length]=this}}},function(t,e){t.exports={VERSION:"1.2.4",BAYEUX_VERSION:"1.0",ID_LENGTH:160,JSONP_CALLBACK:"jsonpcallback",CONNECTION_TYPES:["long-polling","cross-origin-long-polling","callback-polling","websocket","eventsource","in-process"],MANDATORY_CONNECTION_TYPES:["long-polling","callback-polling","in-process"]}},function(t,e,n){"use strict";var i=n(1),r=n(0),s=n(14),o=n(21),a=i({initialize:function(t){this.id=this.name=t},push:function(t){this.trigger("message",t)},isUnused:function(){return 0===this.countListeners("message")}});r(a.prototype,s),r(a,{HANDSHAKE:"/meta/handshake",CONNECT:"/meta/connect",SUBSCRIBE:"/meta/subscribe",UNSUBSCRIBE:"/meta/unsubscribe",DISCONNECT:"/meta/disconnect",META:"meta",SERVICE:"service",expand:function(t){var e=this.parse(t),n=["/**",t],i=e.slice();i[i.length-1]="*",n.push(this.unparse(i));for(var r=1,s=e.length;r=200&&o<300||304===o||1223===o;if(void 0!==e.onbeforeunload&&s.Event.detach(e,"beforeunload",c),n.onreadystatechange=function(){},n=null,!u)return r._handleError(t);try{i=JSON.parse(a)}catch(t){}i?r._receive(i):r._handleError(t)}},n.send(this.encode(t)),n}}),{isUsable:function(t,e,n,i){var s="ReactNative"===navigator.product||r.isSameOrigin(e);n.call(i,s)}});t.exports=c}).call(this,n(2))},function(t,e,n){"use strict";var i=function(t,e){this.message=t,this.options=e,this.attempts=0};n(0)(i.prototype,{getTimeout:function(){return this.options.timeout},getInterval:function(){return this.options.interval},isDeliverable:function(){var t=this.options.attempts,e=this.attempts,n=this.options.deadline,i=(new Date).getTime();return!(void 0!==t&&e>=t)&&!(void 0!==n&&i>n)},send:function(){this.attempts+=1},succeed:function(){},fail:function(){},abort:function(){}}),t.exports=i},function(t,e,n){"use strict";(function(e){var i=n(27),r=n(9),s=n(11),o=n(16);t.exports.connect=function(t,n,r,s){if(void 0!==e&&e.env.STREAM_URL&&!t){var o=/https\:\/\/(\w+)\:(\w+)\@([\w-]*).*\?app_id=(\d+)/.exec(e.env.STREAM_URL);t=o[1],n=o[2];var a=o[3];r=o[4],void 0===s&&(s={}),"getstream"!==a&&"stream-io-api"!==a&&(s.location=a)}return new i(t,n,r,s)},t.exports.errors=r,t.exports.request=o,t.exports.signing=s,t.exports.Client=i}).call(this,n(10))},function(t,e,n){"use strict";(function(e){var i=n(28),r=n(29),s=n(16),o=n(30),a=n(11),c=n(9),u=n(17),h=n(34),l=n(35),f=n(37),d=n(38),p=n(39),v=function(){this.initialize.apply(this,arguments)};if(v.prototype={baseUrl:"https://api.stream-io-api.com/api/",baseAnalyticsUrl:"https://analytics.stream-io-api.com/analytics/",initialize:function(t,o,u,h){if(this.apiKey=t,this.apiSecret=o,this.appId=u,this.options=h||{},this.version=this.options.version||"v1.0",this.fayeUrl=this.options.fayeUrl||"https://faye.getstream.io/faye",this.fayeClient=null,this.request=s,this.group=this.options.group||"unspecified",this.subscriptions={},this.expireTokens=!!this.options.expireTokens&&this.options.expireTokens,this.location=this.options.location,this.baseUrl=this.getBaseUrl(),void 0!==e&&e.env.LOCAL_FAYE&&(this.fayeUrl="http://localhost:9999/faye/"),void 0!==e&&e.env.STREAM_ANALYTICS_BASE_URL&&(this.baseAnalyticsUrl=e.env.STREAM_ANALYTICS_BASE_URL),this.handlers={},this.browser="undefined"!=typeof window,this.node=!this.browser,!this.browser){var l=n(55),f=new(n(56).Agent)({keepAlive:!0,keepAliveMsecs:3e3}),d=new l.Agent({keepAlive:!0,keepAliveMsecs:3e3});this.requestAgent=this.baseUrl.startsWith("https://")?f:d,this.personalizationToken=a.JWTScopeToken(this.apiSecret,"personalization","*",{userId:"*",feedId:"*",expireTokens:this.expireTokens}),this.collectionsToken=a.JWTScopeToken(this.apiSecret,"collections","*",{userId:"*",feedId:"*",expireTokens:this.expireTokens}),this.personalization=new r(this),this.collections=new i(this)}if(this.browser&&this.apiSecret)throw new c.FeedError('You are publicly sharing your App Secret. Do not expose the App Secret in browsers, "native" mobile apps, or other non-trusted environments.')},getBaseUrl:function(t){t||(t="api");var n,i=this.baseUrl;("api"!=t&&(i="https://"+t+".stream-io-api.com/"+t+"/"),this.location)&&(i=(this.options.protocol||"https")+"://"+this.location+"-"+t+".stream-io-api.com/"+t+"/");return void 0!==e&&e.env.LOCAL&&(i="http://localhost:8000/"+t+"/"),n="api"==t?"STREAM_BASE_URL":"STREAM_"+t.toUpperCase()+"_URL",void 0!==e&&e.env[n]&&(i=e.env[n]),i},on:function(t,e){this.handlers[t]=e},off:function(t){void 0===t?this.handlers={}:delete this.handlers[t]},send:function(){var t=Array.prototype.slice.call(arguments),e=t[0];t=t.slice(1),this.handlers[e]&&this.handlers[e].apply(this,t)},wrapPromiseTask:function(t,e,n){var i=this,r=this.wrapCallback(t);return function(t,s,o){t?n(new c.StreamApiError(t,o,s)):/^2/.test(""+s.statusCode)?e(o):n(new c.StreamApiError(JSON.stringify(o)+" with HTTP status code "+s.statusCode,o,s)),r.call(i,t,s,o)}},wrapCallback:function(t){var e=this;return function(){var n=Array.prototype.slice.call(arguments),i=["response"].concat(n);e.send.apply(e,i),void 0!==t&&t.apply(e,n)}},userAgent:function(){return"stream-javascript-client-"+(this.node?"node":"browser")+"-unknown"},getReadOnlyToken:function(t,e){return this.feed(t,e).getReadOnlyToken()},getReadWriteToken:function(t,e){return this.feed(t,e).getReadWriteToken()},feed:function(t,e,n,i,r){if(r=r||{},!t||!e)throw new c.FeedError('Please provide a feed slug and user id, ie client.feed("user", "1")');if(-1!==t.indexOf(":"))throw new c.FeedError('Please initialize the feed using client.feed("user", "1") not client.feed("user:1")');if(u.validateFeedSlug(t),u.validateUserId(e),!this.apiSecret&&!n)throw new c.FeedError("Missing token, in client side mode please provide a feed secret");if(this.apiSecret&&!n){var s=""+t+e;n=r.readOnly?this.getReadOnlyToken(t,e):a.sign(this.apiSecret,s)}var h=new o(this,t,e,n,i);return h},enrichUrl:function(t,e){return e||(e="api"),this.getBaseUrl(e)+this.version+"/"+t},enrichKwargs:function(t){t.url=this.enrichUrl(t.url,t.serviceName),void 0===t.qs&&(t.qs={}),this.browser||(t.agent=this.requestAgent),t.qs.api_key=this.apiKey,t.qs.location=this.group,t.json=!0;var e=t.signature||this.signature;return t.headers={},a.isJWTSignature(e)?(t.headers["stream-auth-type"]="jwt",e=e.split(" ").reverse()[0]):t.headers["stream-auth-type"]="simple",t.headers.Authorization=e,t.headers["X-Stream-Client"]=this.userAgent(),t.withCredentials=!1,t},signActivity:function(t){return this.signActivities([t])[0]},signActivities:function(t){if(!this.apiSecret)return t;for(var e=0;e>8-o%1*8)){if((r=s.charCodeAt(o+=.75))>255)throw new i("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|r}return c}),t.atob||(t.atob=function(t){var e=String(t).replace(/[=]+$/,"");if(e.length%4==1)throw new i("'atob' failed: The string to be decoded is not correctly encoded.");for(var r,s,o=0,a=0,c="";s=e.charAt(a++);~s&&(r=o%4?64*r+s:s,o++%4)?c+=String.fromCharCode(255&r>>(-2*o&6)):0)s=n.indexOf(s);return c})}()},function(t,e){},function(t,e,n){"use strict";var i=n(5);t.exports=i},function(t,e,n){"use strict";(function(e){function n(t){r.length||(i(),!0),r[r.length]=t}t.exports=n;var i,r=[],s=0,o=1024;function a(){for(;so){for(var e=0,n=r.length-s;e=Math.pow(2,32)&&(this._messageId=0),this._messageId.toString(36)},_receiveMessage:function(t){var e,n=t.id;void 0!==t.successful&&(e=this._responseCallbacks[n],delete this._responseCallbacks[n]),this.pipeThroughExtensions("incoming",t,null,function(t){t&&(t.advice&&this._handleAdvice(t.advice),this._deliverMessage(t),e&&e[0].call(e[1],t))},this)},_handleAdvice:function(t){u(this._advice,t),this._dispatcher.timeout=this._advice.timeout/1e3,this._advice.reconnect===this.HANDSHAKE&&this._state!==this.DISCONNECTED&&(this._state=this.UNCONNECTED,this._dispatcher.clientId=null,this._cycleConnection())},_deliverMessage:function(t){t.channel&&void 0!==t.data&&(this.info("Client ? calling listeners for ? with ?",this._dispatcher.clientId,t.channel,t.data),this._channels.distributeMessage(t))},_cycleConnection:function(){this._connectRequest&&(this._connectRequest=null,this.info("Closed connection for ?",this._dispatcher.clientId));var t=this;e.setTimeout(function(){t.connect()},this._advice.interval)}});u(b.prototype,l),u(b.prototype,d),u(b.prototype,f),u(b.prototype,g),t.exports=b}).call(this,n(2))},function(t,e,n){"use strict";var i=n(12);t.exports=function(t,e){for(var n in t)if(i.indexOf(e,n)<0)throw new Error("Unrecognized option: "+n)}},function(t,e){var n="function"==typeof Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};function i(){}t.exports=i,i.prototype.emit=function(t){if("error"===t&&(!this._events||!this._events.error||n(this._events.error)&&!this._events.error.length))throw arguments[1]instanceof Error?arguments[1]:new Error("Uncaught, unspecified 'error' event.");if(!this._events)return!1;var e=this._events[t];if(!e)return!1;if("function"==typeof e){switch(arguments.length){case 1:e.call(this);break;case 2:e.call(this,arguments[1]);break;case 3:e.call(this,arguments[1],arguments[2]);break;default:var i=Array.prototype.slice.call(arguments,1);e.apply(this,i)}return!0}if(n(e)){i=Array.prototype.slice.call(arguments,1);for(var r=e.slice(),s=0,o=r.length;s=3?a[t](e,n,o):a[t](e,o)};o(e)}};n(0)(i,n(6)),t.exports=i},function(t,e,n){"use strict";var i=n(1),r=n(8);t.exports=i(r)},function(t,e,n){"use strict";var i=n(1),r=n(0),s=n(8),o=i({initialize:function(t,e,n,i){this._client=t,this._channels=e,this._callback=n,this._context=i,this._cancelled=!1},withChannel:function(t,e){return this._withChannel=[t,e],this},apply:function(t,e){var n=e[0];this._callback&&this._callback.call(this._context,n.data),this._withChannel&&this._withChannel[0].call(this._withChannel[1],n.channel,n.data)},cancel:function(){this._cancelled||(this._client.unsubscribe(this._channels,this),this._cancelled=!0)},unsubscribe:function(){this.cancel()}});r(o.prototype,s),t.exports=o},function(t,e){},function(t,e){}])}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.stream=e():t.stream=e()}(window,function(){return function(t){var e={};function n(i){if(e[i])return e[i].exports;var r=e[i]={i:i,l:!1,exports:{}};return t[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:i})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="dist/",n(n.s=56)}([function(t,e,n){"use strict";t.exports=function(t,e,n){if(!e)return t;for(var i in e)e.hasOwnProperty(i)&&(t.hasOwnProperty(i)&&!1===n||t[i]!==e[i]&&(t[i]=e[i]));return t}},function(t,e,n){"use strict";var i=n(0);t.exports=function(t,e){"function"!=typeof t&&(e=t,t=Object);var n=function(){return this.initialize&&this.initialize.apply(this,arguments)||this},r=function(){};return r.prototype=t.prototype,n.prototype=new r,i(n.prototype,e),n}},function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){"use strict";t.exports={isURI:function(t){return t&&t.protocol&&t.host&&t.path},isSameOrigin:function(t){return t.protocol===location.protocol&&t.hostname===location.hostname&&t.port===location.port},parse:function(t){if("string"!=typeof t)return t;var e,n,i,r,s,o,a={},c=function(e,n){t=t.replace(n,function(t){return a[e]=t,""}),a[e]=a[e]||""};for(c("protocol",/^[a-z]+\:/i),c("host",/^\/\/[^\/\?#]+/),/^\//.test(t)||a.host||(t=location.pathname.replace(/[^\/]*$/,"")+t),c("pathname",/^[^\?#]*/),c("search",/^\?[^#]*/),c("hash",/^#.*/),a.protocol=a.protocol||location.protocol,a.host?(a.host=a.host.substr(2),e=a.host.split(":"),a.hostname=e[0],a.port=e[1]||""):(a.host=location.host,a.hostname=location.hostname,a.port=location.port),a.pathname=a.pathname||"/",a.path=a.pathname+a.search,o={},r=0,s=(i=(n=a.search.replace(/^\?/,""))?n.split("&"):[]).length;r1&&this._connectMessage&&(this._connectMessage.advice={timeout:0}),this._resolvePromise(this.request(this._outbox)),this._connectMessage=null,this._outbox=[]},_flushLargeBatch:function(){if(!(this.encode(this._outbox).length1&&(i=o[r]),i=i||o["CGI_"+s]):(i=o[r]||o[s])&&!o[r]&&console.warn("The environment variable "+s+" is discouraged. Use "+r+"."),i}}}}),{get:function(t,e,n,i,r){var s=t.endpoint;a.asyncEach(this._transports,function(s,o){var c=s[0],u=s[1],h=t.endpointFor(c);return a.indexOf(n,c)>=0?o():a.indexOf(e,c)<0?(u.isUsable(t,h,function(){}),o()):void u.isUsable(t,h,function(e){if(!e)return o();var n=u.hasOwnProperty("create")?u.create(t,h):new u(t,h);i.call(r,n)})},function(){throw new Error("Could not find a usable connection type for "+o.stringify(s))})},register:function(t,e){this._transports.push([t,e]),e.prototype.connectionType=t},getConnectionTypes:function(){return a.map(this._transports,function(t){return t[0]})},_transports:[]});c(f.prototype,u),c(f.prototype,h),t.exports=f}).call(this,n(15))},function(t,e,n){"use strict";var i=t.exports,r="function"==typeof Error.captureStackTrace,s=!!(new Error).stack;function o(t,e){this.message=t,Error.call(this,this.message),r?Error.captureStackTrace(this,e):this.stack=s?(new Error).stack:""}i._Abstract=o,o.prototype=new Error,i.FeedError=function(t){o.call(this,t)},i.FeedError.prototype=new o,i.SiteError=function(t){o.call(this,t)},i.SiteError.prototype=new o,i.MissingSchemaError=function(t){o.call(this,t)},i.MissingSchemaError.prototype=new o,i.StreamApiError=function(t,e,n){this.error=e,this.response=n,o.call(this,t)},i.StreamApiError.prototype=new o},function(t,e,n){"use strict";(function(e){var i=n(9);t.exports={then:function(t,e){var n=this;return this._promise||(this._promise=new i(function(t,e){n._resolve=t,n._reject=e})),0===arguments.length?this._promise:this._promise.then(t,e)},callback:function(t,e){return this.then(function(n){t.call(e,n)})},errback:function(t,e){return this.then(null,function(n){t.call(e,n)})},timeout:function(t,n){this.then();var i=this;this._timer=e.setTimeout(function(){i._reject(n)},1e3*t)},setDeferredStatus:function(t,n){this._timer&&e.clearTimeout(this._timer),this.then(),"succeeded"===t?this._resolve(n):"failed"===t?this._reject(n):delete this._promise}}}).call(this,n(2))},function(t,e,n){"use strict";t.exports=function(t){return JSON.stringify(t,function(t,e){return this[t]instanceof Array?this[t]:e})}},function(t,e,n){"use strict";var i=n(7),r={LOG_LEVELS:{fatal:4,error:3,warn:2,info:1,debug:0},writeLog:function(t,e){var n=r.logger||(r.wrapper||r).logger;if(n){var s=Array.prototype.slice.apply(t),o="[Faye",a=this.className,c=s.shift().replace(/\?/g,function(){try{return i(s.shift())}catch(t){return"[Object]"}});a&&(o+="."+a),o+="] ","function"==typeof n[e]?n[e](o+c):"function"==typeof n&&n(o+c)}}};for(var s in r.LOG_LEVELS)!function(t){r[t]=function(){this.writeLog(arguments,t)}}(s);t.exports=r},function(t,e,n){"use strict";var i=n(23),r=function(t){return t},s=function(t){throw t},o=function(t){if(this._state=0,this._onFulfilled=[],this._onRejected=[],"function"==typeof t){var e=this;t(function(t){l(e,t)},function(t){d(e,t)})}};o.prototype.then=function(t,e){var n=new o;return a(this,t,n),c(this,e,n),n},o.prototype.catch=function(t){return this.then(null,t)};var a=function(t,e,n){"function"!=typeof e&&(e=r);var i=function(t){u(e,t,n)};0===t._state?t._onFulfilled.push(i):1===t._state&&i(t._value)},c=function(t,e,n){"function"!=typeof e&&(e=s);var i=function(t){u(e,t,n)};0===t._state?t._onRejected.push(i):2===t._state&&i(t._reason)},u=function(t,e,n){i(function(){h(t,e,n)})},h=function(t,e,n){var i;try{i=t(e)}catch(t){return d(n,t)}i===n?d(n,new TypeError("Recursive promise chain detected")):l(n,i)},l=function(t,e){var n,i,r=!1;try{if(n=typeof e,"function"!=typeof(i=null!==e&&("function"===n||"object"===n)&&e.then))return f(t,e);i.call(e,function(e){r^(r=!0)&&l(t,e)},function(e){r^(r=!0)&&d(t,e)})}catch(e){if(!(r^(r=!0)))return;d(t,e)}},f=function(t,e){if(0===t._state){t._state=1,t._value=e,t._onRejected=[];for(var n,i=t._onFulfilled;n=i.shift();)n(e)}},d=function(t,e){if(0===t._state){t._state=2,t._reason=e,t._onFulfilled=[];for(var n,i=t._onRejected;n=i.shift();)n(e)}};o.resolve=function(t){return new o(function(e,n){e(t)})},o.reject=function(t){return new o(function(e,n){n(t)})},o.all=function(t){return new o(function(e,n){var i,r=[],s=t.length;if(0===s)return e(r);for(i=0;i0;)c();a=!1}}()};u()}}},function(t,e,n){"use strict";function i(t){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var r=n(51),s=n(50),o=/^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/,a=n(49);function c(t){try{return a.atob(function(t){return function(t){var e=t.length%4;if(!e)return t;var n=4-e;for(;n--;)t+="=";return t}(t).replace(/\-/g,"+").replace(/_/g,"/")}(t))}catch(t){if("InvalidCharacterError"===t.name)return;throw t}}function u(t){return function(t){if("object"===i(t))return t;try{return JSON.parse(t)}catch(t){return}}(c(t.split(".",1)[0]))}e.headerFromJWS=u,e.sign=function(t,e){var n=new r.createHash("sha1").update(t).digest(),i=r.createHmac("sha1",n).update(e).digest("base64");return i.replace(/\+/g,"-").replace(/\//g,"_").replace(/^=+/,"").replace(/=+$/,"")},e.JWTScopeToken=function(t,e,n,i){var r=i||{},o=!r.expireTokens||!r.expireTokens,a={resource:e,action:n};return r.feedId&&(a.feed_id=r.feedId),r.userId&&(a.user_id=r.userId),s.sign(a,t,{algorithm:"HS256",noTimestamp:o})},e.isJWTSignature=function(t){var e=t.split(" ")[1]||t;return o.test(e)&&!!u(e)}},function(t,e){var n,i,r=t.exports={};function s(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function a(t){if(n===setTimeout)return setTimeout(t,0);if((n===s||!n)&&setTimeout)return n=setTimeout,setTimeout(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:s}catch(t){n=s}try{i="function"==typeof clearTimeout?clearTimeout:o}catch(t){i=o}}();var c,u=[],h=!1,l=-1;function f(){h&&c&&(h=!1,c.length?u=c.concat(u):l=-1,u.length&&d())}function d(){if(!h){var t=a(f);h=!0;for(var e=u.length;e;){for(c=u,u=[];++l1)for(var n=1;n=t)&&!(void 0!==n&&i>n)},send:function(){this.attempts+=1},succeed:function(){},fail:function(){},abort:function(){}}),t.exports=i},function(t,e,n){"use strict";(function(e){var i=n(1),r=n(3),s=n(12),o=n(0),a=n(7),c=o(i(n(4),{encode:function(t){return a(t)},request:function(t){var n,i=this.endpoint.href,r=this;if(e.XMLHttpRequest)n=new XMLHttpRequest;else{if(!e.ActiveXObject)return this._handleError(t);n=new ActiveXObject("Microsoft.XMLHTTP")}n.open("POST",i,!0),n.setRequestHeader("Content-Type","application/json"),n.setRequestHeader("Pragma","no-cache"),n.setRequestHeader("X-Requested-With","XMLHttpRequest");var o=this._dispatcher.headers;for(var a in o)o.hasOwnProperty(a)&&n.setRequestHeader(a,o[a]);var c=function(){n.abort()};return void 0!==e.onbeforeunload&&s.Event.on(e,"beforeunload",c),n.onreadystatechange=function(){if(n&&4===n.readyState){var i=null,o=n.status,a=n.responseText,u=o>=200&&o<300||304===o||1223===o;if(void 0!==e.onbeforeunload&&s.Event.detach(e,"beforeunload",c),n.onreadystatechange=function(){},n=null,!u)return r._handleError(t);try{i=JSON.parse(a)}catch(t){}i?r._receive(i):r._handleError(t)}},n.send(this.encode(t)),n}}),{isUsable:function(t,e,n,i){var s="ReactNative"===navigator.product||r.isSameOrigin(e);n.call(i,s)}});t.exports=c}).call(this,n(2))},function(t,e,n){"use strict";var i=n(1);t.exports=i({initialize:function(){this._index={}},add:function(t){var e=void 0!==t.id?t.id:t;return!this._index.hasOwnProperty(e)&&(this._index[e]=t,!0)},forEach:function(t,e){for(var n in this._index)this._index.hasOwnProperty(n)&&t.call(e,this._index[n])},isEmpty:function(){for(var t in this._index)if(this._index.hasOwnProperty(t))return!1;return!0},member:function(t){for(var e in this._index)if(this._index[e]===t)return!0;return!1},remove:function(t){var e=void 0!==t.id?t.id:t,n=this._index[e];return delete this._index[e],n},toArray:function(){var t=[];return this.forEach(function(e){t.push(e)}),t}})},function(t,e,n){"use strict";t.exports={}},function(t,e,n){"use strict";t.exports={CHANNEL_NAME:/^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,CHANNEL_PATTERN:/^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,ERROR:/^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/,VERSION:/^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/}},function(t,e,n){"use strict";var i=n(1),r=n(0),s=n(11),o=n(20),a=i({initialize:function(t){this.id=this.name=t},push:function(t){this.trigger("message",t)},isUnused:function(){return 0===this.countListeners("message")}});r(a.prototype,s),r(a,{HANDSHAKE:"/meta/handshake",CONNECT:"/meta/connect",SUBSCRIBE:"/meta/subscribe",UNSUBSCRIBE:"/meta/unsubscribe",DISCONNECT:"/meta/disconnect",META:"meta",SERVICE:"service",expand:function(t){var e=this.parse(t),n=["/**",t],i=e.slice();i[i.length-1]="*",n.push(this.unparse(i));for(var r=1,s=e.length;r>18&63,s=c>>12&63,o=c>>6&63,a=63&c,d[l++]=u.charAt(r)+u.charAt(s)+u.charAt(o)+u.charAt(a)}while(h>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(arguments.length>1&&(n=e),i=0;i299)&&i.error){for(var r in t=new Error("CouchDB error: "+(i.error.reason||i.error.error)),i)t[r]=i[r];return e(t,n,i)}return e(t,n,i)})},t.exports=i},function(t,e){},function(t,e){},function(t,e,n){"use strict";var i=n(1),r=n(0),s=n(6),o=i({initialize:function(t,e,n,i){this._client=t,this._channels=e,this._callback=n,this._context=i,this._cancelled=!1},withChannel:function(t,e){return this._withChannel=[t,e],this},apply:function(t,e){var n=e[0];this._callback&&this._callback.call(this._context,n.data),this._withChannel&&this._withChannel[0].call(this._withChannel[1],n.channel,n.data)},cancel:function(){this._cancelled||(this._client.unsubscribe(this._channels,this),this._cancelled=!0)},unsubscribe:function(){this.cancel()}});r(o.prototype,s),t.exports=o},function(t,e,n){"use strict";var i=n(1),r=n(6);t.exports=i(r)},function(t,e,n){"use strict";var i={addExtension:function(t){this._extensions=this._extensions||[],this._extensions.push(t),t.added&&t.added(this)},removeExtension:function(t){if(this._extensions)for(var e=this._extensions.length;e--;)this._extensions[e]===t&&(this._extensions.splice(e,1),t.removed&&t.removed(this))},pipeThroughExtensions:function(t,e,n,i,r){if(this.debug("Passing through ? extensions: ?",t,e),!this._extensions)return i.call(r,e);var s=this._extensions.slice(),o=function(e){if(!e)return i.call(r,e);var a=s.shift();if(!a)return i.call(r,e);var c=a[t];if(!c)return o(e);c.length>=3?a[t](e,n,o):a[t](e,o)};o(e)}};n(0)(i,n(8)),t.exports=i},function(t,e,n){"use strict";var i=n(1),r=n(20),s=i({initialize:function(t,e,n){this.code=t,this.params=Array.prototype.slice.call(e),this.message=n},toString:function(){return this.code+":"+this.params.join(",")+":"+this.message}});s.parse=function(t){if(t=t||"",!r.ERROR.test(t))return new s(null,[],t);var e=t.split(":"),n=parseInt(e[0]),i=e[1].split(",");t=e[2];return new s(n,i,t)};var o={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 a in o)!function(t){s[t]=function(){return new s(o[t][0],arguments,o[t][1]).toString()}}(a);t.exports=s},function(t,e,n){"use strict";(function(e){var i=n(1),r=n(3),s=n(10),o=n(0),a=n(7),c=o(i(n(4),{encode:function(t){var e=s(this.endpoint);return e.query.message=a(t),e.query.jsonp="__jsonp"+c._cbCount+"__",r.stringify(e)},request:function(t){var n=document.getElementsByTagName("head")[0],i=document.createElement("script"),o=c.getCallbackName(),u=s(this.endpoint),h=this;u.query.message=a(t),u.query.jsonp=o;var l=function(){if(!e[o])return!1;e[o]=void 0;try{delete e[o]}catch(t){}i.parentNode.removeChild(i)};return e[o]=function(t){l(),h._receive(t)},i.type="text/javascript",i.src=r.stringify(u),n.appendChild(i),i.onerror=function(){l(),h._handleError(t)},{abort:l}}}),{_cbCount:0,getCallbackName:function(){return this._cbCount+=1,"__jsonp"+this._cbCount+"__"},isUsable:function(t,e,n,i){n.call(i,!0)}});t.exports=c}).call(this,n(2))},function(t,e,n){"use strict";(function(e){var i=n(1),r=n(18),s=n(3),o=n(0),a=n(7),c=o(i(n(4),{encode:function(t){return"message="+encodeURIComponent(a(t))},request:function(t){var n,i=e.XDomainRequest?XDomainRequest:XMLHttpRequest,r=new i,o=++c._id,a=this._dispatcher.headers,u=this;if(r.open("POST",s.stringify(this.endpoint),!0),r.setRequestHeader)for(n in r.setRequestHeader("Pragma","no-cache"),a)a.hasOwnProperty(n)&&r.setRequestHeader(n,a[n]);var h=function(){if(!r)return!1;c._pending.remove(o),r.onload=r.onerror=r.ontimeout=r.onprogress=null,r=null};return r.onload=function(){var e;try{e=JSON.parse(r.responseText)}catch(t){}h(),e?u._receive(e):u._handleError(t)},r.onerror=r.ontimeout=function(){h(),u._handleError(t)},r.onprogress=function(){},i===e.XDomainRequest&&c._pending.add({id:o,xhr:r}),r.send(this.encode(t)),r}}),{_id:0,_pending:new r,isUsable:function(t,n,i,r){if(s.isSameOrigin(n))return i.call(r,!1);if(e.XDomainRequest)return i.call(r,n.protocol===location.protocol);if(e.XMLHttpRequest){var o=new XMLHttpRequest;return i.call(r,void 0!==o.withCredentials)}return i.call(r,!1)}});t.exports=c}).call(this,n(2))},function(t,e,n){"use strict";(function(e){var i=n(1),r=n(3),s=n(10),o=n(0),a=n(6),c=n(4),u=n(17),h=o(i(c,{initialize:function(t,n){if(c.prototype.initialize.call(this,t,n),!e.EventSource)return this.setDeferredStatus("failed");this._xhr=new u(t,n),(n=s(n)).pathname+="/"+t.clientId;var i=new e.EventSource(r.stringify(n)),o=this;i.onopen=function(){o._everConnected=!0,o.setDeferredStatus("succeeded")},i.onerror=function(){o._everConnected?o._handleError([]):(o.setDeferredStatus("failed"),i.close())},i.onmessage=function(t){var e;try{e=JSON.parse(t.data)}catch(t){}e?o._receive(e):o._handleError([])},this._socket=i},close:function(){this._socket&&(this._socket.onopen=this._socket.onerror=this._socket.onmessage=null,this._socket.close(),delete this._socket)},isUsable:function(t,e){this.callback(function(){t.call(e,!0)}),this.errback(function(){t.call(e,!1)})},encode:function(t){return this._xhr.encode(t)},request:function(t){return this._xhr.request(t)}}),{isUsable:function(t,e,n,i){if(!t.clientId)return n.call(i,!1);u.isUsable(t,e,function(r){if(!r)return n.call(i,!1);this.create(t,e).isUsable(n,i)},this)},create:function(t,e){var n=t.transports.eventsource=t.transports.eventsource||{},i=t.clientId,o=s(e);return o.pathname+="/"+(i||""),n[o=r.stringify(o)]=n[o]||new this(t,e),n[o]}});o(h.prototype,a),t.exports=h}).call(this,n(2))},function(t,e,n){"use strict";(function(e){var n=e.MozWebSocket||e.WebSocket;t.exports={create:function(t,e,i){return"function"!=typeof n?null:new n(t)}}}).call(this,n(2))},function(t,e,n){"use strict";(function(e){var i=n(1),r=n(9),s=n(18),o=n(3),a=n(12),c=n(10),u=n(0),h=n(7),l=n(35),f=n(6),d=u(i(n(4),{UNCONNECTED:1,CONNECTING:2,CONNECTED:3,batching:!1,isUsable:function(t,e){this.callback(function(){t.call(e,!0)}),this.errback(function(){t.call(e,!1)}),this.connect()},request:function(t){this._pending=this._pending||new s;for(var e=0,n=t.length;e=Math.pow(2,32)&&(this._messageId=0),this._messageId.toString(36)},_receiveMessage:function(t){var e,n=t.id;void 0!==t.successful&&(e=this._responseCallbacks[n],delete this._responseCallbacks[n]),this.pipeThroughExtensions("incoming",t,null,function(t){t&&(t.advice&&this._handleAdvice(t.advice),this._deliverMessage(t),e&&e[0].call(e[1],t))},this)},_handleAdvice:function(t){u(this._advice,t),this._dispatcher.timeout=this._advice.timeout/1e3,this._advice.reconnect===this.HANDSHAKE&&this._state!==this.DISCONNECTED&&(this._state=this.UNCONNECTED,this._dispatcher.clientId=null,this._cycleConnection())},_deliverMessage:function(t){t.channel&&void 0!==t.data&&(this.info("Client ? calling listeners for ? with ?",this._dispatcher.clientId,t.channel,t.data),this._channels.distributeMessage(t))},_cycleConnection:function(){this._connectRequest&&(this._connectRequest=null,this.info("Closed connection for ?",this._dispatcher.clientId));var t=this;e.setTimeout(function(){t.connect()},this._advice.interval)}});u(b.prototype,l),u(b.prototype,d),u(b.prototype,f),u(b.prototype,g),t.exports=b}).call(this,n(2))},function(t,e,n){"use strict";var i=n(22),r=n(8),s={VERSION:i.VERSION,Client:n(42),Scheduler:n(16)};r.wrapper=s,t.exports=s},function(t,e){},function(t,e){},function(t,e,n){"use strict";(function(e){function n(t){r.length||(i(),!0),r[r.length]=t}t.exports=n;var i,r=[],s=0,o=1024;function a(){for(;so){for(var e=0,n=r.length-s;e>8-o%1*8)){if((r=s.charCodeAt(o+=.75))>255)throw new i("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|r}return c}),t.atob||(t.atob=function(t){var e=String(t).replace(/[=]+$/,"");if(e.length%4==1)throw new i("'atob' failed: The string to be decoded is not correctly encoded.");for(var r,s,o=0,a=0,c="";s=e.charAt(a++);~s&&(r=o%4?64*r+s:s,o++%4)?c+=String.fromCharCode(255&r>>(-2*o&6)):0)s=n.indexOf(s);return c})}()},function(t,e){},function(t,e){},function(t,e,n){"use strict";function i(t){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var r=n(5),s=n(24),o=n(14),a=function(){this.initialize.apply(this,arguments)};a.prototype={initialize:function(t,e,n,i){this.client=t,this.slug=e,this.userId=n,this.id=this.slug+":"+this.userId,this.token=i,this.feedUrl=this.id.replace(":","/"),this.feedTogether=this.id.replace(":",""),this.signature=this.feedTogether+" "+this.token,this.notificationChannel="site-"+this.client.appId+"-feed-"+this.feedTogether},addActivity:function(t,e){return t=this.client.signActivity(t),this.client.post({url:"feed/"+this.feedUrl+"/",body:t,signature:this.signature},e)},removeActivity:function(t,e){var n=t.foreignId?t.foreignId:t,i={};return t.foreignId&&(i.foreign_id="1"),this.client.delete({url:"feed/"+this.feedUrl+"/"+n+"/",qs:i,signature:this.signature},e)},addActivities:function(t,e){var n={activities:t=this.client.signActivities(t)};return this.client.post({url:"feed/"+this.feedUrl+"/",body:n,signature:this.signature},e)},follow:function(t,e,n,i){var r;s.validateFeedSlug(t),s.validateUserId(e);var o=arguments[arguments.length-1];i=o.call?o:void 0;var a=t+":"+e;n&&!n.call&&void 0!==n.limit&&null!==n.limit&&(r=n.limit);var c={target:a};return void 0!==r&&null!==r&&(c.activity_copy_limit=r),this.client.post({url:"feed/"+this.feedUrl+"/following/",body:c,signature:this.signature},i)},unfollow:function(t,e,n,r){var o={},a={};"function"==typeof n&&(r=n),"object"===i(n)&&(o=n),"boolean"==typeof o.keepHistory&&o.keepHistory&&(a.keep_history="1"),s.validateFeedSlug(t),s.validateUserId(e);var c=t+":"+e;return this.client.delete({url:"feed/"+this.feedUrl+"/following/"+c+"/",qs:a,signature:this.signature},r)},following:function(t,e){return void 0!==t&&t.filter&&(t.filter=t.filter.join(",")),this.client.get({url:"feed/"+this.feedUrl+"/following/",qs:t,signature:this.signature},e)},followers:function(t,e){return void 0!==t&&t.filter&&(t.filter=t.filter.join(",")),this.client.get({url:"feed/"+this.feedUrl+"/followers/",qs:t,signature:this.signature},e)},get:function(t,e){return t&&t.mark_read&&t.mark_read.join&&(t.mark_read=t.mark_read.join(",")),t&&t.mark_seen&&t.mark_seen.join&&(t.mark_seen=t.mark_seen.join(",")),this.client.get({url:"feed/"+this.feedUrl+"/",qs:t,signature:this.signature},e)},getFayeClient:function(){return this.client.getFayeClient()},subscribe:function(t){if(!this.client.appId)throw new r.SiteError("Missing app id, which is needed to subscribe, use var client = stream.connect(key, secret, appId);");var e=this.getFayeClient().subscribe("/"+this.notificationChannel,t);return this.client.subscriptions["/"+this.notificationChannel]={token:this.token,userId:this.notificationChannel,fayeSubscription:e},e},unsubscribe:function(){var t=this.client.subscriptions["/"+this.notificationChannel];t&&(delete this.client.subscriptions["/"+this.notificationChannel],t.fayeSubscription.cancel())},getReadOnlyToken:function(){var t=""+this.slug+this.userId;return o.JWTScopeToken(this.client.apiSecret,"*","read",{feedId:t,expireTokens:this.client.expireTokens})},getReadWriteToken:function(){var t=""+this.slug+this.userId;return o.JWTScopeToken(this.client.apiSecret,"*","*",{feedId:t,expireTokens:this.client.expireTokens})},updateActivityToTargets:function(t,e,n,i,r){if(!t)throw new Error("Missing `foreign_id` parameter!");if(!e)throw new Error("Missing `time` parameter!");if(!n&&!i&&!r)throw new Error('Requires you to provide at least one parameter for `new_targets`, `added_targets`, or `removed_targets` - example: `updateActivityToTargets("foreignID:1234", new Date(), [new_targets...], [added_targets...], [removed_targets...])`');if(n&&(i||r))throw new Error("Can't include add_targets or removed_targets if you're also including new_targets");if(i&&r)for(var s=0;s