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