/******/ (function(modules) { // webpackBootstrap
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
/******/ var chunkIds = data[0];
/******/ var moreModules = data[1];
/******/
/******/
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [];
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/
/******/ };
/******/
/******/
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // Promise = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "/js/app": 0
/******/ };
/******/
/******/
/******/
/******/ // script path function
/******/ function jsonpScriptSrc(chunkId) {
/******/ return __webpack_require__.p + "js/chunks/" + ({}[chunkId]||chunkId) + ".js?id=" + {"0":"13c35861eb9ac0fc0484","1":"c1a388a8f1322a4e8192"}[chunkId] + ""
/******/ }
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = function requireEnsure(chunkId) {
/******/ var promises = [];
/******/
/******/
/******/ // JSONP chunk loading for javascript
/******/
/******/ var installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise(function(resolve, reject) {
/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/ });
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var script = document.createElement('script');
/******/ var onScriptComplete;
/******/
/******/ script.charset = 'utf-8';
/******/ script.timeout = 120;
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.src = jsonpScriptSrc(chunkId);
/******/
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ onScriptComplete = function (event) {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var chunk = installedChunks[chunkId];
/******/ if(chunk !== 0) {
/******/ if(chunk) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ chunk[1](error);
/******/ }
/******/ installedChunks[chunkId] = undefined;
/******/ }
/******/ };
/******/ var timeout = setTimeout(function(){
/******/ onScriptComplete({ type: 'timeout', target: script });
/******/ }, 120000);
/******/ script.onerror = script.onload = onScriptComplete;
/******/ document.head.appendChild(script);
/******/ }
/******/ }
/******/ return Promise.all(promises);
/******/ };
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/";
/******/
/******/ // on error function for async loading
/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
/******/
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ jsonpArray.push = webpackJsonpCallback;
/******/ jsonpArray = jsonpArray.slice();
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({
/***/ "./node_modules/@opentok/client/dist/js/opentok.js":
/*!*********************************************************!*\
!*** ./node_modules/@opentok/client/dist/js/opentok.js ***!
\*********************************************************/
/*! no static exports found */
/*! all exports used */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {/**
* @license OpenTok.js 2.17.3 32ce4158c
*
* Copyright (c) 2010-2020 TokBox, Inc.
* Subject to the applicable Software Development Kit (SDK) License Agreement:
* https://tokbox.com/support/sdk_license
*
* Date: Thu, 28 May 2020 09:27:20 GMT
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(true)
module.exports = factory();
else {}
})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 309);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(process) {/*!
* @overview es6-promise - a tiny implementation of Promises/A+.
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
* @license Licensed under MIT license
* See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
* @version v4.2.8+1e68dce6
*/
(function (global, factory) {
true ? module.exports = factory() :
undefined;
}(this, (function () { 'use strict';
function objectOrFunction(x) {
var type = typeof x;
return x !== null && (type === 'object' || type === 'function');
}
function isFunction(x) {
return typeof x === 'function';
}
var _isArray = void 0;
if (Array.isArray) {
_isArray = Array.isArray;
} else {
_isArray = function (x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
}
var isArray = _isArray;
var len = 0;
var vertxNext = void 0;
var customSchedulerFn = void 0;
var asap = function asap(callback, arg) {
queue[len] = callback;
queue[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 2, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
if (customSchedulerFn) {
customSchedulerFn(flush);
} else {
scheduleFlush();
}
}
};
function setScheduler(scheduleFn) {
customSchedulerFn = scheduleFn;
}
function setAsap(asapFn) {
asap = asapFn;
}
var browserWindow = typeof window !== 'undefined' ? window : undefined;
var browserGlobal = browserWindow || {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
// test for web worker but not in IE10
var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
// node
function useNextTick() {
// node version 0.10.x displays a deprecation warning when nextTick is used recursively
// see https://github.com/cujojs/when/issues/410 for details
return function () {
return process.nextTick(flush);
};
}
// vertx
function useVertxTimer() {
if (typeof vertxNext !== 'undefined') {
return function () {
vertxNext(flush);
};
}
return useSetTimeout();
}
function useMutationObserver() {
var iterations = 0;
var observer = new BrowserMutationObserver(flush);
var node = document.createTextNode('');
observer.observe(node, { characterData: true });
return function () {
node.data = iterations = ++iterations % 2;
};
}
// web worker
function useMessageChannel() {
var channel = new MessageChannel();
channel.port1.onmessage = flush;
return function () {
return channel.port2.postMessage(0);
};
}
function useSetTimeout() {
// Store setTimeout reference so es6-promise will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var globalSetTimeout = setTimeout;
return function () {
return globalSetTimeout(flush, 1);
};
}
var queue = new Array(1000);
function flush() {
for (var i = 0; i < len; i += 2) {
var callback = queue[i];
var arg = queue[i + 1];
callback(arg);
queue[i] = undefined;
queue[i + 1] = undefined;
}
len = 0;
}
function attemptVertx() {
try {
var vertx = Function('return this')().require('vertx');
vertxNext = vertx.runOnLoop || vertx.runOnContext;
return useVertxTimer();
} catch (e) {
return useSetTimeout();
}
}
var scheduleFlush = void 0;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush = useMutationObserver();
} else if (isWorker) {
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && "function" === 'function') {
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}
function then(onFulfillment, onRejection) {
var parent = this;
var child = new this.constructor(noop);
if (child[PROMISE_ID] === undefined) {
makePromise(child);
}
var _state = parent._state;
if (_state) {
var callback = arguments[_state - 1];
asap(function () {
return invokeCallback(_state, child, callback, parent._result);
});
} else {
subscribe(parent, child, onFulfillment, onRejection);
}
return child;
}
/**
`Promise.resolve` returns a promise that will become resolved with the
passed `value`. It is shorthand for the following:
```javascript
let promise = new Promise(function(resolve, reject){
resolve(1);
});
promise.then(function(value){
// value === 1
});
```
Instead of writing the above, your code now simply becomes the following:
```javascript
let promise = Promise.resolve(1);
promise.then(function(value){
// value === 1
});
```
@method resolve
@static
@param {Any} value value that the returned promise will be resolved with
Useful for tooling.
@return {Promise} a promise that will become fulfilled with the given
`value`
*/
function resolve$1(object) {
/*jshint validthis:true */
var Constructor = this;
if (object && typeof object === 'object' && object.constructor === Constructor) {
return object;
}
var promise = new Constructor(noop);
resolve(promise, object);
return promise;
}
var PROMISE_ID = Math.random().toString(36).substring(2);
function noop() {}
var PENDING = void 0;
var FULFILLED = 1;
var REJECTED = 2;
function selfFulfillment() {
return new TypeError("You cannot resolve a promise with itself");
}
function cannotReturnOwn() {
return new TypeError('A promises callback cannot return that same promise.');
}
function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
try {
then$$1.call(value, fulfillmentHandler, rejectionHandler);
} catch (e) {
return e;
}
}
function handleForeignThenable(promise, thenable, then$$1) {
asap(function (promise) {
var sealed = false;
var error = tryThen(then$$1, thenable, function (value) {
if (sealed) {
return;
}
sealed = true;
if (thenable !== value) {
resolve(promise, value);
} else {
fulfill(promise, value);
}
}, function (reason) {
if (sealed) {
return;
}
sealed = true;
reject(promise, reason);
}, 'Settle: ' + (promise._label || ' unknown promise'));
if (!sealed && error) {
sealed = true;
reject(promise, error);
}
}, promise);
}
function handleOwnThenable(promise, thenable) {
if (thenable._state === FULFILLED) {
fulfill(promise, thenable._result);
} else if (thenable._state === REJECTED) {
reject(promise, thenable._result);
} else {
subscribe(thenable, undefined, function (value) {
return resolve(promise, value);
}, function (reason) {
return reject(promise, reason);
});
}
}
function handleMaybeThenable(promise, maybeThenable, then$$1) {
if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
handleOwnThenable(promise, maybeThenable);
} else {
if (then$$1 === undefined) {
fulfill(promise, maybeThenable);
} else if (isFunction(then$$1)) {
handleForeignThenable(promise, maybeThenable, then$$1);
} else {
fulfill(promise, maybeThenable);
}
}
}
function resolve(promise, value) {
if (promise === value) {
reject(promise, selfFulfillment());
} else if (objectOrFunction(value)) {
var then$$1 = void 0;
try {
then$$1 = value.then;
} catch (error) {
reject(promise, error);
return;
}
handleMaybeThenable(promise, value, then$$1);
} else {
fulfill(promise, value);
}
}
function publishRejection(promise) {
if (promise._onerror) {
promise._onerror(promise._result);
}
publish(promise);
}
function fulfill(promise, value) {
if (promise._state !== PENDING) {
return;
}
promise._result = value;
promise._state = FULFILLED;
if (promise._subscribers.length !== 0) {
asap(publish, promise);
}
}
function reject(promise, reason) {
if (promise._state !== PENDING) {
return;
}
promise._state = REJECTED;
promise._result = reason;
asap(publishRejection, promise);
}
function subscribe(parent, child, onFulfillment, onRejection) {
var _subscribers = parent._subscribers;
var length = _subscribers.length;
parent._onerror = null;
_subscribers[length] = child;
_subscribers[length + FULFILLED] = onFulfillment;
_subscribers[length + REJECTED] = onRejection;
if (length === 0 && parent._state) {
asap(publish, parent);
}
}
function publish(promise) {
var subscribers = promise._subscribers;
var settled = promise._state;
if (subscribers.length === 0) {
return;
}
var child = void 0,
callback = void 0,
detail = promise._result;
for (var i = 0; i < subscribers.length; i += 3) {
child = subscribers[i];
callback = subscribers[i + settled];
if (child) {
invokeCallback(settled, child, callback, detail);
} else {
callback(detail);
}
}
promise._subscribers.length = 0;
}
function invokeCallback(settled, promise, callback, detail) {
var hasCallback = isFunction(callback),
value = void 0,
error = void 0,
succeeded = true;
if (hasCallback) {
try {
value = callback(detail);
} catch (e) {
succeeded = false;
error = e;
}
if (promise === value) {
reject(promise, cannotReturnOwn());
return;
}
} else {
value = detail;
}
if (promise._state !== PENDING) {
// noop
} else if (hasCallback && succeeded) {
resolve(promise, value);
} else if (succeeded === false) {
reject(promise, error);
} else if (settled === FULFILLED) {
fulfill(promise, value);
} else if (settled === REJECTED) {
reject(promise, value);
}
}
function initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value) {
resolve(promise, value);
}, function rejectPromise(reason) {
reject(promise, reason);
});
} catch (e) {
reject(promise, e);
}
}
var id = 0;
function nextId() {
return id++;
}
function makePromise(promise) {
promise[PROMISE_ID] = id++;
promise._state = undefined;
promise._result = undefined;
promise._subscribers = [];
}
function validationError() {
return new Error('Array Methods must be provided an Array');
}
var Enumerator = function () {
function Enumerator(Constructor, input) {
this._instanceConstructor = Constructor;
this.promise = new Constructor(noop);
if (!this.promise[PROMISE_ID]) {
makePromise(this.promise);
}
if (isArray(input)) {
this.length = input.length;
this._remaining = input.length;
this._result = new Array(this.length);
if (this.length === 0) {
fulfill(this.promise, this._result);
} else {
this.length = this.length || 0;
this._enumerate(input);
if (this._remaining === 0) {
fulfill(this.promise, this._result);
}
}
} else {
reject(this.promise, validationError());
}
}
Enumerator.prototype._enumerate = function _enumerate(input) {
for (var i = 0; this._state === PENDING && i < input.length; i++) {
this._eachEntry(input[i], i);
}
};
Enumerator.prototype._eachEntry = function _eachEntry(entry, i) {
var c = this._instanceConstructor;
var resolve$$1 = c.resolve;
if (resolve$$1 === resolve$1) {
var _then = void 0;
var error = void 0;
var didError = false;
try {
_then = entry.then;
} catch (e) {
didError = true;
error = e;
}
if (_then === then && entry._state !== PENDING) {
this._settledAt(entry._state, i, entry._result);
} else if (typeof _then !== 'function') {
this._remaining--;
this._result[i] = entry;
} else if (c === Promise$1) {
var promise = new c(noop);
if (didError) {
reject(promise, error);
} else {
handleMaybeThenable(promise, entry, _then);
}
this._willSettleAt(promise, i);
} else {
this._willSettleAt(new c(function (resolve$$1) {
return resolve$$1(entry);
}), i);
}
} else {
this._willSettleAt(resolve$$1(entry), i);
}
};
Enumerator.prototype._settledAt = function _settledAt(state, i, value) {
var promise = this.promise;
if (promise._state === PENDING) {
this._remaining--;
if (state === REJECTED) {
reject(promise, value);
} else {
this._result[i] = value;
}
}
if (this._remaining === 0) {
fulfill(promise, this._result);
}
};
Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) {
var enumerator = this;
subscribe(promise, undefined, function (value) {
return enumerator._settledAt(FULFILLED, i, value);
}, function (reason) {
return enumerator._settledAt(REJECTED, i, reason);
});
};
return Enumerator;
}();
/**
`Promise.all` accepts an array of promises, and returns a new promise which
is fulfilled with an array of fulfillment values for the passed promises, or
rejected with the reason of the first passed promise to be rejected. It casts all
elements of the passed iterable to promises as it runs this algorithm.
Example:
```javascript
let promise1 = resolve(1);
let promise2 = resolve(2);
let promise3 = resolve(3);
let promises = [ promise1, promise2, promise3 ];
Promise.all(promises).then(function(array){
// The array here would be [ 1, 2, 3 ];
});
```
If any of the `promises` given to `all` are rejected, the first promise
that is rejected will be given as an argument to the returned promises's
rejection handler. For example:
Example:
```javascript
let promise1 = resolve(1);
let promise2 = reject(new Error("2"));
let promise3 = reject(new Error("3"));
let promises = [ promise1, promise2, promise3 ];
Promise.all(promises).then(function(array){
// Code here never runs because there are rejected promises!
}, function(error) {
// error.message === "2"
});
```
@method all
@static
@param {Array} entries array of promises
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise} promise that is fulfilled when all `promises` have been
fulfilled, or rejected if any of them become rejected.
@static
*/
function all(entries) {
return new Enumerator(this, entries).promise;
}
/**
`Promise.race` returns a new promise which is settled in the same way as the
first passed promise to settle.
Example:
```javascript
let promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 1');
}, 200);
});
let promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 2');
}, 100);
});
Promise.race([promise1, promise2]).then(function(result){
// result === 'promise 2' because it was resolved before promise1
// was resolved.
});
```
`Promise.race` is deterministic in that only the state of the first
settled promise matters. For example, even if other promises given to the
`promises` array argument are resolved, but the first settled promise has
become rejected before the other promises became fulfilled, the returned
promise will become rejected:
```javascript
let promise1 = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('promise 1');
}, 200);
});
let promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
reject(new Error('promise 2'));
}, 100);
});
Promise.race([promise1, promise2]).then(function(result){
// Code here never runs
}, function(reason){
// reason.message === 'promise 2' because promise 2 became rejected before
// promise 1 became fulfilled
});
```
An example real-world use case is implementing timeouts:
```javascript
Promise.race([ajax('foo.json'), timeout(5000)])
```
@method race
@static
@param {Array} promises array of promises to observe
Useful for tooling.
@return {Promise} a promise which settles in the same way as the first passed
promise to settle.
*/
function race(entries) {
/*jshint validthis:true */
var Constructor = this;
if (!isArray(entries)) {
return new Constructor(function (_, reject) {
return reject(new TypeError('You must pass an array to race.'));
});
} else {
return new Constructor(function (resolve, reject) {
var length = entries.length;
for (var i = 0; i < length; i++) {
Constructor.resolve(entries[i]).then(resolve, reject);
}
});
}
}
/**
`Promise.reject` returns a promise rejected with the passed `reason`.
It is shorthand for the following:
```javascript
let promise = new Promise(function(resolve, reject){
reject(new Error('WHOOPS'));
});
promise.then(function(value){
// Code here doesn't run because the promise is rejected!
}, function(reason){
// reason.message === 'WHOOPS'
});
```
Instead of writing the above, your code now simply becomes the following:
```javascript
let promise = Promise.reject(new Error('WHOOPS'));
promise.then(function(value){
// Code here doesn't run because the promise is rejected!
}, function(reason){
// reason.message === 'WHOOPS'
});
```
@method reject
@static
@param {Any} reason value that the returned promise will be rejected with.
Useful for tooling.
@return {Promise} a promise rejected with the given `reason`.
*/
function reject$1(reason) {
/*jshint validthis:true */
var Constructor = this;
var promise = new Constructor(noop);
reject(promise, reason);
return promise;
}
function needsResolver() {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
function needsNew() {
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}
/**
Promise objects represent the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promise's eventual value or the reason
why the promise cannot be fulfilled.
Terminology
-----------
- `promise` is an object or function with a `then` method whose behavior conforms to this specification.
- `thenable` is an object or function that defines a `then` method.
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
- `exception` is a value that is thrown using the throw statement.
- `reason` is a value that indicates why a promise was rejected.
- `settled` the final resting state of a promise, fulfilled or rejected.
A promise can be in one of three states: pending, fulfilled, or rejected.
Promises that are fulfilled have a fulfillment value and are in the fulfilled
state. Promises that are rejected have a rejection reason and are in the
rejected state. A fulfillment value is never a thenable.
Promises can also be said to *resolve* a value. If this value is also a
promise, then the original promise's settled state will match the value's
settled state. So a promise that *resolves* a promise that rejects will
itself reject, and a promise that *resolves* a promise that fulfills will
itself fulfill.
Basic Usage:
------------
```js
let promise = new Promise(function(resolve, reject) {
// on success
resolve(value);
// on failure
reject(reason);
});
promise.then(function(value) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Advanced Usage:
---------------
Promises shine when abstracting away asynchronous interactions such as
`XMLHttpRequest`s.
```js
function getJSON(url) {
return new Promise(function(resolve, reject){
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
}
}
};
});
}
getJSON('/posts.json').then(function(json) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Unlike callbacks, promises are great composable primitives.
```js
Promise.all([
getJSON('/posts'),
getJSON('/comments')
]).then(function(values){
values[0] // => postsJSON
values[1] // => commentsJSON
return values;
});
```
@class Promise
@param {Function} resolver
Useful for tooling.
@constructor
*/
var Promise$1 = function () {
function Promise(resolver) {
this[PROMISE_ID] = nextId();
this._result = this._state = undefined;
this._subscribers = [];
if (noop !== resolver) {
typeof resolver !== 'function' && needsResolver();
this instanceof Promise ? initializePromise(this, resolver) : needsNew();
}
}
/**
The primary way of interacting with a promise is through its `then` method,
which registers callbacks to receive either a promise's eventual value or the
reason why the promise cannot be fulfilled.
```js
findUser().then(function(user){
// user is available
}, function(reason){
// user is unavailable, and you are given the reason why
});
```
Chaining
--------
The return value of `then` is itself a promise. This second, 'downstream'
promise is resolved with the return value of the first promise's fulfillment
or rejection handler, or rejected if the handler throws an exception.
```js
findUser().then(function (user) {
return user.name;
}, function (reason) {
return 'default name';
}).then(function (userName) {
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
// will be `'default name'`
});
findUser().then(function (user) {
throw new Error('Found user, but still unhappy');
}, function (reason) {
throw new Error('`findUser` rejected and we're unhappy');
}).then(function (value) {
// never reached
}, function (reason) {
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
});
```
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
```js
findUser().then(function (user) {
throw new PedagogicalException('Upstream error');
}).then(function (value) {
// never reached
}).then(function (value) {
// never reached
}, function (reason) {
// The `PedgagocialException` is propagated all the way down to here
});
```
Assimilation
------------
Sometimes the value you want to propagate to a downstream promise can only be
retrieved asynchronously. This can be achieved by returning a promise in the
fulfillment or rejection handler. The downstream promise will then be pending
until the returned promise is settled. This is called *assimilation*.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// The user's comments are now available
});
```
If the assimliated promise rejects, then the downstream promise will also reject.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// If `findCommentsByAuthor` fulfills, we'll have the value here
}, function (reason) {
// If `findCommentsByAuthor` rejects, we'll have the reason here
});
```
Simple Example
--------------
Synchronous Example
```javascript
let result;
try {
result = findResult();
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
findResult(function(result, err){
if (err) {
// failure
} else {
// success
}
});
```
Promise Example;
```javascript
findResult().then(function(result){
// success
}, function(reason){
// failure
});
```
Advanced Example
--------------
Synchronous Example
```javascript
let author, books;
try {
author = findAuthor();
books = findBooksByAuthor(author);
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
function foundBooks(books) {
}
function failure(reason) {
}
findAuthor(function(author, err){
if (err) {
failure(err);
// failure
} else {
try {
findBoooksByAuthor(author, function(books, err) {
if (err) {
failure(err);
} else {
try {
foundBooks(books);
} catch(reason) {
failure(reason);
}
}
});
} catch(error) {
failure(err);
}
// success
}
});
```
Promise Example;
```javascript
findAuthor().
then(findBooksByAuthor).
then(function(books){
// found books
}).catch(function(reason){
// something went wrong
});
```
@method then
@param {Function} onFulfilled
@param {Function} onRejected
Useful for tooling.
@return {Promise}
*/
/**
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
as the catch block of a try/catch statement.
```js
function findAuthor(){
throw new Error('couldn't find that author');
}
// synchronous
try {
findAuthor();
} catch(reason) {
// something went wrong
}
// async with promises
findAuthor().catch(function(reason){
// something went wrong
});
```
@method catch
@param {Function} onRejection
Useful for tooling.
@return {Promise}
*/
Promise.prototype.catch = function _catch(onRejection) {
return this.then(null, onRejection);
};
/**
`finally` will be invoked regardless of the promise's fate just as native
try/catch/finally behaves
Synchronous example:
```js
findAuthor() {
if (Math.random() > 0.5) {
throw new Error();
}
return new Author();
}
try {
return findAuthor(); // succeed or fail
} catch(error) {
return findOtherAuther();
} finally {
// always runs
// doesn't affect the return value
}
```
Asynchronous example:
```js
findAuthor().catch(function(reason){
return findOtherAuther();
}).finally(function(){
// author was either found, or not
});
```
@method finally
@param {Function} callback
@return {Promise}
*/
Promise.prototype.finally = function _finally(callback) {
var promise = this;
var constructor = promise.constructor;
if (isFunction(callback)) {
return promise.then(function (value) {
return constructor.resolve(callback()).then(function () {
return value;
});
}, function (reason) {
return constructor.resolve(callback()).then(function () {
throw reason;
});
});
}
return promise.then(callback, callback);
};
return Promise;
}();
Promise$1.prototype.then = then;
Promise$1.all = all;
Promise$1.race = race;
Promise$1.resolve = resolve$1;
Promise$1.reject = reject$1;
Promise$1._setScheduler = setScheduler;
Promise$1._setAsap = setAsap;
Promise$1._asap = asap;
/*global self*/
function polyfill() {
var local = void 0;
if (typeof (typeof window !== undefined ? window : global) !== 'undefined') {
local = (typeof window !== undefined ? window : global);
} else if (typeof self !== 'undefined') {
local = self;
} else {
try {
local = Function('return this')();
} catch (e) {
throw new Error('polyfill failed because global object is unavailable in this environment');
}
}
var P = local.Promise;
if (P) {
var promiseToString = null;
try {
promiseToString = Object.prototype.toString.call(P.resolve());
} catch (e) {
// silently ignored
}
if (promiseToString === '[object Promise]' && !P.cast) {
return;
}
}
local.Promise = Promise$1;
}
// Strange compat..
Promise$1.polyfill = polyfill;
Promise$1.Promise = Promise$1;
return Promise$1;
})));
//# sourceMappingURL=es6-promise.map
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(67)))
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const debug = __webpack_require__(449);
const memoize = __webpack_require__(215);
const logLevels = __webpack_require__(127);
/**
* @typedef {Object} Logger
* @property {Function} error
* @property {Function} warn
* @property {Function} info
* @property {Function} log
* @property {Function} debug
* @property {Function} spam
*/
const createLogger = memoize(namespace => {
/** @type Logger */
const API = {};
const setMethods = () => {
Object.keys(logLevels).forEach(name => {
const method = logLevels[name].method;
const log = debug("OpenTok:" + namespace + ":" + name.toLowerCase());
log.log = console[method].bind(console);
API[name.toLowerCase()] = log;
});
};
setMethods();
return API;
});
createLogger.setLogLevel = level => {
let oldRules;
try {
oldRules = (typeof window !== undefined ? window : global).localStorage.debug;
} catch (err) {
// will get Uncaught DOMException: Failed to read the 'localStorage' property from 'Window':
// The document is sandboxed and lacks the 'allow-same-origin' flag.
oldRules = '';
}
const newDebugRules = Object.keys(logLevels).map(name => (level >= logLevels[name].priority ? '' : '-') + "(autogen)?OpenTok:*:" + name.toLowerCase());
const debugStr = [...newDebugRules, ...(oldRules || '').split(/[\s,]/).filter(rule => rule.indexOf('(autogen)?') === -1)].filter(x => x).join(',');
debug.enable(debugStr);
};
module.exports = createLogger;
/***/ }),
/* 2 */
/***/ (function(module, exports) {
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
module.exports = _interopRequireDefault;
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(process) {
/*
* Environment details
*
* Contains information about the current environment.
*
* @property name The name of the Environment (Chrome, FF, Node, etc)
* @property version Usually a Float, except in Node which uses a String
* @property userAgent The raw user agent
*
*/
const env = {
version: -1,
// @todo why is the default -1?
name: 'unknown'
};
if (process && process.versions && typeof process.versions.node === 'string') {
env.name = 'Node';
env.isNode = true;
env.version = parseInt(process.versions.node, 10);
env.userAgent = env.name + " " + env.version;
} else {
env.userAgent = (typeof window !== undefined ? window : global).navigator.userAgent.toLowerCase(); // @todo why lowercase?
env.appName = (typeof window !== undefined ? window : global).navigator.appName;
env.navigatorVendor = undefined;
env.name = 'unknown';
if (env.userAgent.indexOf('opera') > -1 || env.userAgent.indexOf('opr/') > -1) {
env.name = 'Opera';
env.isOpera = true;
if (/opr\/([0-9]{1,}[.0-9]{0,})/.exec(env.userAgent) !== null) {
env.version = parseFloat(RegExp.$1);
}
} else if (env.userAgent.indexOf('firefox') > -1) {
env.name = 'Firefox';
env.isFirefox = true;
if (/firefox\/([0-9]{1,}[.0-9]{0,})/.exec(env.userAgent) !== null) {
env.version = parseFloat(RegExp.$1);
}
} else if (env.appName === 'Microsoft Internet Explorer') {
// IE 10 and below
env.name = 'IE';
env.isIE = true;
if (/msie ([0-9]{1,}[.0-9]{0,})/.exec(env.userAgent) !== null) {
env.version = parseFloat(RegExp.$1);
}
} else if (env.userAgent.match(/edge?\/(\d+)(?:\.\d+)+$/)) {
env.name = 'Edge';
env.isEdge = true;
const version = RegExp.$1;
env.version = parseInt(version, 10);
} else if (env.appName === 'Netscape' && env.userAgent.indexOf('trident') > -1) {
// IE 11+
env.name = 'IE';
env.isIE = true;
if (/trident\/.*rv:([0-9]{1,}[.0-9]{0,})/.exec(env.userAgent) !== null) {
env.version = parseFloat(RegExp.$1);
}
} else if (env.userAgent.indexOf('chrome') > -1) {
env.name = 'Chrome';
env.isElectron = env.userAgent.indexOf('electron/') > -1;
if (!env.isElectron) {
env.isChrome = true;
}
if (/chrome\/([0-9]{1,}[.0-9]{0,})/.exec(env.userAgent) !== null) {
env.version = parseFloat(RegExp.$1);
}
} else if ((typeof window !== undefined ? window : global).navigator.vendor && (typeof window !== undefined ? window : global).navigator.vendor.toLowerCase().indexOf('apple') > -1) {
env.name = 'Safari';
env.isSafari = true;
if (/version\/([0-9]{1,}[.0-9]{0,})/.exec(env.userAgent) !== null) {
env.version = parseFloat(RegExp.$1);
}
} // The Chromium-based Edge was released on this version
env.isChromiumEdge = env.name === 'Edge' && env.version >= 79; // Prior versions ofo Edge versions were based on a proprietary, non-Chromium
// code base
env.isLegacyEdge = env.name === 'Edge' && env.version < 79;
env.userAgent = (typeof window !== undefined ? window : global).navigator.userAgent;
env.protocol = (typeof window !== undefined ? window : global).location.protocol;
env.hostName = (typeof window !== undefined ? window : global).location.hostName;
}
module.exports = env;
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(67)))
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* eslint-disable no-underscore-dangle */
// The top-level namespace, also performs basic DOMElement selecting.
//
// @example Get the DOM element with the id of 'domId'
// OTHelpers('#domId')
//
// @example Get all video elements
// OTHelpers('video')
//
// @example Get all elements with the class name of 'foo'
// OTHelpers('.foo')
//
// @example Get all elements with the class name of 'foo',
// and do something with the first.
// var collection = OTHelpers('.foo');
// console.log(collection.first);
//
//
// The second argument is the context, that is document or parent Element, to
// select from.
//
// @example Get a video element within the element with the id of 'domId'
// OTHelpers('video', OTHelpers('#domId'))
//
//
//
// OTHelpers will accept any of the following and return a collection:
// OTHelpers()
// OTHelpers('css selector', optionalParentNode)
// OTHelpers(DomNode)
// OTHelpers([array of DomNode])
//
// The collection is a ElementCollection object, see the ElementCollection docs for usage info.
//
const OTHelpers = __webpack_require__(221);
const setDeprecatedProperty = __webpack_require__(223);
const eventing = __webpack_require__(5);
const log = __webpack_require__(1);
OTHelpers.logging = log('OT.$');
OTHelpers.ElementCollection = __webpack_require__(222);
OTHelpers.env = __webpack_require__(3);
OTHelpers.ajax = __webpack_require__(236);
OTHelpers.statable = __webpack_require__(513);
OTHelpers.Analytics = __webpack_require__(238);
OTHelpers.Collection = __webpack_require__(241);
OTHelpers.domExtras = __webpack_require__(517);
OTHelpers.Error = __webpack_require__(242);
setDeprecatedProperty(OTHelpers, 'useLogHelpers', {
value(mixin) {
const logging = log('useLogHelpersDeprecatedMixin'); // eslint-disable-next-line no-param-reassign
Object.keys(logging).forEach(key => {
mixin[key] = logging[key];
});
return mixin;
},
name: 'OT.useLogHelpers',
getWarning: 'Please use an external library for your debugging needs'
});
OTHelpers.defaults = __webpack_require__(133);
setDeprecatedProperty(OTHelpers, 'eventing', {
value: eventing,
name: 'OT.$.eventing',
getWarning: 'Please use an external library to create an event emitter.'
}); // TODO: Remove the need for this kind of bazzadry.
Object.keys(OTHelpers).forEach(key => {
const component = OTHelpers[key];
const attachments = component._attachToOTHelpers || {};
Object.keys(attachments).forEach(attachmentName => {
if (Object.hasOwnProperty.call(OTHelpers, attachmentName) && !(key === 'util' && attachmentName === 'bind')) {
console.warn("More than one module is trying to define " + attachmentName + " on OTHelpers.");
}
OTHelpers[attachmentName] = attachments[attachmentName];
});
});
module.exports = OTHelpers;
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const Events = __webpack_require__(43);
const isFunction = __webpack_require__(13);
const logging = __webpack_require__(1)('Events');
const assign = __webpack_require__(6);
const weakMemoizeBind = __webpack_require__(463);
/**
* This base class defines the on
, once
, and off
* methods of objects that can dispatch events.
*
* @class EventDispatcher
*/
module.exports = function eventing(self) {
const ee = new Events();
ee.on('newListener', eventName => {
ee.emit(eventName + ":added");
});
ee.on('removeListener', eventName => {
ee.emit(eventName + ":removed");
});
const eventingMixin = {
/**
* Adds an event handler function for one or more events.
*
*
* The following code adds an event handler for one event: *
* ** obj.on("eventName", function (event) { * // This is the event handler. * }); ** *
If you pass in multiple event names and a handler method, the handler is * registered for each of those events:
* ** obj.on("eventName1 eventName2", * function (event) { * // This is the event handler. * }); ** *
You can also pass in a third context
parameter (which is optional) to
* define the value of this
in the handler method:
obj.on("eventName", * function (event) { * // This is the event handler. * }, * obj); ** *
* The method also supports an alternate syntax, in which the first parameter is an object * that is a hash map of event names and handler functions and the second parameter (optional) * is the context for this in each handler: *
** obj.on( * { * eventName1: function (event) { * // This is the handler for eventName1. * }, * eventName2: function (event) { * // This is the handler for eventName2. * } * }, * obj); ** *
* If you do not add a handler for an event, the event is ignored locally. *
* * @param {String} type The string identifying the type of event. You can specify multiple event * names in this string, separating them with a space. The event handler will process each of * the events. * @param {Function} handler The handler function to process the event. This function takes * the event object as a parameter. * @param {Object} context (Optional) Defines the value ofthis
in the event
* handler function.
*
* @returns {EventDispatcher} The EventDispatcher object.
*
* @memberOf EventDispatcher
* @method #on
* @see off()
* @see once()
* @see Events
*/
on(eventNames, handlerOrContext, context) {
if (typeof eventNames === 'string' && handlerOrContext) {
eventNames.split(' ').forEach(eventName => {
ee.on(eventName, weakMemoizeBind(handlerOrContext, context));
});
} else {
Object.keys(eventNames).forEach(eventName => {
ee.on(eventName, weakMemoizeBind(eventNames[eventName], handlerOrContext));
});
}
return this;
},
/**
* Removes an event handler or handlers.
*
* If you pass in one event name and a handler method, the handler is removed for that * event:
* *obj.off("eventName", eventHandler);* *
If you pass in multiple event names and a handler method, the handler is removed for * those events:
* *obj.off("eventName1 eventName2", eventHandler);* *
If you pass in an event name (or names) and no handler method, all handlers are * removed for those events:
* *obj.off("event1Name event2Name");* *
If you pass in no arguments, all event handlers are removed for all events * dispatched by the object:
* *obj.off();* *
* The method also supports an alternate syntax, in which the first parameter is an object that * is a hash map of event names and handler functions and the second parameter (optional) is * the context for this in each handler: *
** obj.off( * { * eventName1: event1Handler, * eventName2: event2Handler * }); ** * @param {String} type (Optional) The string identifying the type of event. You can * use a space to specify multiple events, as in "accessAllowed accessDenied * accessDialogClosed". If you pass in no
type
value (or other arguments),
* all event handlers are removed for the object.
* @param {Function} handler (Optional) The event handler function to remove. The handler
* must be the same function object as was passed into on()
. Be careful with
* helpers like bind()
that return a new function when called. If you pass in
* no handler
, all event handlers are removed for the specified event
* type
.
* @param {Object} context (Optional) If you specify a context
, the event handler
* is removed for all specified events and handlers that use the specified context. (The
* context must match the context passed into on()
.)
*
* @returns {Object} The object that dispatched the event.
*
* @memberOf EventDispatcher
* @method #off
* @see on()
* @see once()
* @see Events
*/
off(eventNames, handlerOrContext, context) {
if (typeof eventNames === 'string') {
this.off(eventNames.split(' '), handlerOrContext, context);
} else if (Array.isArray(eventNames)) {
eventNames.forEach(eventName => {
if (handlerOrContext && isFunction(handlerOrContext)) {
ee.removeListener(eventName, weakMemoizeBind(handlerOrContext, context));
} else {
ee.removeAllListeners(eventName);
}
});
} else if (!eventNames) {
ee.removeAllListeners();
} else {
Object.keys(eventNames).forEach(eventName => {
// @todo was {foo: undefined} expected to remove all foo events?
ee.removeListener(eventName, weakMemoizeBind(eventNames[eventName], handlerOrContext));
});
}
return this;
},
/**
* Adds an event handler function for one or more events. Once the handler is called,
* the specified handler method is removed as a handler for this event. (When you use
* the on()
method to add an event handler, the handler is not
* removed when it is called.) The once()
method is the equivilent of
* calling the on()
* method and calling off()
the first time the handler is invoked.
*
* * The following code adds a one-time event handler for one event: *
* ** obj.once("eventName", function (event) { * // This is the event handler. * }); ** *
If you pass in multiple event names and a handler method, the handler is registered * for each of those events:
* *obj.once("eventName1 eventName2" * function (event) { * // This is the event handler. * }); ** *
You can also pass in a third context
parameter (which is optional) to define
* the value of
* this
in the handler method:
obj.once("eventName", * function (event) { * // This is the event handler. * }, * obj); ** *
* The method also supports an alternate syntax, in which the first parameter is an object that * is a hash map of event names and handler functions and the second parameter (optional) is the * context for this in each handler: *
** obj.once( * { * eventName1: function (event) { * // This is the event handler for eventName1. * }, * eventName2: function (event) { * // This is the event handler for eventName1. * } * }, * obj); ** * @param {String} type The string identifying the type of event. You can specify multiple * event names in this string, separating them with a space. The event handler will process * the first occurence of the events. After the first event, the handler is removed (for * all specified events). * @param {Function} handler The handler function to process the event. This function takes * the event object as a parameter. * @param {Object} context (Optional) Defines the value of
this
in the event
* handler function.
*
* @returns {Object} The object that dispatched the event.
*
* @memberOf EventDispatcher
* @method #once
* @see on()
* @see off()
* @see Events
*/
once(eventNames, handlerOrContext, context) {
if (typeof eventNames === 'string' && handlerOrContext) {
eventNames.split(' ').forEach(eventName => {
ee.once(eventName, weakMemoizeBind(handlerOrContext, context));
});
} else {
Object.keys(eventNames).forEach(eventName => {
ee.once(eventName, weakMemoizeBind(eventNames[eventName], handlerOrContext));
});
}
return this;
},
/**
* Execute any listeners bound to the +event+ Event.
*
* Each handler will be executed.
*
* @param [Event] event An Event object.
* @return this
*/
dispatchEvent(event) {
if (!event.type) {
throw new Error('dispatchEvent: Event has no type');
}
if (!event.target) {
event.target = this; // eslint-disable-line no-param-reassign
}
this.trigger(event.type, event);
return this;
},
/**
* Execute each handler for the event called +name+.
*
* Each handler will be executed, and any exceptions that they throw will
* be caught and logged
*
* @example
* foo.on('bar', function(name, message) {
* alert("Hello " + name + ": " + message);
* });
*
* foo.trigger('OpenTok', 'asdf'); // -> Hello OpenTok: asdf
*
* @param {String} eventName The name of this event.
* @param {Array} arguments The arguments handlers will be called with
*
* @return this
*/
trigger(eventName) {
try {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
ee.emit(eventName, ...args);
} catch (e) {
console.error('An error occurred in an event handler', e);
}
return this;
},
// Alias of trigger for easier node compatibility
emit() {
return self.trigger(...arguments);
},
/**
* @deprecated use on() or once() instead.
* * This method registers a method as an event listener for a specific event. *
* *
* If a handler is not registered for an event, the event is ignored locally. If the * event listener function does not exist, the event is ignored locally. *
*
* Throws an exception if the listener
name is invalid.
*
this
in the event
* handler function.
*
* @memberOf EventDispatcher
* @method #addEventListener
* @see on()
* @see once()
* @see Events
*/
addEventListener(eventName, handler, context) {
logging.warn('The addEventListener() method is deprecated. Use on() or once() instead.');
return self.on(eventName, handler, context);
},
/**
* @deprecated use off() instead.
* * Removes an event listener for a specific event. *
* *
* Throws an exception if the listener
name is invalid.
*
context
, the event
* handler is removed for all specified events and event listeners that use the specified
context. (The context must match the context passed into
* addEventListener()
.)
*
* @memberOf EventDispatcher
* @method #removeEventListener
* @see off()
* @see Events
*/
// See 'off' for usage.
// @depreciated will become a private helper function in the future.
removeEventListener(eventName, handler, context) {
logging.warn('The removeEventListener() method is deprecated. Use off() instead.');
return self.off(eventName, handler, context);
},
listenerCount(eventName) {
return ee.listenerCount(eventName);
}
};
return assign(self, eventingMixin);
};
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
var assignValue = __webpack_require__(80),
copyObject = __webpack_require__(38),
createAssigner = __webpack_require__(224),
isArrayLike = __webpack_require__(21),
isPrototype = __webpack_require__(65),
keys = __webpack_require__(27);
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Assigns own enumerable string keyed properties of source objects to the
* destination object. Source objects are applied from left to right.
* Subsequent sources overwrite property assignments of previous sources.
*
* **Note:** This method mutates `object` and is loosely based on
* [`Object.assign`](https://mdn.io/Object/assign).
*
* @static
* @memberOf _
* @since 0.10.0
* @category Object
* @param {Object} object The destination object.
* @param {...Object} [sources] The source objects.
* @returns {Object} Returns `object`.
* @see _.assignIn
* @example
*
* function Foo() {
* this.a = 1;
* }
*
* function Bar() {
* this.c = 3;
* }
*
* Foo.prototype.b = 2;
* Bar.prototype.d = 4;
*
* _.assign({ 'a': 0 }, new Foo, new Bar);
* // => { 'a': 1, 'c': 3 }
*/
var assign = createAssigner(function(object, source) {
if (isPrototype(source) || isArrayLike(source)) {
copyObject(source, keys(source), object);
return;
}
for (var key in source) {
if (hasOwnProperty.call(source, key)) {
assignValue(object, key, source[key]);
}
}
});
module.exports = assign;
/***/ }),
/* 7 */
/***/ (function(module, exports) {
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(_.noop);
* // => true
*
* _.isObject(null);
* // => false
*/
function isObject(value) {
var type = typeof value;
return value != null && (type == 'object' || type == 'function');
}
module.exports = isObject;
/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// Note: Some of these descriptions here are more specific than the name would suggest, because we
// only use them in one place. However, this shouldn't prevent us from using the error elsewhere if
// the name fits, and we should update the description to be more general when this happens.
module.exports = {
// The API key does not match the token or session.
AUTHENTICATION_ERROR: 'OT_AUTHENTICATION_ERROR',
// JSON response was badly formed.
BADLY_FORMED_RESPONSE: 'OT_BADLY_FORMED_RESPONSE',
// Unable to publish because your browser failed to get access to your microphone. You may need to
// fully quit and restart your browser to get it to work.
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=4799 for more details.
CHROME_MICROPHONE_ACQUISITION_ERROR: 'OT_CHROME_MICROPHONE_ACQUISITION_ERROR',
// Failed to connect to session.
CONNECT_FAILED: 'OT_CONNECT_FAILED',
// The session has exceded the maximum number of simultaneous connections.
CONNECTION_LIMIT_EXCEEDED: 'OT_CONNECTION_LIMIT_EXCEEDED',
// The constraints for getting user media could not be satisfied.
CONSTRAINTS_NOT_SATISFIED: 'OT_CONSTRAINTS_NOT_SATISFIED',
// Failed to create peer connection.
CREATE_PEER_CONNECTION_FAILED: 'OT_CREATE_PEER_CONNECTION_FAILED',
// Action failed due to session disconnection.
DISCONNECTED: 'OT_DISCONNECTED',
// Received an unexpected empty response from the server.
EMPTY_RESPONSE_BODY: 'OT_EMPTY_RESPONSE_BODY',
// The hardware to fulfil the user media request appears to exist but could not be acquired. It
// might be in use by another application.
HARDWARE_UNAVAILABLE: 'OT_HARDWARE_UNAVAILABLE',
// Something went wrong while establishing WebRTC connectivity.
ICE_WORKFLOW_FAILED: 'OT_ICE_WORKFLOW_FAILED',
// Received an invalid HTTP status. This may be considered invalid for the endpoint only and not
// HTTP in general.
INVALID_HTTP_STATUS: 'OT_INVALID_HTTP_STATUS',
// One or more parameters was not valid or not provided.
INVALID_PARAMETER: 'OT_INVALID_PARAMETER',
// The session id was invalid.
INVALID_SESSION_ID: 'OT_INVALID_SESSION_ID',
// The fetching of the stream for the video element has been aborted.
MEDIA_ERR_ABORTED: 'OT_MEDIA_ERR_ABORTED',
// A decoding error occurred while trying to play the stream in the video element.
MEDIA_ERR_DECODE: 'OT_MEDIA_ERR_DECODE',
// A network error caused the stream to stop being fetched.
MEDIA_ERR_NETWORK: 'OT_MEDIA_ERR_NETWORK',
// The stream has been detected to be not suitable for playback.
MEDIA_ERR_SRC_NOT_SUPPORTED: 'OT_MEDIA_ERR_SRC_NOT_SUPPORTED',
// The ended event on the video element fired
MEDIA_ENDED: 'OT_MEDIA_ENDED',
// No devices were found to provide the media stream.
NO_DEVICES_FOUND: 'OT_NO_DEVICES_FOUND',
// Video and audio was disabled. You need to enable at least one.
NO_VALID_CONSTRAINTS: 'OT_NO_VALID_CONSTRAINTS',
// Couldn't perform action due to not being connected.
NOT_CONNECTED: 'OT_NOT_CONNECTED',
// A resource was not found.
NOT_FOUND: 'OT_NOT_FOUND',
// Something in the user media request is not supported.
NOT_SUPPORTED: 'OT_NOT_SUPPORTED',
// An action was not permitted.
PERMISSION_DENIED: 'OT_PERMISSION_DENIED',
// The signal could not be sent due to the rate limit.
RATE_LIMIT_EXCEEDED: 'OT_RATE_LIMIT_EXCEEDED',
// Error calling OT.reportIssue(). Check the client\'s network connection.
REPORT_ISSUE_FAILED: 'OT_REPORT_ISSUE_FAILED',
// Screen-sharing support in this browser requires an extension, but the extension is not
// installed.
SCREEN_SHARING_EXTENSION_NOT_INSTALLED: 'OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED',
// Screen-sharing support in this browser requires an extension, but one has not been registered.
SCREEN_SHARING_EXTENSION_NOT_REGISTERED: 'OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED',
// Screen sharing is not supported in the browser.
SCREEN_SHARING_NOT_SUPPORTED: 'OT_SCREEN_SHARING_NOT_SUPPORTED',
// The WebRTC connection failed during setDescription.
SET_REMOTE_DESCRIPTION_FAILED: 'OT_SET_REMOTE_DESCRIPTION_FAILED',
// Rumor.Socket cannot connect when it is already connecting or connected.
SOCKET_ALREADY_CONNECTED_CONNECTING: 'OT_SOCKET_ALREADY_CONNECTED_CONNECTING',
// A connection was closed abnormally (that is, with no close frame being sent) when a status code
// is expected.
SOCKET_CLOSE_ABNORMAL: 'OT_SOCKET_CLOSE_ABNORMAL',
// Exception was thrown during Rumor connection, possibly because of a blocked port.
SOCKET_CLOSE_CONNECT_EXCEPTION: 'OT_SOCKET_CLOSE_CONNECT_EXCEPTION',
// Connectivity loss was detected as it was too long since the socket received the last PONG
// message.
SOCKET_CLOSE_CONNECTIVITY_LOSS: 'OT_SOCKET_CLOSE_CONNECTIVITY_LOSS',
// Falling back on this error for socket close because a more suitable one was not found.
SOCKET_CLOSE_FALLBACK_CODE: 'OT_SOCKET_CLOSE_FALLBACK_CODE',
// An endpoint received data within a message that was not consistent with the type of the message
// (e.g., non-UTF-8 [RFC3629] data within a text message)
SOCKET_CLOSE_INCONSISTENT_DATA: 'OT_SOCKET_CLOSE_INCONSISTENT_DATA',
// No status code was provided even though one was expected.
SOCKET_CLOSE_NO_STATUS: 'OT_SOCKET_CLOSE_NO_STATUS',
// An endpoint received a message that violates its policy. This is a generic status code that
// can be returned when there is no other more suitable status code (e.g., 1003 or 1009) or if
// there is a need to hide specific details about the policy.
SOCKET_CLOSE_POLICY_VIOLATION: 'OT_SOCKET_CLOSE_POLICY_VIOLATION',
// The endpoint is going away, either because of a server failure or because the browser is
// navigating away from the page that opened the connection.
SOCKET_CLOSE_GOING_AWAY: 'OT_SOCKET_CLOSE_GOING_AWAY',
// A protocol error occurred.
SOCKET_CLOSE_PROTOCOL_ERROR: 'OT_SOCKET_CLOSE_PROTOCOL_ERROR',
// Timed out while waiting for the Rumor socket to connect.
SOCKET_CLOSE_TIMEOUT: 'OT_SOCKET_CLOSE_TIMEOUT',
// An endpoint received a message that is too big for it to process
SOCKET_CLOSE_TOO_LARGE: 'OT_SOCKET_CLOSE_TOO_LARGE',
// An endpoint encountered an unexpected condition that prevented it from fulfilling the request.
SOCKET_CLOSE_UNEXPECTED_CONDITION: 'OT_SOCKET_CLOSE_UNEXPECTED_CONDITION',
// An endpoint has indicated that reconnections are not available.
SOCKET_CLOSE_UNSUPPORTED: 'OT_SOCKET_CLOSE_UNSUPPORTED',
// Failed to create the stream in the server model.
STREAM_CREATE_FAILED: 'OT_STREAM_CREATE_FAILED',
// The stream was destroyed before it could be subscribed to.
STREAM_DESTROYED: 'OT_STREAM_DESTROYED',
// The session has exceded the maximum number of simultaneous streams.
STREAM_LIMIT_EXCEEDED: 'OT_STREAM_LIMIT_EXCEEDED',
// Couldn't subscribe to the stream because it was not found.
STREAM_NOT_FOUND: 'OT_STREAM_NOT_FOUND',
// Couldn't connect due to a terms of service violation.
TERMS_OF_SERVICE_FAILURE: 'OT_TERMS_OF_SERVICE_FAILURE',
// A timer expired while waiting for an action to occur.
TIMEOUT: 'OT_TIMEOUT',
// The operation was cancelled
CANCEL: 'OT_CANCEL',
// Generic media abort error
UNABLE_TO_CAPTURE_MEDIA: 'OT_UNABLE_TO_CAPTURE_MEDIA',
// Generic/ Unknown screen share error
UNABLE_TO_CAPTURE_SCREEN: 'OT_UNABLE_TO_CAPTURE_SCREEN',
// The error code received was unexpected.
UNEXPECTED_ERROR_CODE: 'OT_UNEXPECTED_ERROR_CODE',
// Received an unexpected HTTP status.
UNEXPECTED_HTTP_STATUS: 'OT_UNEXPECTED_HTTP_STATUS',
// An unexpected server response was received which could not be handled.
UNEXPECTED_SERVER_RESPONSE: 'OT_UNEXPECTED_SERVER_RESPONSE',
// An unknown anvil error occurred.
UNKNOWN_HTTP_ERROR: 'OT_UNKNOWN_HTTP_ERROR',
UNSUPPORTED_BROWSER: 'OT_UNSUPPORTED_BROWSER',
// Access to user media was denied.
USER_MEDIA_ACCESS_DENIED: 'OT_USER_MEDIA_ACCESS_DENIED',
// There was a cross domain error or the server responded with invalid JSON.
XDOMAIN_OR_PARSING_ERROR: 'OT_XDOMAIN_OR_PARSING_ERROR',
API_KEY_DISABLED: 'OT_API_KEY_DISABLED',
// There was an error when trying to get the list of supported codecs.
UNABLE_TO_ACCESS_MEDIA_ENGINE: 'OT_UNABLE_TO_ACCESS_MEDIA_ENGINE'
};
/***/ }),
/* 9 */
/***/ (function(module, exports) {
/**
* Checks if `value` is classified as an `Array` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
* _.isArray([1, 2, 3]);
* // => true
*
* _.isArray(document.body.children);
* // => false
*
* _.isArray('abc');
* // => false
*
* _.isArray(_.noop);
* // => false
*/
var isArray = Array.isArray;
module.exports = isArray;
/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {
var freeGlobal = __webpack_require__(168);
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
module.exports = root;
/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = {
JS_EXCEPTION: 2000,
AUTHENTICATION_ERROR: 1004,
INVALID_SESSION_ID: 1005,
CONNECT_FAILED: 1006,
CONNECT_REJECTED: 1007,
CONNECTION_TIMEOUT: 1008,
NOT_CONNECTED: 1010,
INVALID_PARAMETER: 1011,
P2P_CONNECTION_FAILED: 1013,
API_RESPONSE_FAILURE: 1014,
TERMS_OF_SERVICE_FAILURE: 1026,
CONNECTION_LIMIT_EXCEEDED: 1027,
UNABLE_TO_PUBLISH: 1500,
UNABLE_TO_SUBSCRIBE: 1501,
UNSUPPORTED_VIDEO_CODEC: 1502,
UNABLE_TO_FORCE_DISCONNECT: 1520,
UNABLE_TO_FORCE_UNPUBLISH: 1530,
PUBLISHER_ICE_WORKFLOW_FAILED: 1553,
SUBSCRIBER_ICE_WORKFLOW_FAILED: 1554,
STREAM_LIMIT_EXCEEDED: 1605,
UNEXPECTED_SERVER_RESPONSE: 2001,
REPORT_ISSUE_ERROR: 2011,
ANVIL_BADLY_FORMED_RESPONSE: 3001,
ANVIL_INVALID_HTTP_STATUS: 3002,
ANVIL_XDOMAIN_OR_PARSING_ERROR: 3003,
ANVIL_UNKNOWN_HTTP_ERROR: 3004,
ANVIL_UNEXPECTED_ERROR_CODE: 3005,
ANVIL_EMPTY_RESPONSE_BODY: 3006,
ANVIL_CONNECT_FAILED: 3007,
UNABLE_TO_ACCESS_MEDIA_ENGINE: 5001
};
/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-param-reassign, global-require */
module.exports = function otErrorFactory(deps) {
if (deps === void 0) {
deps = {};
}
const errorReporting = deps.errorReporting || __webpack_require__(185)();
const Errors = deps.Errors || __webpack_require__(8);
const OTErrorClass = deps.OTErrorClass || __webpack_require__(33);
const names = Object.keys(Errors).map(shortName => Errors[shortName]);
return function otError(name, plainError, code) {
if (names.indexOf(name) === -1) {
return new Error("Attempt to use invalid error name (" + name + "). Original message: " + plainError.message);
}
if (!(plainError instanceof Error || /^\[object .*Error\]$/.test(Object.prototype.toString.call(plainError)))) {
return new Error("Did not pass Error as second argument: " + plainError);
} // OTErrorClass is the deprecated OT.Error class from the docs.
const error = new OTErrorClass(undefined, plainError.message);
error.name = name;
if (!plainError.stack) {
try {
throw plainError;
} catch (e) {// IE populates the error's .stack when it is thrown, nothing to do here
}
}
error.stack = plainError.stack;
if (code) {
error.code = code;
}
errorReporting.send(error);
return error;
};
};
/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {
var baseGetTag = __webpack_require__(26),
isObject = __webpack_require__(7);
/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]',
funcTag = '[object Function]',
genTag = '[object GeneratorFunction]',
proxyTag = '[object Proxy]';
/**
* Checks if `value` is classified as a `Function` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a function, else `false`.
* @example
*
* _.isFunction(_);
* // => true
*
* _.isFunction(/abc/);
* // => false
*/
function isFunction(value) {
if (!isObject(value)) {
return false;
}
// The use of `Object#toString` avoids issues with the `typeof` operator
// in Safari 9 which returns 'object' for typed arrays and other constructors.
var tag = baseGetTag(value);
return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}
module.exports = isFunction;
/***/ }),
/* 14 */
/***/ (function(module, exports) {
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
function isObjectLike(value) {
return value != null && typeof value == 'object';
}
module.exports = isObjectLike;
/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {
var v1 = __webpack_require__(515);
var v4 = __webpack_require__(516);
var uuid = v4;
uuid.v1 = v1;
uuid.v4 = v4;
module.exports = uuid;
/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var bind = __webpack_require__(187);
var isBuffer = __webpack_require__(378);
/*global toString:true*/
// utils is a library of generic helper functions non-specific to axios
var toString = Object.prototype.toString;
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
function isArray(val) {
return toString.call(val) === '[object Array]';
}
/**
* Determine if a value is an ArrayBuffer
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an ArrayBuffer, otherwise false
*/
function isArrayBuffer(val) {
return toString.call(val) === '[object ArrayBuffer]';
}
/**
* Determine if a value is a FormData
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an FormData, otherwise false
*/
function isFormData(val) {
return (typeof FormData !== 'undefined') && (val instanceof FormData);
}
/**
* Determine if a value is a view on an ArrayBuffer
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
*/
function isArrayBufferView(val) {
var result;
if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
result = ArrayBuffer.isView(val);
} else {
result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);
}
return result;
}
/**
* Determine if a value is a String
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a String, otherwise false
*/
function isString(val) {
return typeof val === 'string';
}
/**
* Determine if a value is a Number
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Number, otherwise false
*/
function isNumber(val) {
return typeof val === 'number';
}
/**
* Determine if a value is undefined
*
* @param {Object} val The value to test
* @returns {boolean} True if the value is undefined, otherwise false
*/
function isUndefined(val) {
return typeof val === 'undefined';
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
function isObject(val) {
return val !== null && typeof val === 'object';
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
function isDate(val) {
return toString.call(val) === '[object Date]';
}
/**
* Determine if a value is a File
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a File, otherwise false
*/
function isFile(val) {
return toString.call(val) === '[object File]';
}
/**
* Determine if a value is a Blob
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Blob, otherwise false
*/
function isBlob(val) {
return toString.call(val) === '[object Blob]';
}
/**
* Determine if a value is a Function
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Function, otherwise false
*/
function isFunction(val) {
return toString.call(val) === '[object Function]';
}
/**
* Determine if a value is a Stream
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Stream, otherwise false
*/
function isStream(val) {
return isObject(val) && isFunction(val.pipe);
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
function isURLSearchParams(val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;
}
/**
* Trim excess whitespace off the beginning and end of a string
*
* @param {String} str The String to trim
* @returns {String} The String freed of excess whitespace
*/
function trim(str) {
return str.replace(/^\s*/, '').replace(/\s*$/, '');
}
/**
* Determine if we're running in a standard browser environment
*
* This allows axios to run in a web worker, and react-native.
* Both environments support XMLHttpRequest, but not fully standard globals.
*
* web workers:
* typeof window -> undefined
* typeof document -> undefined
*
* react-native:
* navigator.product -> 'ReactNative'
*/
function isStandardBrowserEnv() {
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
return false;
}
return (
typeof window !== 'undefined' &&
typeof document !== 'undefined'
);
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
function forEach(obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return;
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj];
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj);
}
} else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj);
}
}
}
}
/**
* Accepts varargs expecting each argument to be an object, then
* immutably merges the properties of each object and returns result.
*
* When multiple objects contain the same key the later object in
* the arguments list will take precedence.
*
* Example:
*
* ```js
* var result = merge({foo: 123}, {foo: 456});
* console.log(result.foo); // outputs 456
* ```
*
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
function merge(/* obj1, obj2, obj3, ... */) {
var result = {};
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = merge(result[key], val);
} else {
result[key] = val;
}
}
for (var i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue);
}
return result;
}
/**
* Extends object a by mutably adding to it the properties of object b.
*
* @param {Object} a The object to be extended
* @param {Object} b The object to copy properties from
* @param {Object} thisArg The object to bind function to
* @return {Object} The resulting value of object a
*/
function extend(a, b, thisArg) {
forEach(b, function assignValue(val, key) {
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
module.exports = {
isArray: isArray,
isArrayBuffer: isArrayBuffer,
isBuffer: isBuffer,
isFormData: isFormData,
isArrayBufferView: isArrayBufferView,
isString: isString,
isNumber: isNumber,
isObject: isObject,
isUndefined: isUndefined,
isDate: isDate,
isFile: isFile,
isBlob: isBlob,
isFunction: isFunction,
isStream: isStream,
isURLSearchParams: isURLSearchParams,
isStandardBrowserEnv: isStandardBrowserEnv,
forEach: forEach,
merge: merge,
extend: extend,
trim: trim
};
/***/ }),
/* 17 */
/***/ (function(module, exports) {
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
module.exports = _defineProperty;
/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(583);
/***/ }),
/* 19 */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(Promise) {function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
module.exports = _asyncToGenerator;
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 20 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__(2);
var _createClass2 = _interopRequireDefault(__webpack_require__(40));
var _inheritsLoose2 = _interopRequireDefault(__webpack_require__(55));
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-param-reassign, global-require, max-len */
const Event = __webpack_require__(135);
const eventNames = __webpack_require__(24);
module.exports = function EventsFactory(deps) {
if (deps === void 0) {
deps = {};
}
const logging = deps.logging || __webpack_require__(1)('Events');
const Events = {};
/**
* The Event object defines the basic OpenTok event object that is passed to
* event listeners. Other OpenTok event classes implement the properties and methods of
* the Event object.
*
* For example, the Stream object dispatches a streamPropertyChanged
event when
* the stream's properties are updated. You add a callback for an event using the
* on()
method of the Stream object:
* stream.on("streamPropertyChanged", function (event) { * alert("Properties changed for stream " + event.target.streamId); * });* * @class Event * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable * (
true
) or not (false
). You can cancel the default behavior by
* calling the preventDefault()
method of the Event object in the callback
* function. (See preventDefault().)
*
* @property {Object} target The object that dispatched the event.
*
* @property {String} type The type of event.
*/
/**
* Prevents the default behavior associated with the event from taking place.
*
* To see whether an event has a default behavior, check the cancelable
property
* of the event object.
Call the preventDefault()
method in the callback function for the event.
The following events have default behaviors:
* *sessionDisconnect
See
*
* SessionDisconnectEvent.preventDefault().streamDestroyed
See
* StreamEvent.preventDefault().accessDialogOpened
See the
* accessDialogOpened event.accessDenied
See the
* accessDenied event.preventDefault()
(true
) or not (false
).
* See preventDefault().
* @method #isDefaultPrevented
* @return {Boolean}
* @memberof Event
*/
/**
* The {@link OT} class dispatches exception
events when the OpenTok API encounters
* an exception (error). The ExceptionEvent object defines the properties of the event
* object that is dispatched.
*
* Note that you set up a callback for the exception
event by calling the
* OT.on()
method.
* code * * | ** title * | *
* 1004 * * | ** Authentication error * | *
* 1005 * * | ** Invalid Session ID * | *
* 1006 * * | ** Connect Failed * | *
* 1007 * * | ** Connect Rejected * | *
* 1008 * * | ** Connect Time-out * | *
* 1009 * * | ** Security Error * | *
* 1010 * * | ** Not Connected * | *
* 1011 * * | ** Invalid Parameter * | *
* 1013 * | ** Connection Failed * | *
* 1014 * | ** API Response Failure * | *
* 1026 * | ** Terms of Service Violation: Export Compliance * | *
* 1500 * | ** Unable to Publish * | *
* 1520 * | ** Unable to Force Disconnect * | *
* 1530 * | ** Unable to Force Unpublish * | *
* 1535 * | ** Force Unpublish on Invalid Stream * | *
* 2000 * * | ** Internal Error * | *
* 2010 * * | ** Report Issue Failure * | *
Check the message
property for more details about the error.
exception
event, this will be an object other than the OT object
* (such as a Session object or a Publisher object).
*
* @property {String} title The error title.
* @augments Event
*/
Events.ExceptionEvent = function ExceptionEvent(type, message, title, code, component, target, error) {
return new Event(type, false, {
error,
message,
title,
code,
component,
target
});
};
Events.IssueReportedEvent = function IssueReportedEvent(type, issueId) {
return new Event(type, false, {
issueId
});
}; // Triggered when the JS dynamic config and the DOM have loaded.
Events.EnvLoadedEvent = function EnvLoadedEvent(type) {
return new Event(type, false);
};
/**
* Defines connectionCreated
and connectionDestroyed
events dispatched
* by the {@link Session} object.
*
* The Session object dispatches a connectionCreated
event when a client (including
* your own) connects to a Session. It also dispatches a connectionCreated
event for
* every client in the session when you first connect. (when your local client connects, the
* Session object also dispatches a sessionConnected
event, defined by the
* {@link SessionConnectEvent} class.)
*
* While you are connected to the session, the Session object dispatches a
* connectionDestroyed
event when another client disconnects from the Session.
* (When you disconnect, the Session object also dispatches a sessionDisconnected
* event, defined by the {@link SessionDisconnectEvent} class.)
*
*
The following code keeps a running total of the number of connections to a session
* by monitoring the connections
property of the sessionConnect
,
* connectionCreated
and connectionDestroyed
events:
var apiKey = ""; // Replace with your API key. See https://tokbox.com/account * var sessionID = ""; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/. * var token = ""; // Replace with a generated token that has been assigned the moderator role. * // See https://tokbox.com/developer/guides/create-token/. * var connectionCount = 0; * * var session = OT.initSession(apiKey, sessionID); * session.on("connectionCreated", function(event) { * connectionCount++; * displayConnectionCount(); * }); * session.on("connectionDestroyed", function(event) { * connectionCount--; * displayConnectionCount(); * }); * session.connect(token); * * function displayConnectionCount() { * document.getElementById("connectionCountField").value = connectionCount.toString(); * }* *
This example assumes that there is an input text field in the HTML DOM
* with the id
set to "connectionCountField"
:
<input type="text" id="connectionCountField" value="0"></input>* * * @property {Connection} connection A Connection object for the connection that was * created or deleted. * * @property {Array} connections Deprecated. Use the
connection
property. A
* connectionCreated
or connectionDestroyed
event is dispatched
* for each connection created and destroyed in the session.
*
* @property {String} reason For a connectionDestroyed
event,
* a description of why the connection ended. This property can have the following values:
*
* "clientDisconnected"
A client disconnected from the session by calling
* the disconnect()
method of the Session object or by closing the browser.
* (See Session.disconnect().)"forceDisconnected"
A moderator has disconnected the publisher
* from the session, by calling the forceDisconnect()
method of the Session
* object. (See Session.forceDisconnect().)"networkDisconnected"
The network connection terminated abruptly
* (for example, the client lost their internet connection).Depending on the context, this description may allow the developer to refine * the course of action they take in response to an event.
* *For a connectionCreated
event, this string is undefined.
The following code initializes a session and sets up an event listener for when * a stream published by another client is created:
* ** session.on("streamCreated", function(event) { * // streamContainer is a DOM element * subscriber = session.subscribe(event.stream, targetElement); * }).connect(token); ** *
The following code initializes a session and sets up an event listener for when * other clients' streams end:
* ** session.on("streamDestroyed", function(event) { * console.log("Stream " + event.stream.name + " ended. " + event.reason); * }).connect(token); ** *
The following code publishes a stream and adds an event listener for when the streaming * starts
* ** var publisher = session.publish(targetElement) * .on("streamCreated", function(event) { * console.log("Publisher started streaming."); * ); ** *
The following code publishes a stream, and leaves the Publisher in the HTML DOM * when the streaming stops:
* ** var publisher = session.publish(targetElement) * .on("streamDestroyed", function(event) { * event.preventDefault(); * console.log("Publisher stopped streaming."); * ); ** * @class StreamEvent * * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable * (
true
) or not (false
). You can cancel the default behavior by
* calling the preventDefault()
method of the StreamEvent object in the event
* listener function. The streamDestroyed
event is cancelable.
* (See preventDefault().)
*
* @property {String} reason For a streamDestroyed
event,
* a description of why the session disconnected. This property can have one of the following
* values:
*
* "clientDisconnected"
A client disconnected from the session by calling
* the disconnect()
method of the Session object or by closing the browser.
* (See Session.disconnect().)"forceDisconnected"
A moderator has disconnected the publisher of the
* stream from the session, by calling the forceDisconnect()
method of the Session
* object. (See Session.forceDisconnect().)"forceUnpublished"
A moderator has forced the publisher of the stream
* to stop publishing the stream, by calling the forceUnpublish()
method of the
* Session object.
* (See Session.forceUnpublish().)"mediaStopped"
The user publishing the stream has stopped sharing
* media. For example, the user closed the window that is a source for a screen-sharing
* stream. Or the user disconnected a microphone that was the audio source for an audio-only
* stream. Or the video and audio sources are MediaStreamTrack elements and the sources of
* the media are stopped or destroyed."networkDisconnected"
The network connection terminated abruptly (for
* example, the client lost their internet connection).Depending on the context, this description may allow the developer to refine * the course of action they take in response to an event.
* *For a streamCreated
event, this string is undefined.
streamCreated
event) or deleted (in the case of a
* streamDestroyed
event).
*
* @property {Array} streams Deprecated. Use the stream
property. A
* streamCreated
or streamDestroyed
event is dispatched for
* each stream added or destroyed.
*
* @augments Event
*/
let streamEventPluralDeprecationWarningShown = false;
Events.StreamEvent = /*#__PURE__*/function (_Event2) {
(0, _inheritsLoose2.default)(StreamEvent, _Event2);
function StreamEvent(type, stream, reason, cancelable) {
return _Event2.call(this, type, cancelable, {
stream,
reason
}) || this;
}
(0, _createClass2.default)(StreamEvent, [{
key: "streams",
get: function get() {
if (!streamEventPluralDeprecationWarningShown) {
logging.warn('OT.StreamEvent streams property is deprecated, use stream instead.');
streamEventPluralDeprecationWarningShown = true;
}
return [this.stream];
}
}]);
return StreamEvent;
}(Event);
/**
* Prevents the default behavior associated with the event from taking place.
*
* For the streamDestroyed
event dispatched by the Session object,
* the default behavior is that all Subscriber objects that are subscribed to the stream are
* unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a
* destroyed
event when the element is removed from the HTML DOM. If you call the
* preventDefault()
method in the event listener for the streamDestroyed
* event, the default behavior is prevented and you can clean up Subscriber objects using your
* own code. See
* Session.getSubscribersForStream().
* For the streamDestroyed
event dispatched by a Publisher object, the default
* behavior is that the Publisher object is removed from the HTML DOM. The Publisher object
* dispatches a destroyed
event when the element is removed from the HTML DOM.
* If you call the preventDefault()
method in the event listener for the
* streamDestroyed
event, the default behavior is prevented, and you can
* retain the Publisher for reuse or clean it up using your own code.
*
To see whether an event has a default behavior, check the cancelable
property of
* the event object.
* Call the preventDefault()
method in the event listener function for the event.
*
connect()
method of the Session object.
*
* In version 2.2, the completionHandler of the Session.connect()
method
* indicates success or failure in connecting to the session.
*
* @class SessionConnectEvent
* @property {Array} connections Deprecated in version 2.2 (and set to an empty array). In
* version 2.2, listen for the connectionCreated
event dispatched by the Session
* object. In version 2.2, the Session object dispatches a connectionCreated
event
* for each connection (including your own). This includes connections present when you first
* connect to the session.
*
* @property {Array} streams Deprecated in version 2.2 (and set to an empty array). In version
* 2.2, listen for the streamCreated
event dispatched by the Session object. In
* version 2.2, the Session object dispatches a streamCreated
event for each stream
* other than those published by your client. This includes streams
* present when you first connect to the session.
*
* @see Session.connect()
disconnect()
method of the session object.
*
* * The following code initializes a session and sets up an event listener for when a session is * disconnected. *
*var apiKey = ""; // Replace with your API key. See https://tokbox.com/account * var sessionID = ""; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/ * var token = ""; // Replace with a generated token that has been assigned the moderator role. * // See https://tokbox.com/developer/guides/create-token/ * * var session = OT.initSession(apiKey, sessionID); * session.on("sessionDisconnected", function(event) { * alert("The session disconnected. " + event.reason); * }); * session.connect(token); ** * @property {String} reason A description of why the session disconnected. * This property can have the following values: * *
"clientDisconnected"
— A client disconnected from the
* session by calling the disconnect()
method of the Session
* object or by closing the browser. ( See Session.disconnect().)
* "forceDisconnected"
— A moderator has disconnected you from
* the session by calling the forceDisconnect()
method of the
* Session object. (See Session.forceDisconnect().)
* "networkDisconnected"
— The network connection terminated
* abruptly (for example, the client lost its internet connection).
*
* Prior to dispatching a
* sessionDisconnected
event for this reason, the Session object dispatches a
* reconnecting
event, and the client attempts to reconnect to the OpenTok session.
* If the reconnection fails, the Session object dispatches a sessionDisconnected
* event with the reason
property set to "networkDisconnected"
.
*
* For the sessionDisconnectEvent
, the default behavior is that all
* Subscriber objects are unsubscribed and removed from the HTML DOM. Each
* Subscriber object dispatches a destroyed
event when the element
* is removed from the HTML DOM. If you call the preventDefault()
* method in the event listener for the sessionDisconnect
event,
* the default behavior is prevented, and you can, optionally, clean up
* Subscriber objects using your own code).
*
* To see whether an event has a default behavior, check the
* cancelable
property of the event object.
*
* Call the preventDefault()
method in the event listener function
* for the event.
*
streamPropertyChanged
event in the
* following circumstances:
*
* publishAudio()
or
* publishVideo()
methods of the Publish object. Note that a
* subscriber's video can be disabled or enabled for reasons other than the
* publisher disabling or enabling it. A Subscriber object dispatches
* videoDisabled
and videoEnabled
events in all
* conditions that cause the subscriber's stream to be disabled or enabled.
* videoDimensions
property of the Stream object has
* changed (see Stream.videoDimensions).
* videoType
property of the Stream object has changed.
* This can happen in a stream published by a mobile device. (See
* Stream.videoType.)
* "hasAudio"
, "hasVideo"
, or "videoDimensions"
.
* @property {Object} newValue The new value of the property (after the change).
* @property {Object} oldValue The old value of the property (before the change).
* @property {Stream} stream The Stream object for which a property has changed.
*
* @see Publisher.publishAudio()
* @see Publisher.publishVideo()
* @see Stream.videoDimensions
* @augments Event
*/
Events.StreamPropertyChangedEvent = function StreamPropertyChangedEvent(type, stream, changedProperty, oldValue, newValue) {
return new Event(type, false, {
stream,
changedProperty,
oldValue,
newValue
});
};
/**
* Dispatched when the video dimensions of the video change for a screen-sharing
* video stream (when the user resizes the window being captured).
*
* @class VideoDimensionsChangedEvent
* @property {Object} newValue The new video dimensions (after the change). This object has two
* properties: height
(the height, in pixels) and width
(the width,
* in pixels).
* @property {Object} oldValue The old video dimensions (before the change). This object has two
* properties: height
(the old height, in pixels) and width
(the old
* width, in pixels).
*
* @see Publisher videoDimensionsChanged
* event
* @see Subscriber videoDimensionsChanged
* event
* @augments Event
*/
Events.VideoDimensionsChangedEvent = function VideoDimensionsChangedEvent(target, oldValue, newValue) {
return new Event('videoDimensionsChanged', false, {
target,
oldValue,
newValue
});
};
/**
* Defines event objects for the archiveStarted
and archiveStopped
* events. The Session object dispatches these events when an archive recording of the session
* starts and stops.
*
* @property {String} id The archive ID.
* @property {String} name The name of the archive. You can assign an archive a name when you
* create it, using the OpenTok REST API or one
* of the OpenTok server SDKs.
*
* @class ArchiveEvent
* @augments Event
*/
Events.ArchiveEvent = function ArchiveEvent(type, archive) {
return new Event(type, false, {
id: archive.id,
name: archive.name,
status: archive.status,
archive
});
};
Events.ArchiveUpdatedEvent = function ArchiveUpdatedEvent(stream, key, oldValue, newValue) {
return new Event('updated', false, {
target: stream,
changedProperty: key,
oldValue,
newValue
});
};
/**
* The Session object dispatches a signal event when the client receives a signal from the
* session.
*
* @class SignalEvent
* @property {String} type The type assigned to the signal (if there is one). Use the type to
* filter signals received (by adding an event handler for signal:type1 or signal:type2, etc.)
* @property {String} data The data string sent with the signal (if there is one).
* @property {Connection} from The Connection corresponding to the client that sent the
* signal.
*
* @see Session.signal()
* @see Session events (signal and signal:type)
* @augments Event
*/
Events.SignalEvent = function SignalEvent(type, data, from) {
return new Event(type ? "signal:" + type : eventNames.SIGNAL, false, {
data,
from
});
};
Events.StreamUpdatedEvent = function StreamUpdatedEvent(stream, key, oldValue, newValue) {
return new Event('updated', false, {
target: stream,
changedProperty: key,
oldValue,
newValue
});
};
Events.DestroyedEvent = function DestroyedEvent(type, target, reason) {
return new Event(type, false, {
target,
reason
});
};
Events.ConnectionStateChangedEvent = function ConnectionStateChangedEvent(type, target) {
return new Event(type, false, {
target
});
};
/**
* Defines the event object for the videoDisabled
and videoEnabled
* events dispatched by the Subscriber.
*
* @class VideoEnabledChangedEvent
*
* @property {Boolean} cancelable Whether the event has a default behavior that is cancelable
* (true
) or not (false
). You can cancel the default behavior by
* calling the preventDefault()
method of the event object in the callback
* function. (See preventDefault().)
*
* @property {String} reason The reason the video was disabled or enabled. This can be set to one
* of the following values:
*
* "publishVideo"
— The publisher started or stopped publishing video,
* by calling publishVideo(true)
or publishVideo(false)
."quality"
— The OpenTok Media Router starts or stops sending video
* to the subscriber based on stream quality changes. This feature of the OpenTok Media
* Router has a subscriber drop the video stream when connectivity degrades. (The subscriber
* continues to receive the audio stream, if there is one.)
*
* If connectivity improves to support video again, the Subscriber object dispatches
* a videoEnabled
event, and the Subscriber resumes receiving video.
*
* By default, the Subscriber displays a video disabled indicator when a
* videoDisabled
event with this reason is dispatched and removes the indicator
* when the videoEnabled
event with this reason is dispatched. You can control
* the display of this icon by calling the setStyle()
method of the Subscriber,
* setting the videoDisabledDisplayMode
property(or you can set the style when
* calling the Session.subscribe()
method, setting the style
property
* of the properties
parameter).
*
* This feature is only available in sessions that use the OpenTok Media Router (sessions with * the media mode * set to routed), not in sessions with the media mode set to relayed. *
"subscribeToVideo"
— The subscriber started or stopped subscribing to
* video, by calling subscribeToVideo(true)
or
* subscribeToVideo(false)
."codecNotSupported"
— The subscriber stopped subscribing to video due
* to an incompatible codec."codecChanged"
— The subscriber video was enabled after a codec change
* from an incompatible codec."videoDisabled"
or
* "videoEnabled"
.
*
* @see Subscriber videoDisabled event
* @see Subscriber videoEnabled event
* @augments Event
*/
Events.VideoEnabledChangedEvent = function VideoEnabledChangedEvent(type, properties) {
return new Event(type, false, {
reason: properties.reason
});
};
Events.VideoDisableWarningEvent = function VideoDisableWarningEvent(type
/* , properties */
) {
return new Event(type, false);
};
/**
* Dispatched periodically by a Subscriber or Publisher object to indicate the audio
* level. This event is dispatched up to 60 times per second, depending on the browser.
*
* @property {Number} audioLevel The audio level, from 0 to 1.0. Adjust this value logarithmically
* for use in adjusting a user interface element, such as a volume meter. Use a moving average
* to smooth the data.
*
* @class AudioLevelUpdatedEvent
* @augments Event
*/
Events.AudioLevelUpdatedEvent = function AudioLevelUpdatedEvent(audioLevel) {
return new Event(eventNames.AUDIO_LEVEL_UPDATED, false, {
audioLevel
});
};
/**
* Dispatched by a Publisher when the user has stopped sharing one or all media types
* (video, audio, or screen).
*
* @property {MediaStreamTrack} track The media track that has ended. This property is undefined
* if all media tracks have stopped. Check the kind
property of this object to
* see if the track is an audio track or a video track.
*
* @class MediaStoppedEvent
* @augments Event
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack
*/
Events.MediaStoppedEvent = function MediaStoppedEvent(target, track) {
return new Event(eventNames.MEDIA_STOPPED, true, {
target,
track
});
};
/**
* Dispatched by a Subscriber or Publisher object to indicate the video
element
* (or object
element in Internet Explorer) was created. Add a listener for this event
* when you set the insertDefaultUI
option to false
in the call to the
* OT.initPublisher() method or the
* Session.subscribe() method. The element
* property of the event object is a reference to the Publisher's video
element
* (or the object
element in Internet Explorer). Add it to the HTML DOM to display the
* video. (When you set the insertDefaultUI
option to false
, the
* video
element is not inserted into the DOM automatically.)
*
* Add a listener for this event only if you have set the
* The Error class defines the Error thrown when the promise returned by
* the OT.#unblockAudio() method is rejected.
*
*
* The
* Deprecation notice:
*
* The Errors when calling Errors when calling Errors when calling Errors when calling Errors when calling Errors when calling Errors when calling Errors when calling General errors that can occur when calling any method:
* Use the Note that for all errors, this string starts with
* The Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when calling Errors that can occur when the Promise returned by the You can either pass one parameter or two parameters to this method. If you pass one parameter, insertDefaultUI
option to
* false
. If you have not set insertDefaultUI
option
* to false
, do not move the video
element (or the object
* element containing the video in Internet Explorer) in the HTML DOM. Doing so causes the
* Publisher or Subscriber object to be destroyed.
*
* @property {Number} element A reference to the Publisher or Subscriber's video
* element (or in Internet Explorer the object
element containing the video).
* Add it to the HTML DOM to display the video.
*
* @class VideoElementCreatedEvent
* @augments Event
*/
Events.VideoElementCreatedEvent = function VideoElementCreatedEvent(element) {
return new Event(eventNames.VIDEO_ELEMENT_CREATED, false, {
element
});
};
return Events;
};
/***/ }),
/* 21 */
/***/ (function(module, exports, __webpack_require__) {
var isFunction = __webpack_require__(13),
isLength = __webpack_require__(109);
/**
* Checks if `value` is array-like. A value is considered array-like if it's
* not a function and has a `value.length` that's an integer greater than or
* equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is array-like, else `false`.
* @example
*
* _.isArrayLike([1, 2, 3]);
* // => true
*
* _.isArrayLike(document.body.children);
* // => true
*
* _.isArrayLike('abc');
* // => true
*
* _.isArrayLike(_.noop);
* // => false
*/
function isArrayLike(value) {
return value != null && isLength(value.length) && !isFunction(value);
}
module.exports = isArrayLike;
/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// TODO: Eliminate the need for this module, which is globally tracking these objects.
const Collection = __webpack_require__(241);
const sessionObjects = {
// Publishers are id'd by their guid
publishers: new Collection('guid'),
// Subscribers are id'd by their widgetId
subscribers: new Collection('widgetId'),
sessions: new Collection()
};
module.exports = sessionObjects;
/***/ }),
/* 23 */
/***/ (function(module, exports) {
function _extends() {
module.exports = _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
module.exports = _extends;
/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// Event names lookup
module.exports = {
// Activity Status for cams/mics
ACTIVE: 'active',
INACTIVE: 'inactive',
UNKNOWN: 'unknown',
// Archive types
PER_SESSION: 'perSession',
PER_STREAM: 'perStream',
// Events
EXCEPTION: 'exception',
ISSUE_REPORTED: 'issueReported',
// Session Events
SESSION_CONNECTED: 'sessionConnected',
SESSION_RECONNECTING: 'sessionReconnecting',
SESSION_RECONNECTED: 'sessionReconnected',
SESSION_DISCONNECTED: 'sessionDisconnected',
STREAM_CREATED: 'streamCreated',
STREAM_DESTROYED: 'streamDestroyed',
CONNECTION_CREATED: 'connectionCreated',
CONNECTION_DESTROYED: 'connectionDestroyed',
SIGNAL: 'signal',
STREAM_PROPERTY_CHANGED: 'streamPropertyChanged',
MICROPHONE_LEVEL_CHANGED: 'microphoneLevelChanged',
// Publisher Events
RESIZE: 'resize',
SETTINGS_BUTTON_CLICK: 'settingsButtonClick',
DEVICE_INACTIVE: 'deviceInactive',
INVALID_DEVICE_NAME: 'invalidDeviceName',
ACCESS_ALLOWED: 'accessAllowed',
ACCESS_DENIED: 'accessDenied',
ACCESS_DIALOG_OPENED: 'accessDialogOpened',
ACCESS_DIALOG_CLOSED: 'accessDialogClosed',
ECHO_CANCELLATION_MODE_CHANGED: 'echoCancellationModeChanged',
MEDIA_STOPPED: 'mediaStopped',
PUBLISHER_DESTROYED: 'destroyed',
// Subscriber Events
SUBSCRIBER_DESTROYED: 'destroyed',
SUBSCRIBER_CONNECTED: 'connected',
SUBSCRIBER_DISCONNECTED: 'disconnected',
// DeviceManager Events
DEVICES_DETECTED: 'devicesDetected',
// DevicePanel Events
DEVICES_SELECTED: 'devicesSelected',
CLOSE_BUTTON_CLICK: 'closeButtonClick',
MICLEVEL: 'microphoneActivityLevel',
MICGAINCHANGED: 'microphoneGainChanged',
// Environment Loader
ENV_LOADED: 'envLoaded',
ENV_UNLOADED: 'envUnloaded',
// Audio activity Events
AUDIO_LEVEL_UPDATED: 'audioLevelUpdated',
VIDEO_ELEMENT_CREATED: 'videoElementCreated'
};
/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
var logDisabled_ = true;
var deprecationWarnings_ = true;
/**
* Extract browser version out of the provided user agent string.
*
* @param {!string} uastring userAgent string.
* @param {!string} expr Regular expression used as match criteria.
* @param {!number} pos position in the version string to be returned.
* @return {!number} browser version.
*/
function extractVersion(uastring, expr, pos) {
var match = uastring.match(expr);
return match && match.length >= pos && parseInt(match[pos], 10);
}
// Wraps the peerconnection event eventNameToWrap in a function
// which returns the modified event object (or false to prevent
// the event).
function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
if (!window.RTCPeerConnection) {
return;
}
var proto = window.RTCPeerConnection.prototype;
var nativeAddEventListener = proto.addEventListener;
proto.addEventListener = function(nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap) {
return nativeAddEventListener.apply(this, arguments);
}
var wrappedCallback = function(e) {
var modifiedEvent = wrapper(e);
if (modifiedEvent) {
cb(modifiedEvent);
}
};
this._eventMap = this._eventMap || {};
this._eventMap[cb] = wrappedCallback;
return nativeAddEventListener.apply(this, [nativeEventName,
wrappedCallback]);
};
var nativeRemoveEventListener = proto.removeEventListener;
proto.removeEventListener = function(nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap || !this._eventMap
|| !this._eventMap[cb]) {
return nativeRemoveEventListener.apply(this, arguments);
}
var unwrappedCb = this._eventMap[cb];
delete this._eventMap[cb];
return nativeRemoveEventListener.apply(this, [nativeEventName,
unwrappedCb]);
};
Object.defineProperty(proto, 'on' + eventNameToWrap, {
get: function() {
return this['_on' + eventNameToWrap];
},
set: function(cb) {
if (this['_on' + eventNameToWrap]) {
this.removeEventListener(eventNameToWrap,
this['_on' + eventNameToWrap]);
delete this['_on' + eventNameToWrap];
}
if (cb) {
this.addEventListener(eventNameToWrap,
this['_on' + eventNameToWrap] = cb);
}
},
enumerable: true,
configurable: true
});
}
// Utility methods.
module.exports = {
extractVersion: extractVersion,
wrapPeerConnectionEvent: wrapPeerConnectionEvent,
disableLog: function(bool) {
if (typeof bool !== 'boolean') {
return new Error('Argument type: ' + typeof bool +
'. Please use a boolean.');
}
logDisabled_ = bool;
return (bool) ? 'adapter.js logging disabled' :
'adapter.js logging enabled';
},
/**
* Disable or enable deprecation warnings
* @param {!boolean} bool set to true to disable warnings.
*/
disableWarnings: function(bool) {
if (typeof bool !== 'boolean') {
return new Error('Argument type: ' + typeof bool +
'. Please use a boolean.');
}
deprecationWarnings_ = !bool;
return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
},
log: function() {
if (typeof window === 'object') {
if (logDisabled_) {
return;
}
if (typeof console !== 'undefined' && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
}
},
/**
* Shows a deprecation warning suggesting the modern and spec-compatible API.
*/
deprecated: function(oldMethod, newMethod) {
if (!deprecationWarnings_) {
return;
}
console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
' instead.');
},
/**
* Browser detector.
*
* @return {object} result containing browser and version
* properties.
*/
detectBrowser: function(window) {
var navigator = window && window.navigator;
// Returned result object.
var result = {};
result.browser = null;
result.version = null;
// Fail early if it's not a browser
if (typeof window === 'undefined' || !window.navigator) {
result.browser = 'Not a browser.';
return result;
}
if (navigator.mozGetUserMedia) { // Firefox.
result.browser = 'firefox';
result.version = extractVersion(navigator.userAgent,
/Firefox\/(\d+)\./, 1);
} else if (navigator.webkitGetUserMedia) {
// Chrome, Chromium, Webview, Opera.
// Version matches Chrome/WebRTC version.
result.browser = 'chrome';
result.version = extractVersion(navigator.userAgent,
/Chrom(e|ium)\/(\d+)\./, 2);
} else if (navigator.mediaDevices &&
navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
result.browser = 'edge';
result.version = extractVersion(navigator.userAgent,
/Edge\/(\d+).(\d+)$/, 2);
} else if (window.RTCPeerConnection &&
navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { // Safari.
result.browser = 'safari';
result.version = extractVersion(navigator.userAgent,
/AppleWebKit\/(\d+)\./, 1);
} else { // Default fallthrough: not supported.
result.browser = 'Not a supported browser.';
return result;
}
return result;
}
};
/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {
var Symbol = __webpack_require__(37),
getRawTag = __webpack_require__(338),
objectToString = __webpack_require__(339);
/** `Object#toString` result references. */
var nullTag = '[object Null]',
undefinedTag = '[object Undefined]';
/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
/**
* The base implementation of `getTag` without fallbacks for buggy environments.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
function baseGetTag(value) {
if (value == null) {
return value === undefined ? undefinedTag : nullTag;
}
return (symToStringTag && symToStringTag in Object(value))
? getRawTag(value)
: objectToString(value);
}
module.exports = baseGetTag;
/***/ }),
/* 27 */
/***/ (function(module, exports, __webpack_require__) {
var arrayLikeKeys = __webpack_require__(172),
baseKeys = __webpack_require__(111),
isArrayLike = __webpack_require__(21);
/**
* Creates an array of the own enumerable property names of `object`.
*
* **Note:** Non-object values are coerced to objects. See the
* [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
* for more details.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
* @example
*
* function Foo() {
* this.a = 1;
* this.b = 2;
* }
*
* Foo.prototype.c = 3;
*
* _.keys(new Foo);
* // => ['a', 'b'] (iteration order is not guaranteed)
*
* _.keys('hi');
* // => ['0', '1']
*/
function keys(object) {
return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
module.exports = keys;
/***/ }),
/* 28 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(Promise) {
var _interopRequireDefault = __webpack_require__(2);
var _extends2 = _interopRequireDefault(__webpack_require__(23));
var _createClass2 = _interopRequireDefault(__webpack_require__(40));
/* global __PROPERTIES__ */
const defaultAxios = __webpack_require__(186);
const cloneDeep = __webpack_require__(50);
const pick = __webpack_require__(394);
const mapKeys = __webpack_require__(446);
const logging = __webpack_require__(1)('StaticConfig');
const _require = __webpack_require__(128),
prependProxyToUrlIfNeeded = _require.prependProxyToUrlIfNeeded;
/**
* @typedef {Object} builtInConfig properties that are baked in and are injected through webpack
* @property {String} version Build version (comes from package.json)
* @property {String} buildHash Commit hash this build was built from
* @property {Object} minimumVersion
* @property {String} minimumVersion.firefox Minimum version of Firefox needed to support OpenTok
* @property {String} minimumVersion.chrome Minimum version of Chrome needed to support OpenTok
* @property {Boolean} debug If true sets logging level to DEBUG else sets it to WARN
* @property {String} websiteURL Used to construct urls to the TokBox website
* @property {String} loggingURL Where to send analytical events
* @property {String} apiURL The API to talk to (Anvil)
*/
/** @type builtInConfig */
const builtInConfig = cloneDeep({"version":"v2.17.3","buildHash":"32ce4158c","minimumVersion":{"firefox":52,"chrome":49},"debug":"false","websiteURL":"http://www.tokbox.com","configURL":"https://config.opentok.com","ipWhitelistConfigURL":"","cdnURL":"https://static.opentok.com","loggingURL":"https://hlg.tokbox.com/prod","apiURL":"https://anvil.opentok.com","sentryDSN":"https://40bf769a2f474b2a8ef66d2809c7cfe0@sentry.io/104064"});
const whitelistAllowedRuntimeProperties = pick(['apiURL', 'assetURL', 'cdnURL', 'enableErrorReporting', 'sessionInfoOverrides', 'loggingURL']);
const liveConfigMap = {
apiUrl: 'apiURL',
loggingUrl: 'loggingURL'
};
const normalizeLiveConfig = mapKeys(key => liveConfigMap[key]);
function staticConfigFactory(_temp) {
let _ref = _temp === void 0 ? {} : _temp,
_ref$axios = _ref.axios,
axios = _ref$axios === void 0 ? defaultAxios : _ref$axios;
/**
* @class StaticConfig
*/
let StaticConfig = /*#__PURE__*/function () {
StaticConfig.onlyLocal = function onlyLocal() {
const runtimeProperties = cloneDeep((typeof window !== undefined ? window : global).OTProperties);
return new StaticConfig((0, _extends2.default)({}, builtInConfig, {}, whitelistAllowedRuntimeProperties(runtimeProperties)));
}
/**
* Construct a StaticConfig instance with baked in, runtime, and live configuration
*
* @static
* @param {any} { sessionId, token, useIpWhitelistConfigUrl }
* @memberof StaticConfig
* @return {PromisecompletionHandler
parameter:
*
*
*
*
* completionHandler
parameter is a function that is called when the call to
* the asynchronous method succeeds or fails. If the asynchronous call fails, the completion
* handler function is passed an error object (defined by the Error class). The code
* and message
properties of the error object provide details about the error.
*
* OT.Error
will not be defined in an upcoming version of OpenTok. When this happens,
* errors emitted by the SDK will simply be instances of standard Error
objects.
* If your code currently checks if (error instanceof OT.Error)
please replace it
* if (/^OT_/.test(error.name))
. (The name
property for all OpenTok
* errors with begins with 'OT_'
.) Note however that this code is for the
* purposes of smoothly upgrading, and it may be more appropriate for your code to simply compare
* against the specific error you want to handle, such as
* if (error.name === 'OT_USER_MEDIA_ACCESS_DENIED')
.
*
* @property {Number} code Deprecated — the error code. Use the name
property,
* not the code
property, to identify the error programatically.
*
* code
property of the Error object can be set to one of the values
* listed below. This property can also be set to other values, and you may want to report
* this value when discussing an issue with TokBox support.
* Session.connect()
:
*
*
*
*
*
* code
Description
*
*
* 1004
* Authentication error. Check the error message for details. This error can result if you
* pass in an expired token when trying to connect to a session. It can also occur if you pass
* in an invalid token or API key. Make sure that you are generating the token using the
* current version of one of the
* OpenTok server SDKs.
*
*
* 1005
* Invalid Session ID. Make sure you generate the session ID using the current version of
* one of the OpenTok server
* SDKs.
*
*
* 1006
* Connect Failed. Unable to connect to the session. You may want to have the client check
* the network connection.
*
*
* 1026
* Terms of service violation: export compliance. See the
* Terms of Service.
*
*
* 1027
* Connection limit exceeded. The client tried to connect to a session that has exceeded
* limit for simultaneous connections.
*
*
* 2001
* Connect Failed. Unexpected response from the OpenTok server. Try connecting again
* later.
* Session.forceDisconnect()
:
*
*
*
*
*
*
* code
* Description
*
*
* 1010
* The client is not connected to the OpenTok session. Check that client connects
* successfully and has not disconnected before calling forceDisconnect().
*
*
* 1520
* Unable to force disconnect. The client's token does not have the role set to moderator.
* Once the client has connected to the session, the
* capabilities
property of
* the Session object lists the client's capabilities.Session.forceUnpublish()
:
*
*
*
*
*
* code
Description
*
*
* 1010
* The client is not connected to the OpenTok session. Check that client connects
* successfully and has not disconnected before calling forceUnpublish().
*
*
* 1530
* Unable to force unpublish. The client's token does not have the role set to moderator.
* Once the client has connected to the session, the
* capabilities
property of
* the Session object lists the client's capabilities.
*
* 1535
* Force Unpublish on an invalid stream. Make sure that the stream has not left the
* session before you call the
* forceUnpublish()
method.Session.publish()
:
*
*
*
*
*
* code
Description
*
*
* 1010
* The client is not connected to the OpenTok session. Check that the client connects
* successfully before trying to publish. And check that the client has not disconnected
* before trying to publish.
*
*
* 1500
* Unable to Publish. This can be caused by the following:
*
*
*
*
* capabilities
* property of the Session object lists the client's capabilities.
*
* 1553
* WebRTC ICE workflow error. This is an error that occurs when trying to establish
* communication between clients in the session. Try publishing again or reconnecting to
* the session.
*
*
* 1601
* Internal error -- WebRTC publisher error. Try republishing or reconnecting to the
* session.
*
*
* 2001
* Publish Failed. Unexpected response from the OpenTok server. Try publishing again
* later.
* Session.signal()
:
*
*
*
*
*
* code
Description
*
*
* 400
* One of the signal properties — data, type, or to —
* is invalid. Or the data cannot be parsed as JSON.
*
*
* 404 The to connection does not exist.
*
*
* 413 The type string exceeds the maximum length (128 bytes),
* or the data string exceeds the maximum size (8 kB).
*
*
* 500
* The client is not connected to the OpenTok session. Check that the client connects
* successfully before trying to signal. And check that the client has not disconnected before
* trying to signal.
*
*
* 2001
* Signal Failed. Unexpected response from the OpenTok server. Try sending the signal again
* later.
* Session.subscribe()
:
*
*
*
*
*
*
* code
* Description
*
*
* 1013
* WebRTC PeerConnection error. Try resubscribing to the stream or
* reconnecting to the session.
*
*
* 1554
* WebRTC ICE workflow error. This is an error that occurs when trying to establish
* communication between clients in the session. Try resubscribing to the stream or
* reconnecting to the session.
*
*
* 1600
* Internal error -- WebRTC subscriber error. Try resubscribing to the stream or
* reconnecting to the session.
*
*
* 1605
* Stream limit exceeded. The client tried to subscribe to a stream in a session
* that has exceeded the limit for simultaneous streams.
*
*
* 2001
* Subscribe Failed. Unexpected response from the OpenTok server. Try subscribing again
* later.
* OT.initPublisher()
:
*
*
*
*
*
* code
Description
*
*
* 1004
* Authentication error. Check the error message for details. This error can result if you
* pass in an expired token when trying to connect to a session. It can also occur if you
* pass in an invalid token or API key. Make sure that you are generating the token using
* the current version of one of the
* OpenTok server SDKs.
*
*
* 1550
* Screen sharing is not supported (and you set the
* videoSource
property
* of the options
parameter of OT.initPublisher()
to
* "application"
, "screen"
, or "window"
).
* Before calling OT.initPublisher()
, you can call
* OT.checkScreenSharingCapability()
* to check if screen sharing is supported.
*
* 1551
* A screen-sharing extension needs to be registered but it is not. This error can occur
* when you set the
* videoSource
property of the options
parameter
* of OT.initPublisher()
to "application"
, "screen"
,
* or "window"
. Before calling OT.initPublisher()
, you can call
* OT.checkScreenSharingCapability()
* to check if screen sharing requires an extension to be registered.
*
* 1552
* A screen-sharing extension is required, but it is not installed. This error can occur
* when you set the
* videoSource
property of the options
parameter
* of OT.initPublisher()
to "screen"
. Before calling
* OT.initPublisher()
, you can call
* OT.checkScreenSharingCapability()
* to check if screen sharing requires an extension to be installed.OT.reportIssue()
:
*
*
*
*
*
* code
Description
*
*
* 2011
* Error calling OT.reportIssue(). Check the client's network connection.
*
*
*
* @property {String} message The message string provides details about the error.
*
*
*
*
* code
Description
*
*
* 1011
* Invalid Parameter. Check that you have passed valid parameter values into the method
* call.
*
*
* 2000
* Internal Error. Try reconnecting to the OpenTok session and trying the action again.
* name
property (not the message
property) to identify the error
* programatically.
*
* @property {String} name A string identifying the error. Use this property (not the
* code
property) to identify the error.
*
* 'OT_'
.
*
* name
property of the Error object can be set to one of the values
* listed below. For unexpected errors, this property can be undefined or set to other
* values. You may want to report this value when discussing an issue with TokBox support.
* OT.initPublisher()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_HARDWARE_UNAVAILABLE'
The hardware to fulfill the user media request appears to exist but could not be acquired.
* It might be in use by another application.
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_MEDIA_ERR_ABORTED'
The fetching of the stream for the video element has been aborted.
*
*
*
* 'OT_MEDIA_ERR_DECODE'
A decoding error occurred while trying to play the stream in the video element.
*
*
*
* 'OT_MEDIA_ERR_NETWORK'
A network error caused the stream to stop being fetched.
*
*
*
* 'OT_MEDIA_ERR_SRC_NOT_SUPPORTED'
The stream has been detected to be not suitable for playback.
*
*
*
* 'OT_NOT_SUPPORTED'
Something in the user media request is not supported.
*
*
*
* 'OT_NO_DEVICES_FOUND'
No devices were found to provide the media stream.
*
*
*
* 'OT_NO_VALID_CONSTRAINTS'
Video and audio were both disabled. You need to enable at least one.
*
*
*
* 'OT_SCREEN_SHARING_NOT_SUPPORTED'
Screen sharing is not supported in the browser.
*
*
*
* 'OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED'
Screen-sharing support in this browser requires an extension, but one has not been
* registered.
*
*
*
* 'OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED'
Screen-sharing support in this browser requires an extension, but the extension is not
* installed.
* OT.reportIssue()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_REPORT_ISSUE_FAILED'
Error calling OT.reportIssue(). Check the client\'s network connection.
* Session.connect()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_AUTHENTICATION_ERROR'
The API key does not match the token or session.
*
*
*
* 'OT_BADLY_FORMED_RESPONSE'
The JSON response from the OpenTok server was badly formed.
*
*
*
* 'OT_CONNECT_FAILED'
Failed to connect to session.
*
*
*
* 'OT_CONNECTION_LIMIT_EXCEEDED'
The client tried to connect to a session that has exceeded the limit for
* simultaneous connections.
*
*
*
* 'OT_EMPTY_RESPONSE_BODY'
Received an unexpected empty response from the OpenTok server.
*
*
*
* 'OT_INVALID_SESSION_ID'
The session ID was invalid.
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
*
*
*
* 'OT_TERMS_OF_SERVICE_FAILURE'
Couldn't connect due to a terms of service violation.
*
*
*
* 'OT_INVALID_HTTP_STATUS'
Received an unexpected HTTP status.
*
*
*
* 'OT_XDOMAIN_OR_PARSING_ERROR'
There was a cross domain error or the server responded with invalid JSON.
* Session.forceDisconnect()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
*
*
*
* 'OT_PERMISSION_DENIED'
This token does not allow this action. The role must be at least moderator.
* Session.forceUnpublish()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
*
*
*
* 'OT_PERMISSION_DENIED'
This token does not allow this action. The role must be at least moderator.
* Session.publish()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_CHROME_MICROPHONE_ACQUISITION_ERROR'
Chrome fails to get access to the microphone, due to a Chrome error. To work
* around this issue, the end-user must restart Chrome and reload your site. This is a
* known issue
* to be fixed in Chrome.
*
*
*
* 'OT_CONSTRAINTS_NOT_SATISFIED'
The constraints for getting user media could not be satisfied.
*
*
*
* 'OT_CREATE_PEER_CONNECTION_FAILED'
Failed to create a connection between this client and the subscriber or the
* OpenTok Media Router.
*
*
*
* 'OT_HARDWARE_UNAVAILABLE'
The hardware to fulfill the user media request appears to exist but could not be acquired.
* It might be in use by another application.
*
*
*
* 'OT_ICE_WORKFLOW_FAILED'
Something went wrong while establishing WebRTC connectivity.
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_MEDIA_ERR_ABORTED'
The fetching of the stream for the video element has been aborted.
*
*
*
* 'OT_MEDIA_ERR_DECODE'
A decoding error occurred while trying to play the stream in the video element.
*
*
*
* 'OT_MEDIA_ERR_NETWORK'
A network error caused the stream to stop being fetched.
*
*
*
* 'OT_MEDIA_ERR_SRC_NOT_SUPPORTED'
The stream has been detected to be not suitable for playback.
*
*
*
* 'OT_NO_DEVICES_FOUND'
No devices were found to provide the media stream.
*
*
*
* 'OT_NO_VALID_CONSTRAINTS'
Video and audio were both disabled. You need to enable at least one.
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
*
*
*
* 'OT_NOT_SUPPORTED'
Something in the user media request is not supported.
*
*
*
* 'OT_PERMISSION_DENIED'
This token does not allow publishing. The role must be at least publisher to enable
* this funcitonality.
*
*
*
* 'OT_SCREEN_SHARING_NOT_SUPPORTED'
Screen sharing is not supported in the browser.
*
*
*
* 'OT_SCREEN_SHARING_EXTENSION_NOT_REGISTERED'
Screen-sharing support in this browser requires an extension, but one has not been
* registered.
*
*
*
* 'OT_SCREEN_SHARING_EXTENSION_NOT_INSTALLED'
Screen-sharing support in this browser requires an extension, but the extension is not
* installed.
*
*
*
* 'OT_SET_REMOTE_DESCRIPTION_FAILED'
The WebRTC connection failed during setDescription.
*
*
*
* 'OT_STREAM_CREATE_FAILED'
Failed to create the stream in the server model.
*
*
*
* 'OT_TIMEOUT'
Could not publish in a reasonable amount of time.
*
*
*
* 'OT_USER_MEDIA_ACCESS_DENIED'
The end-user denied permission to access hardware devices, or the end-user
* denied access to the screen (for a screen-sharing stream).
*
*
*
* 'OT_UNEXPECTED_SERVER_RESPONSE'
This indicates an unexpected error from the OpenTok server.
* Session.signal()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
*
*
*
* 'OT_NOT_FOUND'
A resource was not found.
*
*
*
* 'OT_RATE_LIMIT_EXCEEDED'
The signal could not be sent due to the rate limit.
* Session.subscribe()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_CREATE_PEER_CONNECTION_FAILED'
Failed to create a connection between this client and the stream's publisher or the
* OpenTok Media Router.
*
*
*
* 'OT_DISCONNECTED'
Action failed because the client is not connected to the session.
*
*
*
* 'OT_INVALID_PARAMETER'
One or more parameters was not valid or not provided.
*
*
*
* 'OT_ICE_WORKFLOW_FAILED'
Something went wrong while establishing WebRTC connectivity.
*
*
*
* 'OT_MEDIA_ERR_ABORTED'
The fetching of the stream for the video element has been aborted.
*
*
*
* 'OT_MEDIA_ERR_DECODE'
A decoding error occurred while trying to play the stream in the video element.
*
*
*
* 'OT_MEDIA_ERR_NETWORK'
A network error caused the stream to stop being fetched.
*
*
*
* 'OT_MEDIA_ERR_SRC_NOT_SUPPORTED'
The stream has been detected to be not suitable for playback.
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
*
*
*
* 'OT_SET_REMOTE_DESCRIPTION_FAILED'
The WebRTC connection failed during setDescription.
*
*
*
* 'OT_STREAM_DESTROYED'
The stream was destroyed before it could be subscribed to.
*
*
*
* 'OT_STREAM_LIMIT_EXCEEDED'
The client tried to subscribe to a stream in a session that has exceeded the limit
* for simultaneous streams.
*
*
*
* 'OT_STREAM_NOT_FOUND'
Couldn't subscribe to the stream because it was not found.
* Subscriber.getStats()
:
*
*
*
*
*
* name
Description
*
*
*
* 'OT_NOT_CONNECTED'
Couldn't perform action because you are not connected to the network.
* OT.unblockAudio()
* method is rejected:
*
*
* @class Error
* @augments Event
*/
const OTErrorClass = function OTErrorClass(code, message) {
Error.call(this);
this.code = code;
this.message = message;
};
OTErrorClass.prototype = Object.create(Error.prototype);
OTErrorClass.prototype.constructor = OTErrorClass;
module.exports = OTErrorClass;
eventing(OTErrorClass);
const errorsCodesToTitle = {
1004: 'Authentication error',
1005: 'Invalid Session ID',
1006: 'Connect Failed',
1007: 'Connect Rejected',
1008: 'Connect Time-out',
1009: 'Security Error',
1010: 'Not Connected',
1011: 'Invalid Parameter',
1012: 'Peer-to-peer Stream Play Failed',
1013: 'Connection Failed',
1014: 'API Response Failure',
1015: 'PeerConnection not connected, cannot call this method',
1021: 'Request Timeout',
1026: 'Terms of Service Violation: Export Compliance',
1027: 'Connection Limit Exceeded',
1500: 'Unable to Publish',
1501: 'Unable to Subscribe',
1502: 'Unsupported Video Codec',
1503: 'No TURN server found',
1520: 'Unable to Force Disconnect',
1530: 'Unable to Force Unpublish',
1553: 'ICEWorkflow failed',
1600: 'createOffer, createAnswer, setLocalDescription, setRemoteDescription',
1605: 'Stream Limit Exceeded',
2000: 'Internal Error',
2001: 'Unexpected Server Response',
4000: 'WebSocket Connection Failed',
4001: 'WebSocket Network Disconnected',
5001: 'Unable to access media engine'
};
/**
* Get the title of an error by error code
*
* @property {Number|String} code The error code to lookup
* @return {String} The title of the message with that code
*
* @example
*
* OTError.getTitleByCode(1006) === 'Connect Failed'
*/
OTErrorClass.getTitleByCode = function (code) {
return errorsCodesToTitle[+code];
}; // @todo redo this when we have time to tidy up
//
// @example
//
// OTError.handleJsException("Descriptive error message", 2000, {
// session: session,
// target: stream|publisher|subscriber|session|etc
// });
//
OTErrorClass.handleJsException = (_ref) => {
let error = _ref.error,
errorMsg = _ref.errorMsg,
code = _ref.code,
target = _ref.target,
analytics = _ref.analytics;
if (!target) {
throw new Error('handleJsException requires target');
}
const getCode = () => code !== undefined ? code : error && error.code;
const getMessage = () => errorMsg !== undefined ? errorMsg : error && error.message;
const title = OTErrorClass.getTitleByCode(getCode());
logging.error("OT.exception :: title: " + title + " (" + getCode() + ") msg: " + getMessage());
try {
const options = {
partnerId: target.session ? target.session.apiKey : target.apiKey,
sessionId: target.session ? target.session.sessionId : target.sessionId
};
analytics.logError(getCode(), 'tb.exception', title, {
details: getMessage()
}, options);
const event = new Events.ExceptionEvent(eventNames.EXCEPTION, getMessage(), title, getCode(), target, target, error);
if (error) {
event.stack = error.stack;
} else {
// Augment the event object with a stacktrace for error reporting purposes
try {
throw new Error();
} catch (caughtError) {
event.stack = caughtError.stack;
}
}
OTErrorClass.dispatchEvent(event);
} catch (err) {
logging.error("OT.exception :: Failed to dispatch exception - " + err.toString()); // Don't throw an error because this is asynchronous
// don't do an exceptionHandler because that would be recursive
}
};
/***/ }),
/* 34 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-underscore-dangle, no-param-reassign */
const OTHelpers = __webpack_require__(4); // A mixin to encapsulate the basic widget behaviour. This needs a better name,
// it's not actually a widget. It's actually "Behaviour that can be applied to
// an object to make it support the basic Chrome widget workflow"...but that would
// probably been too long a name.
module.exports = function Widget(widget, options) {
let _mode;
const _options = options || {}; //
// @param [String] mode
// 'on', 'off', or 'auto'
//
widget.setDisplayMode = function (mode) {
const newMode = mode || 'auto';
if (_mode === newMode) {
return;
}
OTHelpers.removeClass(this.domElement, "OT_mode-" + _mode);
OTHelpers.addClass(this.domElement, "OT_mode-" + newMode);
_mode = newMode;
};
widget.getDisplayMode = function () {
return _mode;
};
widget.showAfterLoading = function () {
OTHelpers.removeClass(this.domElement, 'OT_hide-forced');
};
widget.hideWhileLoading = function () {
OTHelpers.addClass(this.domElement, 'OT_hide-forced');
};
widget.destroy = function () {
if (_options.onDestroy) {
_options.onDestroy(this.domElement);
}
if (this.domElement) {
OTHelpers.removeElement(this.domElement);
}
return widget;
};
widget.appendTo = function (parent) {
// create the element under parent
this.domElement = OTHelpers.createElement(_options.nodeName || 'div', _options.htmlAttributes, _options.htmlContent);
if (_options.onCreate) {
_options.onCreate(this.domElement);
}
widget.setDisplayMode(_options.mode);
if (_options.mode === 'auto') {
// if the mode is auto we hold the "on mode" for 2 seconds
// this will let the proper widgets nicely fade away and help discoverability
OTHelpers.addClass(widget.domElement, 'OT_mode-on-hold');
setTimeout(() => {
OTHelpers.removeClass(widget.domElement, 'OT_mode-on-hold');
}, 2000);
} // add the widget to the parent
parent.appendChild(this.domElement);
return widget;
};
};
/***/ }),
/* 35 */
/***/ (function(module, exports, __webpack_require__) {
var baseClone = __webpack_require__(74);
/** Used to compose bitmasks for cloning. */
var CLONE_SYMBOLS_FLAG = 4;
/**
* Creates a shallow clone of `value`.
*
* **Note:** This method is loosely based on the
* [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
* and supports cloning arrays, array buffers, booleans, date objects, maps,
* numbers, `Object` objects, regexes, sets, strings, symbols, and typed
* arrays. The own enumerable properties of `arguments` objects are cloned
* as plain objects. An empty object is returned for uncloneable values such
* as error objects, functions, DOM nodes, and WeakMaps.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to clone.
* @returns {*} Returns the cloned value.
* @see _.cloneDeep
* @example
*
* var objects = [{ 'a': 1 }, { 'b': 2 }];
*
* var shallow = _.clone(objects);
* console.log(shallow[0] === objects[0]);
* // => true
*/
function clone(value) {
return baseClone(value, CLONE_SYMBOLS_FLAG);
}
module.exports = clone;
/***/ }),
/* 36 */
/***/ (function(module, exports, __webpack_require__) {
var baseIsNative = __webpack_require__(337),
getValue = __webpack_require__(342);
/**
* Gets the native function at `key` of `object`.
*
* @private
* @param {Object} object The object to query.
* @param {string} key The key of the method to get.
* @returns {*} Returns the function if it's native, else `undefined`.
*/
function getNative(object, key) {
var value = getValue(object, key);
return baseIsNative(value) ? value : undefined;
}
module.exports = getNative;
/***/ }),
/* 37 */
/***/ (function(module, exports, __webpack_require__) {
var root = __webpack_require__(10);
/** Built-in value references. */
var Symbol = root.Symbol;
module.exports = Symbol;
/***/ }),
/* 38 */
/***/ (function(module, exports, __webpack_require__) {
var assignValue = __webpack_require__(80),
baseAssignValue = __webpack_require__(60);
/**
* Copies properties of `source` to `object`.
*
* @private
* @param {Object} source The object to copy properties from.
* @param {Array} props The property identifiers to copy.
* @param {Object} [object={}] The object to copy properties to.
* @param {Function} [customizer] The function to customize copied values.
* @returns {Object} Returns `object`.
*/
function copyObject(source, props, object, customizer) {
var isNew = !object;
object || (object = {});
var index = -1,
length = props.length;
while (++index < length) {
var key = props[index];
var newValue = customizer
? customizer(object[key], source[key], key, object, source)
: undefined;
if (newValue === undefined) {
newValue = source[key];
}
if (isNew) {
baseAssignValue(object, key, newValue);
} else {
assignValue(object, key, newValue);
}
}
return object;
}
module.exports = copyObject;
/***/ }),
/* 39 */
/***/ (function(module, exports, __webpack_require__) {
var DataView = __webpack_require__(366),
Map = __webpack_require__(106),
Promise = __webpack_require__(367),
Set = __webpack_require__(180),
WeakMap = __webpack_require__(181),
baseGetTag = __webpack_require__(26),
toSource = __webpack_require__(169);
/** `Object#toString` result references. */
var mapTag = '[object Map]',
objectTag = '[object Object]',
promiseTag = '[object Promise]',
setTag = '[object Set]',
weakMapTag = '[object WeakMap]';
var dataViewTag = '[object DataView]';
/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = toSource(DataView),
mapCtorString = toSource(Map),
promiseCtorString = toSource(Promise),
setCtorString = toSource(Set),
weakMapCtorString = toSource(WeakMap);
/**
* Gets the `toStringTag` of `value`.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
var getTag = baseGetTag;
// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
(Map && getTag(new Map) != mapTag) ||
(Promise && getTag(Promise.resolve()) != promiseTag) ||
(Set && getTag(new Set) != setTag) ||
(WeakMap && getTag(new WeakMap) != weakMapTag)) {
getTag = function(value) {
var result = baseGetTag(value),
Ctor = result == objectTag ? value.constructor : undefined,
ctorString = Ctor ? toSource(Ctor) : '';
if (ctorString) {
switch (ctorString) {
case dataViewCtorString: return dataViewTag;
case mapCtorString: return mapTag;
case promiseCtorString: return promiseTag;
case setCtorString: return setTag;
case weakMapCtorString: return weakMapTag;
}
}
return result;
};
}
module.exports = getTag;
/***/ }),
/* 40 */
/***/ (function(module, exports) {
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
module.exports = _createClass;
/***/ }),
/* 41 */
/***/ (function(module, exports, __webpack_require__) {
var isSymbol = __webpack_require__(68);
/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;
/**
* Converts `value` to a string key if it's not a string or symbol.
*
* @private
* @param {*} value The value to inspect.
* @returns {string|symbol} Returns the key.
*/
function toKey(value) {
if (typeof value == 'string' || isSymbol(value)) {
return value;
}
var result = (value + '');
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}
module.exports = toKey;
/***/ }),
/* 42 */
/***/ (function(module, exports, __webpack_require__) {
var basePick = __webpack_require__(443),
flatRest = __webpack_require__(126);
/**
* Creates an object composed of the picked `object` properties.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param {Object} object The source object.
* @param {...(string|string[])} [paths] The property paths to pick.
* @returns {Object} Returns the new object.
* @example
*
* var object = { 'a': 1, 'b': '2', 'c': 3 };
*
* _.pick(object, ['a', 'c']);
* // => { 'a': 1, 'c': 3 }
*/
var pick = flatRest(function(object, paths) {
return object == null ? {} : basePick(object, paths);
});
module.exports = pick;
/***/ }),
/* 43 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// Copyright Joyent, Inc. and other Node contributors.
//
// 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:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 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 R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
? R.apply
: function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
}
var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target)
.concat(Object.getOwnPropertySymbols(target));
};
} else {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target);
};
}
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
return value !== value;
}
function EventEmitter() {
EventEmitter.init.call(this);
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
}
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
}
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error');
var events = this._events;
if (events !== undefined)
doError = (doError && events.error === undefined);
else if (!doError)
return false;
// If there is no 'error' event listener then throw.
if (doError) {
var er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event
}
// At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
err.context = er;
throw err; // Unhandled 'error' event
}
var handler = events[type];
if (handler === undefined)
return false;
if (typeof handler === 'function') {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
checkListener(listener);
events = target._events;
if (events === undefined) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// Check for listener leak
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
// No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + String(type) + ' listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0)
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
checkListener(listener);
events = this._events;
if (events === undefined)
return this;
list = events[type];
if (list === undefined)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (events === undefined)
return this;
// not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (events === undefined)
return [];
var evlistener = events[type];
if (evlistener === undefined)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events !== undefined) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener !== undefined) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
/***/ }),
/* 44 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _undefined = __webpack_require__(466)(); // Support ES3 engines
module.exports = function (val) { return val !== _undefined && val !== null; };
/***/ }),
/* 45 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = __webpack_require__(480)()
? __webpack_require__(97).Symbol
: __webpack_require__(483);
/***/ }),
/* 46 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const performance = (typeof window !== undefined ? window : global).performance || {};
/**
* Returns the number of milliseconds since the the UNIX epoch
*
* When available (performance api exists), this function will not be skewed
* by clock adjustments. Only use if you require this functionality, otherwise
* use Date.now().
*
* @returns {number} Number of milliseconds since UNIX epoch
*/
module.exports = function highResolutionNow() {
if (performance.now) {
return performance.timing.navigationStart + performance.now();
}
return Date.now();
};
/***/ }),
/* 47 */
/***/ (function(module, exports, __webpack_require__) {
var before = __webpack_require__(560);
/**
* Creates a function that is restricted to invoking `func` once. Repeat calls
* to the function return the value of the first invocation. The `func` is
* invoked with the `this` binding and arguments of the created function.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Function
* @param {Function} func The function to restrict.
* @returns {Function} Returns the new restricted function.
* @example
*
* var initialize = _.once(createApplication);
* initialize();
* initialize();
* // => `createApplication` is invoked once
*/
function once(func) {
return before(2, func);
}
module.exports = once;
/***/ }),
/* 48 */
/***/ (function(module, exports) {
/**
* Performs a
* [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
* comparison between two values to determine if they are equivalent.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
* var object = { 'a': 1 };
* var other = { 'a': 1 };
*
* _.eq(object, object);
* // => true
*
* _.eq(object, other);
* // => false
*
* _.eq('a', 'a');
* // => true
*
* _.eq('a', Object('a'));
* // => false
*
* _.eq(NaN, NaN);
* // => true
*/
function eq(value, other) {
return value === other || (value !== value && other !== other);
}
module.exports = eq;
/***/ }),
/* 49 */
/***/ (function(module, exports) {
/**
* Copies the values of `source` to `array`.
*
* @private
* @param {Array} source The array to copy values from.
* @param {Array} [array=[]] The array to copy values to.
* @returns {Array} Returns `array`.
*/
function copyArray(source, array) {
var index = -1,
length = source.length;
array || (array = Array(length));
while (++index < length) {
array[index] = source[index];
}
return array;
}
module.exports = copyArray;
/***/ }),
/* 50 */
/***/ (function(module, exports, __webpack_require__) {
var baseClone = __webpack_require__(74);
/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
CLONE_SYMBOLS_FLAG = 4;
/**
* This method is like `_.clone` except that it recursively clones `value`.
*
* @static
* @memberOf _
* @since 1.0.0
* @category Lang
* @param {*} value The value to recursively clone.
* @returns {*} Returns the deep cloned value.
* @see _.clone
* @example
*
* var objects = [{ 'a': 1 }, { 'b': 2 }];
*
* var deep = _.cloneDeep(objects);
* console.log(deep[0] === objects[0]);
* // => false
*/
function cloneDeep(value) {
return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
}
module.exports = cloneDeep;
/***/ }),
/* 51 */
/***/ (function(module, exports, __webpack_require__) {
var baseGet = __webpack_require__(91);
/**
* Gets the value at `path` of `object`. If the resolved value is
* `undefined`, the `defaultValue` is returned in its place.
*
* @static
* @memberOf _
* @since 3.7.0
* @category Object
* @param {Object} object The object to query.
* @param {Array|string} path The path of the property to get.
* @param {*} [defaultValue] The value returned for `undefined` resolved values.
* @returns {*} Returns the resolved value.
* @example
*
* var object = { 'a': [{ 'b': { 'c': 3 } }] };
*
* _.get(object, 'a[0].b.c');
* // => 3
*
* _.get(object, ['a', '0', 'b', 'c']);
* // => 3
*
* _.get(object, 'a.b.c', 'default');
* // => 'default'
*/
function get(object, path, defaultValue) {
var result = object == null ? undefined : baseGet(object, path);
return result === undefined ? defaultValue : result;
}
module.exports = get;
/***/ }),
/* 52 */
/***/ (function(module, exports, __webpack_require__) {
var isArray = __webpack_require__(9),
isKey = __webpack_require__(125),
stringToPath = __webpack_require__(214),
toString = __webpack_require__(92);
/**
* Casts `value` to a path array if it's not one.
*
* @private
* @param {*} value The value to inspect.
* @param {Object} [object] The object to query keys on.
* @returns {Array} Returns the cast property path array.
*/
function castPath(value, object) {
if (isArray(value)) {
return value;
}
return isKey(value, object) ? [value] : stringToPath(toString(value));
}
module.exports = castPath;
/***/ }),
/* 53 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// ES3 safe
var _undefined = void 0;
module.exports = function (value) { return value !== _undefined && value !== null; };
/***/ }),
/* 54 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__(2);
var _extends2 = _interopRequireDefault(__webpack_require__(23));
var _createClass2 = _interopRequireDefault(__webpack_require__(40));
/* eslint-disable no-underscore-dangle */
const guidStorage = __webpack_require__(245);
const Analytics = __webpack_require__(238);
const SessionInfo = __webpack_require__(246);
const StaticConfig = __webpack_require__(28)();
const eventing = __webpack_require__(5);
const sanitizeQosData = __webpack_require__(526);
const _require = __webpack_require__(128),
prependProxyToUrlIfNeeded = _require.prependProxyToUrlIfNeeded;
const LOG_VERSION = '2';
let AnalyticsHelper = /*#__PURE__*/function () {
function AnalyticsHelper(_temp) {
let _ref = _temp === void 0 ? {} : _temp,
_ref$staticConfig = _ref.staticConfig,
staticConfig = _ref$staticConfig === void 0 ? StaticConfig.onlyLocal() : _ref$staticConfig,
_ref$sessionInfo = _ref.sessionInfo,
sessionInfo = _ref$sessionInfo === void 0 ? new SessionInfo() : _ref$sessionInfo,
ajax = _ref.ajax,
queue = _ref.queue,
proxyUrl = _ref.proxyUrl;
this.proxyUrl = proxyUrl;
this.ajax = ajax;
this.queue = queue;
this.sessionInfo = sessionInfo;
this.staticConfig = staticConfig;
}
var _proto = AnalyticsHelper.prototype;
_proto._getCommon = function _getCommon() {
return {
clientVersion: this.staticConfig.clientVersion,
buildHash: this.staticConfig.buildHash,
source: (typeof window !== undefined ? window : global).location && (typeof window !== undefined ? window : global).location.href,
logVersion: LOG_VERSION,
apiServer: this.staticConfig.apiUrl,
clientSystemTime: new Date().getTime(),
sessionId: this.sessionInfo.sessionId,
mediaServerName: this.sessionInfo.mediaServerName,
p2p: this.sessionInfo.p2pEnabled,
messagingServer: this.sessionInfo.messagingServer,
messagingUrl: this.sessionInfo.messagingURL,
version: this.staticConfig.version,
partnerId: this.sessionInfo.partnerId
};
};
_proto.logError = function logError(code, type, message, details, options) {
if (options === void 0) {
options = {};
}
guidStorage.get((error, guid) => {
if (error) {
// @todo
return;
}
const args = [code, type, message, details, (0, _extends2.default)({
guid
}, this.combineWithCommon(options))];
AnalyticsHelper.emit('logError', ...args);
this._analytics.logError(...args);
});
};
_proto.combineWithCommon = function combineWithCommon(options) {
return (0, _extends2.default)({}, this._getCommon(), {}, options);
};
_proto.logEvent = function logEvent(options, throttle, completionHandler) {
if (options === void 0) {
options = {};
}
guidStorage.get((error, guid) => {
if (error) {
// @todo
return;
}
const logData = (0, _extends2.default)({
guid
}, this.combineWithCommon(options));
AnalyticsHelper.emit('logEvent', logData);
this._analytics.logEvent(logData, false, throttle, completionHandler);
});
};
_proto.logQOS = function logQOS(options) {
if (options === void 0) {
options = {};
}
guidStorage.get((error, guid) => {
if (error) {
// @todo
return;
}
const qosData = (0, _extends2.default)({
guid,
duration: 0
}, this.combineWithCommon(options));
sanitizeQosData(qosData);
AnalyticsHelper.emit('logQOS', qosData);
this._analytics.logQOS(qosData);
});
};
(0, _createClass2.default)(AnalyticsHelper, [{
key: "staticConfig",
get: function get() {
return this._staticConfig;
},
set: function set(staticConfig) {
this._staticConfig = staticConfig;
this._analytics = new Analytics({
loggingUrl: prependProxyToUrlIfNeeded(this.staticConfig.loggingUrl, this.proxyUrl),
ajax: this.ajax,
queue: this.queue
});
}
}]);
return AnalyticsHelper;
}();
eventing(AnalyticsHelper);
module.exports = AnalyticsHelper;
/***/ }),
/* 55 */
/***/ (function(module, exports) {
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
module.exports = _inheritsLoose;
/***/ }),
/* 56 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* eslint-disable global-require */
const once = __webpack_require__(47);
const StaticConfig = __webpack_require__(28)(); // Indicates whether this client supports WebRTC
//
// This is defined as: getUserMedia + PeerConnection + exceeds min browser version
//
exports.check = function (deps) {
if (deps === void 0) {
deps = {};
}
const env = deps.env || __webpack_require__(3);
const hasPeerConnectionCapability = deps.hasPeerConnectionCapability || __webpack_require__(569);
const logging = deps.logging || __webpack_require__(1)('hasOpenTokSupport');
/** @type StaticConfig */
const staticConfig = deps.staticConfig || StaticConfig.onlyLocal();
const minimumVersions = staticConfig.minimumVersion || {};
const minimumVersion = minimumVersions[env.name.toLowerCase()];
if (minimumVersion && minimumVersion > env.version) {
logging.debug('Support for', env.name, 'is disabled because we require', minimumVersion, 'but this is', env.version);
return false;
}
if (env.name === 'Node') {
// Node works, even though it doesn't have getUserMedia
return true;
}
return hasPeerConnectionCapability();
};
exports.once = once(() => exports.check());
/***/ }),
/* 57 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-void, one-var, prefer-const, no-shadow, vars-on-top, no-var */
/* eslint-disable no-mixed-operators */
const assign = __webpack_require__(6);
const find = __webpack_require__(58);
const findIndex = __webpack_require__(265);
const logging = __webpack_require__(1)('SDPHelpers');
const START_MEDIA_SSRC = 10000;
const START_RTX_SSRC = 20000; // Here are the structure of the rtpmap attribute and the media line, most of the
// complex Regular Expressions in this code are matching against one of these two
// formats:
// * a=rtpmap:
*
*
* name
Description
*
*
*
* 'OT_MEDIA_ERR_ABORTED'
The fetching of an audio stream has been aborted.
*
*
*
* 'OT_MEDIA_ERR_NETWORK'
A network error caused a stream to stop being fetched.
*
*
*
* 'OT_MEDIA_ERR_DECODE'
A decoding error occurred while trying to play a stream.
*
*
*
* 'OT_MEDIA_ERR_SRC_NOT_SUPPORTED'
A stream has been detected to be not suitable for playback.
* global.setTimeout
.
*
* @param {function()} callback
* @param {number} frequency how many times per second we want to execute the callback
* @param {number} maxIterations the maximum amount of iterations for this runner
* @constructor
*/
function IntervalRunner(callback, desiredFrequency, maxIterations) {
if (maxIterations === void 0) {
maxIterations = Infinity;
}
let timeoutId = null;
let running = false;
let iteration = 1;
const timeBetween = 1000 / desiredFrequency;
const loop = () => {
if (!running || iteration > maxIterations) {
return;
}
iteration += 1;
callback();
timeoutId = setTimeout(loop, timeBetween);
};
this.start = () => {
if (running) {
return;
}
running = true;
setTimeout(loop, timeBetween);
};
this.stop = () => {
(typeof window !== undefined ? window : global).clearTimeout(timeoutId);
running = false;
};
}
module.exports = IntervalRunner;
/***/ }),
/* 72 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const pick = __webpack_require__(42);
const mapValues = __webpack_require__(620);
const isObject = __webpack_require__(7);
const frameRateTrackers = {};
function getFrameRate(stat) {
return Number(stat.framerateMean || stat.googFrameRateSent || stat.googFrameRateReceived || stat.googFrameRateInput || stat.googFrameRateOutput || 0);
}
function getFrames(stat) {
return Number(stat.framesEncoded || stat.framesDecoded);
}
function calcFrameRate(stat, startTime) {
if (getFrameRate(stat)) {
return getFrameRate(stat);
}
if (!getFrames(stat)) {
return undefined;
}
let frameRate = 0;
if (frameRateTrackers[stat.id] !== undefined) {
frameRate = (getFrames(stat) - frameRateTrackers[stat.id].frames) / ((stat.timestamp - frameRateTrackers[stat.id].timestamp) / 1000);
} else {
frameRate = getFrames(stat) / ((stat.timestamp - startTime) / 1000);
}
frameRateTrackers[stat.id] = {
frames: getFrames(stat),
timestamp: stat.timestamp
};
return Math.round(frameRate * 100) / 100;
}
function getLinkedTrack(stat, allStats) {
if (!allStats) {
return {};
}
const linkedTrack = allStats.filter(x => x.id === stat.mediaTrackId);
return linkedTrack.length ? linkedTrack[0] : {};
}
const getStatsHelpers = {
isVideoStat(stat, allStats) {
const linkedTrack = getLinkedTrack(stat, allStats);
return stat.mediaType === 'video' || // Chrome / Firefox
'googFrameWidthReceived' in stat || // Old Chrome
'googFrameWidthInput' in stat || // Old Chrome
stat.type === 'inbound-rtp' && stat.id.indexOf('Video') !== -1 || // Safari
stat.type === 'inboundrtp' && linkedTrack.frameWidth !== 0; // Edge
},
isVideoSenderStat(stat) {
// Chrome implementation only has this property for RTP video stat
return 'googFrameWidthSent' in stat || stat.mediaType === 'video' || stat.id !== undefined && stat.id.toLowerCase().indexOf('video') !== -1;
},
isAudioStat(stat, allStats) {
const linkedTrack = getLinkedTrack(stat, allStats);
return stat.mediaType === 'audio' || // Chrome / Firefox
'audioInputLevel' in stat || // Old Chrome
'audioOutputLevel' in stat || // Old Chrome
stat.type === 'inbound-rtp' && stat.id.indexOf('Audio') !== -1 || // Safari
stat.type === 'inboundrtp' && linkedTrack.frameWidth === 0; // Edge;
},
isInboundStat(stat) {
return 'bytesReceived' in stat || 'packetsReceived' in stat;
},
isOutboundStat(stat) {
return 'bytesSent' in stat || 'packetsSent' in stat;
},
isVideoTrackStat(stat) {
return stat.type === 'track' && (stat.frameHeight > 0 || stat.frameWidth > 0 || stat.framesCorrupted > 0 || stat.framesDecoded > 0 || stat.framesPerSecond > 0 || stat.framesSent > 0 || stat.framesReceived > 0);
},
parseStatCategory(stat) {
const statCategory = {
packetsLost: 0,
packetsReceived: 0,
bytesReceived: 0 // frameRate is only set for video stat
};
if ('packetsReceived' in stat) {
statCategory.packetsReceived = parseInt(stat.packetsReceived, 10);
}
if ('packetsLost' in stat) {
statCategory.packetsLost = parseInt(stat.packetsLost, 10);
}
if ('bytesReceived' in stat) {
statCategory.bytesReceived = parseInt(stat.bytesReceived, 10);
}
if (this.isVideoStat(stat)) {
if ('framerateMean' in stat) {
statCategory.frameRate = Number(stat.framerateMean);
} else if ('googFrameRateOutput' in stat) {
statCategory.frameRate = Number(stat.googFrameRateOutput);
} else if ('googFrameRateInput' in stat) {
statCategory.frameRate = Number(stat.googFrameRateInput);
}
}
return statCategory;
},
normalizeTimestamp(timestamp, now) {
if (now === void 0) {
now = Date.now();
}
if (isObject(timestamp) && 'getTime' in timestamp) {
// Chrome as of 39 delivers a "kind of Date" object for timestamps
// we duck check it and get the timestamp
return timestamp.getTime();
}
if (Math.abs(timestamp / (1000 * now) - 1) < 0.05) {
// If the timestamp is within 5% of 1000x now, we assume its unit is microseconds and
// divide by 1000 to correct for this.
return timestamp / 1000;
}
return timestamp;
},
normalizeStats(stats, inbound, startTime) {
if (inbound === void 0) {
inbound = true;
}
const video = stats.filter(stat => getStatsHelpers.isVideoStat(stat, stats)).filter(stat => getStatsHelpers.isInboundStat(stat) === inbound)[0];
const audio = stats.filter(stat => getStatsHelpers.isAudioStat(stat, stats)).filter(stat => getStatsHelpers.isInboundStat(stat) === inbound)[0];
if (!audio && !video) {
throw new Error('could not find stats for either audio or video');
}
const otStats = {
timestamp: getStatsHelpers.normalizeTimestamp(video ? video.timestamp : audio.timestamp)
};
const getOutboundStats = stat => pick(stat, ['packetsSent', 'packetsLost', 'bytesSent']);
const getInboundStats = stat => pick(stat, ['packetsReceived', 'packetsLost', 'bytesReceived']);
const getCommonStats = inbound ? getInboundStats : getOutboundStats;
if (video) {
otStats.video = Object.assign({
frameRate: calcFrameRate(video, startTime)
}, mapValues(getCommonStats(video), Number));
}
if (audio) {
otStats.audio = mapValues(getCommonStats(audio), Number);
}
return otStats;
}
};
module.exports = getStatsHelpers;
/***/ }),
/* 73 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
exports.__esModule = true;
exports.codes = exports.messages = void 0;
// https://tools.ietf.org/html/rfc6455#section-7.4.1
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Close_codes
const messages = {
1001: 'The endpoint is going away, either because of a server failure or because the browser ' + 'is navigating away from the page that opened the connection.',
1002: 'The endpoint is terminating the connection due to a protocol error. ' + '(CLOSE_PROTOCOL_ERROR)',
1003: 'The connection is being terminated because the endpoint has indicated ' + 'that reconnections are not available. (CLOSE_UNSUPPORTED)',
1005: 'Indicates that no status code was provided even though one was expected. ' + '(CLOSE_NO_STATUS)',
1006: 'Used to indicate that a connection was closed abnormally (that is, with no ' + 'close frame being sent) when a status code is expected. (CLOSE_ABNORMAL)',
1007: 'Indicates that an endpoint is terminating the connection because it has received ' + 'data within a message that was not consistent with the type of the message (e.g., ' + 'non-UTF-8 [RFC3629] data within a text message)',
1008: 'Indicates that an endpoint is terminating the connection because it has received a ' + 'message that violates its policy. This is a generic status code that can be returned ' + 'when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a ' + 'need to hide specific details about the policy',
1009: 'Indicates that an endpoint is terminating the connection because it has received a ' + 'message that is too big for it to process (CLOSE_TOO_LARGE)',
1011: 'Indicates that a server is terminating the connection because it encountered an ' + 'unexpected condition that prevented it from fulfilling the request',
// ... codes in the 4000-4999 range are available for use by applications.
4001: 'Connectivity loss was detected as it was too long since the socket received the ' + 'last PONG message',
4010: 'Timed out while waiting for the Rumor socket to connect.',
4020: 'Error code unavailable.',
4030: 'Exception was thrown during Rumor connection, possibly because of a blocked port.'
};
exports.messages = messages;
const codes = {
CLOSE_NORMAL: 1000,
CLOSE_GOING_AWAY: 1001,
CLOSE_PROTOCOL_ERROR: 1002,
CLOSE_UNSUPPORTED: 1003,
CLOSE_NO_STATUS: 1005,
CLOSE_ABNORMAL: 1006,
CLOSE_INCONSISTENT_DATA: 1007,
CLOSE_POLICY_VIOLATION: 1008,
CLOSE_TOO_LARGE: 1009,
CLOSE_UNEXPECTED_CONDITION: 1011,
CLOSE_CONNECTIVITY_LOSS: 4001,
CLOSE_TIMEOUT: 4010,
CLOSE_FALLBACK_CODE: 4020,
CLOSE_CONNECT_EXCEPTION: 4030,
ALREADY_CONNECTED_CONNECTING: null
};
exports.codes = codes;
/***/ }),
/* 74 */
/***/ (function(module, exports, __webpack_require__) {
var Stack = __webpack_require__(75),
arrayEach = __webpack_require__(108),
assignValue = __webpack_require__(80),
baseAssign = __webpack_require__(171),
baseAssignIn = __webpack_require__(360),
cloneBuffer = __webpack_require__(174),
copyArray = __webpack_require__(49),
copySymbols = __webpack_require__(363),
copySymbolsIn = __webpack_require__(365),
getAllKeys = __webpack_require__(177),
getAllKeysIn = __webpack_require__(179),
getTag = __webpack_require__(39),
initCloneArray = __webpack_require__(368),
initCloneByTag = __webpack_require__(369),
initCloneObject = __webpack_require__(184),
isArray = __webpack_require__(9),
isBuffer = __webpack_require__(62),
isMap = __webpack_require__(373),
isObject = __webpack_require__(7),
isSet = __webpack_require__(375),
keys = __webpack_require__(27);
/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
CLONE_FLAT_FLAG = 2,
CLONE_SYMBOLS_FLAG = 4;
/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
arrayTag = '[object Array]',
boolTag = '[object Boolean]',
dateTag = '[object Date]',
errorTag = '[object Error]',
funcTag = '[object Function]',
genTag = '[object GeneratorFunction]',
mapTag = '[object Map]',
numberTag = '[object Number]',
objectTag = '[object Object]',
regexpTag = '[object RegExp]',
setTag = '[object Set]',
stringTag = '[object String]',
symbolTag = '[object Symbol]',
weakMapTag = '[object WeakMap]';
var arrayBufferTag = '[object ArrayBuffer]',
dataViewTag = '[object DataView]',
float32Tag = '[object Float32Array]',
float64Tag = '[object Float64Array]',
int8Tag = '[object Int8Array]',
int16Tag = '[object Int16Array]',
int32Tag = '[object Int32Array]',
uint8Tag = '[object Uint8Array]',
uint8ClampedTag = '[object Uint8ClampedArray]',
uint16Tag = '[object Uint16Array]',
uint32Tag = '[object Uint32Array]';
/** Used to identify `toStringTag` values supported by `_.clone`. */
var cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[funcTag] =
cloneableTags[weakMapTag] = false;
/**
* The base implementation of `_.clone` and `_.cloneDeep` which tracks
* traversed objects.
*
* @private
* @param {*} value The value to clone.
* @param {boolean} bitmask The bitmask flags.
* 1 - Deep clone
* 2 - Flatten inherited properties
* 4 - Clone symbols
* @param {Function} [customizer] The function to customize cloning.
* @param {string} [key] The key of `value`.
* @param {Object} [object] The parent object of `value`.
* @param {Object} [stack] Tracks traversed objects and their clone counterparts.
* @returns {*} Returns the cloned value.
*/
function baseClone(value, bitmask, customizer, key, object, stack) {
var result,
isDeep = bitmask & CLONE_DEEP_FLAG,
isFlat = bitmask & CLONE_FLAT_FLAG,
isFull = bitmask & CLONE_SYMBOLS_FLAG;
if (customizer) {
result = object ? customizer(value, key, object, stack) : customizer(value);
}
if (result !== undefined) {
return result;
}
if (!isObject(value)) {
return value;
}
var isArr = isArray(value);
if (isArr) {
result = initCloneArray(value);
if (!isDeep) {
return copyArray(value, result);
}
} else {
var tag = getTag(value),
isFunc = tag == funcTag || tag == genTag;
if (isBuffer(value)) {
return cloneBuffer(value, isDeep);
}
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
result = (isFlat || isFunc) ? {} : initCloneObject(value);
if (!isDeep) {
return isFlat
? copySymbolsIn(value, baseAssignIn(result, value))
: copySymbols(value, baseAssign(result, value));
}
} else {
if (!cloneableTags[tag]) {
return object ? value : {};
}
result = initCloneByTag(value, tag, isDeep);
}
}
// Check for circular references and return its corresponding clone.
stack || (stack = new Stack);
var stacked = stack.get(value);
if (stacked) {
return stacked;
}
stack.set(value, result);
if (isSet(value)) {
value.forEach(function(subValue) {
result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
});
} else if (isMap(value)) {
value.forEach(function(subValue, key) {
result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
});
}
var keysFunc = isFull
? (isFlat ? getAllKeysIn : getAllKeys)
: (isFlat ? keysIn : keys);
var props = isArr ? undefined : keysFunc(value);
arrayEach(props || value, function(subValue, key) {
if (props) {
key = subValue;
subValue = value[key];
}
// Recursively populate clone (susceptible to call stack limits).
assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
});
return result;
}
module.exports = baseClone;
/***/ }),
/* 75 */
/***/ (function(module, exports, __webpack_require__) {
var ListCache = __webpack_require__(76),
stackClear = __webpack_require__(332),
stackDelete = __webpack_require__(333),
stackGet = __webpack_require__(334),
stackHas = __webpack_require__(335),
stackSet = __webpack_require__(336);
/**
* Creates a stack cache object to store key-value pairs.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
function Stack(entries) {
var data = this.__data__ = new ListCache(entries);
this.size = data.size;
}
// Add methods to `Stack`.
Stack.prototype.clear = stackClear;
Stack.prototype['delete'] = stackDelete;
Stack.prototype.get = stackGet;
Stack.prototype.has = stackHas;
Stack.prototype.set = stackSet;
module.exports = Stack;
/***/ }),
/* 76 */
/***/ (function(module, exports, __webpack_require__) {
var listCacheClear = __webpack_require__(327),
listCacheDelete = __webpack_require__(328),
listCacheGet = __webpack_require__(329),
listCacheHas = __webpack_require__(330),
listCacheSet = __webpack_require__(331);
/**
* Creates an list cache object.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
function ListCache(entries) {
var index = -1,
length = entries == null ? 0 : entries.length;
this.clear();
while (++index < length) {
var entry = entries[index];
this.set(entry[0], entry[1]);
}
}
// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;
module.exports = ListCache;
/***/ }),
/* 77 */
/***/ (function(module, exports, __webpack_require__) {
var eq = __webpack_require__(48);
/**
* Gets the index at which the `key` is found in `array` of key-value pairs.
*
* @private
* @param {Array} array The array to inspect.
* @param {*} key The key to search for.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function assocIndexOf(array, key) {
var length = array.length;
while (length--) {
if (eq(array[length][0], key)) {
return length;
}
}
return -1;
}
module.exports = assocIndexOf;
/***/ }),
/* 78 */
/***/ (function(module, exports, __webpack_require__) {
var getNative = __webpack_require__(36);
/* Built-in method references that are verified to be native. */
var nativeCreate = getNative(Object, 'create');
module.exports = nativeCreate;
/***/ }),
/* 79 */
/***/ (function(module, exports, __webpack_require__) {
var isKeyable = __webpack_require__(351);
/**
* Gets the data for `map`.
*
* @private
* @param {Object} map The map to query.
* @param {string} key The reference key.
* @returns {*} Returns the map data.
*/
function getMapData(map, key) {
var data = map.__data__;
return isKeyable(key)
? data[typeof key == 'string' ? 'string' : 'hash']
: data.map;
}
module.exports = getMapData;
/***/ }),
/* 80 */
/***/ (function(module, exports, __webpack_require__) {
var baseAssignValue = __webpack_require__(60),
eq = __webpack_require__(48);
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Assigns `value` to `key` of `object` if the existing value is not equivalent
* using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
* for equality comparisons.
*
* @private
* @param {Object} object The object to modify.
* @param {string} key The key of the property to assign.
* @param {*} value The value to assign.
*/
function assignValue(object, key, value) {
var objValue = object[key];
if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
(value === undefined && !(key in object))) {
baseAssignValue(object, key, value);
}
}
module.exports = assignValue;
/***/ }),
/* 81 */
/***/ (function(module, exports) {
module.exports = function(module) {
if (!module.webpackPolyfill) {
module.deprecate = function() {};
module.paths = [];
// module.parent = undefined by default
if (!module.children) module.children = [];
Object.defineProperty(module, "loaded", {
enumerable: true,
get: function() {
return module.l;
}
});
Object.defineProperty(module, "id", {
enumerable: true,
get: function() {
return module.i;
}
});
module.webpackPolyfill = 1;
}
return module;
};
/***/ }),
/* 82 */
/***/ (function(module, exports, __webpack_require__) {
var baseIsTypedArray = __webpack_require__(358),
baseUnary = __webpack_require__(64),
nodeUtil = __webpack_require__(110);
/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
/**
* Checks if `value` is classified as a typed array.
*
* @static
* @memberOf _
* @since 3.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
* @example
*
* _.isTypedArray(new Uint8Array);
* // => true
*
* _.isTypedArray([]);
* // => false
*/
var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
module.exports = isTypedArray;
/***/ }),
/* 83 */
/***/ (function(module, exports, __webpack_require__) {
var isObject = __webpack_require__(7);
/** Built-in value references. */
var objectCreate = Object.create;
/**
* The base implementation of `_.create` without support for assigning
* properties to the created object.
*
* @private
* @param {Object} proto The object to inherit from.
* @returns {Object} Returns the new object.
*/
var baseCreate = (function() {
function object() {}
return function(proto) {
if (!isObject(proto)) {
return {};
}
if (objectCreate) {
return objectCreate(proto);
}
object.prototype = proto;
var result = new object;
object.prototype = undefined;
return result;
};
}());
module.exports = baseCreate;
/***/ }),
/* 84 */
/***/ (function(module, exports) {
/**
* This method returns the first argument it receives.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Util
* @param {*} value Any value.
* @returns {*} Returns `value`.
* @example
*
* var object = { 'a': 1 };
*
* console.log(_.identity(object) === object);
* // => true
*/
function identity(value) {
return value;
}
module.exports = identity;
/***/ }),
/* 85 */
/***/ (function(module, exports, __webpack_require__) {
var baseCreate = __webpack_require__(83),
isObject = __webpack_require__(7);
/**
* Creates a function that produces an instance of `Ctor` regardless of
* whether it was invoked as part of a `new` expression or by `call` or `apply`.
*
* @private
* @param {Function} Ctor The constructor to wrap.
* @returns {Function} Returns the new wrapped function.
*/
function createCtor(Ctor) {
return function() {
// Use a `switch` statement to work with class constructors. See
// http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
// for more details.
var args = arguments;
switch (args.length) {
case 0: return new Ctor;
case 1: return new Ctor(args[0]);
case 2: return new Ctor(args[0], args[1]);
case 3: return new Ctor(args[0], args[1], args[2]);
case 4: return new Ctor(args[0], args[1], args[2], args[3]);
case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
}
var thisBinding = baseCreate(Ctor.prototype),
result = Ctor.apply(thisBinding, args);
// Mimic the constructor's `return` behavior.
// See https://es5.github.io/#x13.2.2 for more details.
return isObject(result) ? result : thisBinding;
};
}
module.exports = createCtor;
/***/ }),
/* 86 */
/***/ (function(module, exports, __webpack_require__) {
var baseIndexOf = __webpack_require__(412);
/**
* A specialized version of `_.includes` for arrays without support for
* specifying an index to search from.
*
* @private
* @param {Array} [array] The array to inspect.
* @param {*} target The value to search for.
* @returns {boolean} Returns `true` if `target` is found, else `false`.
*/
function arrayIncludes(array, value) {
var length = array == null ? 0 : array.length;
return !!length && baseIndexOf(array, value, 0) > -1;
}
module.exports = arrayIncludes;
/***/ }),
/* 87 */
/***/ (function(module, exports, __webpack_require__) {
var toFinite = __webpack_require__(418);
/**
* Converts `value` to an integer.
*
* **Note:** This method is loosely based on
* [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to convert.
* @returns {number} Returns the converted integer.
* @example
*
* _.toInteger(3.2);
* // => 3
*
* _.toInteger(Number.MIN_VALUE);
* // => 0
*
* _.toInteger(Infinity);
* // => 1.7976931348623157e+308
*
* _.toInteger('3.2');
* // => 3
*/
function toInteger(value) {
var result = toFinite(value),
remainder = result % 1;
return result === result ? (remainder ? result - remainder : result) : 0;
}
module.exports = toInteger;
/***/ }),
/* 88 */
/***/ (function(module, exports, __webpack_require__) {
var MapCache = __webpack_require__(107),
setCacheAdd = __webpack_require__(426),
setCacheHas = __webpack_require__(427);
/**
*
* Creates an array cache object to store unique values.
*
* @private
* @constructor
* @param {Array} [values] The values to cache.
*/
function SetCache(values) {
var index = -1,
length = values == null ? 0 : values.length;
this.__data__ = new MapCache;
while (++index < length) {
this.add(values[index]);
}
}
// Add methods to `SetCache`.
SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
SetCache.prototype.has = setCacheHas;
module.exports = SetCache;
/***/ }),
/* 89 */
/***/ (function(module, exports) {
/**
* Checks if a `cache` value for `key` exists.
*
* @private
* @param {Object} cache The cache to query.
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
function cacheHas(cache, key) {
return cache.has(key);
}
module.exports = cacheHas;
/***/ }),
/* 90 */
/***/ (function(module, exports) {
/**
* Converts `set` to an array of its values.
*
* @private
* @param {Object} set The set to convert.
* @returns {Array} Returns the values.
*/
function setToArray(set) {
var index = -1,
result = Array(set.size);
set.forEach(function(value) {
result[++index] = value;
});
return result;
}
module.exports = setToArray;
/***/ }),
/* 91 */
/***/ (function(module, exports, __webpack_require__) {
var castPath = __webpack_require__(52),
toKey = __webpack_require__(41);
/**
* The base implementation of `_.get` without support for default values.
*
* @private
* @param {Object} object The object to query.
* @param {Array|string} path The path of the property to get.
* @returns {*} Returns the resolved value.
*/
function baseGet(object, path) {
path = castPath(path, object);
var index = 0,
length = path.length;
while (object != null && index < length) {
object = object[toKey(path[index++])];
}
return (index && index == length) ? object : undefined;
}
module.exports = baseGet;
/***/ }),
/* 92 */
/***/ (function(module, exports, __webpack_require__) {
var baseToString = __webpack_require__(433);
/**
* Converts `value` to a string. An empty string is returned for `null`
* and `undefined` values. The sign of `-0` is preserved.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to convert.
* @returns {string} Returns the converted string.
* @example
*
* _.toString(null);
* // => ''
*
* _.toString(-0);
* // => '-0'
*
* _.toString([1, 2, 3]);
* // => '1,2,3'
*/
function toString(value) {
return value == null ? '' : baseToString(value);
}
module.exports = toString;
/***/ }),
/* 93 */
/***/ (function(module, exports, __webpack_require__) {
var baseFor = __webpack_require__(219),
keys = __webpack_require__(27);
/**
* The base implementation of `_.forOwn` without support for iteratee shorthands.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Object} Returns `object`.
*/
function baseForOwn(object, iteratee) {
return object && baseFor(object, iteratee, keys);
}
module.exports = baseForOwn;
/***/ }),
/* 94 */
/***/ (function(module, exports, __webpack_require__) {
var identity = __webpack_require__(84),
overRest = __webpack_require__(218),
setToString = __webpack_require__(122);
/**
* The base implementation of `_.rest` which doesn't validate or coerce arguments.
*
* @private
* @param {Function} func The function to apply a rest parameter to.
* @param {number} [start=func.length-1] The start position of the rest parameter.
* @returns {Function} Returns the new function.
*/
function baseRest(func, start) {
return setToString(overRest(func, start, identity), func + '');
}
module.exports = baseRest;
/***/ }),
/* 95 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var objToString = Object.prototype.toString
, id = objToString.call((function () { return arguments; })());
module.exports = function (value) { return objToString.call(value) === id; };
/***/ }),
/* 96 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var objToString = Object.prototype.toString, id = objToString.call("");
module.exports = function (value) {
return (
typeof value === "string" ||
(value &&
typeof value === "object" &&
(value instanceof String || objToString.call(value) === id)) ||
false
);
};
/***/ }),
/* 97 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = __webpack_require__(481)() ? globalThis : __webpack_require__(482);
/***/ }),
/* 98 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
exports.adaptIceServers = function adaptIceServers(iceServers) {
return iceServers.map(iceServer => ({
url: iceServer.url,
// deprecated
urls: iceServer.urls || [iceServer.url],
username: iceServer.username,
credential: iceServer.credential
}));
};
exports.parseIceServers = function parseIceServers(message) {
let iceServers;
try {
iceServers = JSON.parse(message.data).content.iceServers;
} catch (e) {
return [];
}
return exports.adaptIceServers(iceServers);
};
/***/ }),
/* 99 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(Promise) {
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable one-var, no-underscore-dangle, no-use-before-define, no-param-reassign */
/* eslint-disable max-len, no-var, vars-on-top, global-require */
const promiseTimeout = __webpack_require__(149);
const now = __webpack_require__(46);
const assign = __webpack_require__(6);
const cloneDeep = __webpack_require__(50);
const intersection = __webpack_require__(244);
const merge = __webpack_require__(143);
const omit = __webpack_require__(263);
const uuid = __webpack_require__(15);
const eventing = __webpack_require__(5);
const OTHelpersError = __webpack_require__(242);
const createCleanupJobs = __webpack_require__(150);
const SDPHelpers = __webpack_require__(57);
module.exports = function PeerConnectionFactory(deps) {
if (deps === void 0) {
deps = {};
}
const hasValidPeerConnection = deps.hasValidPeerConnection || __webpack_require__(582);
const applySdpTransform = deps.applySdpTransform || __webpack_require__(151);
const createPeerConnection = deps.createPeerConnection || __webpack_require__(585);
const env = deps.env || __webpack_require__(3);
const sdpTransformDefaults = deps.sdpTransformDefaults || __webpack_require__(587);
const getStatsAdapter = deps.getStatsAdapter || __webpack_require__(266);
const IceCandidateProcessor = deps.IceCandidateProcessor || __webpack_require__(592);
const createLog = deps.logging || __webpack_require__(1);
const offerProcessor = deps.offerProcessor || __webpack_require__(594);
const PeerConnectionChannels = deps.PeerConnectionChannels || __webpack_require__(596);
const subscribeProcessor = deps.subscribeProcessor || __webpack_require__(598);
const Qos = deps.Qos || __webpack_require__(268).default;
const windowMock = deps.global || (typeof window !== undefined ? window : global);
const debounce = deps.debounce || __webpack_require__(602);
const needsToSwapH264Profiles = deps.needsToSwapH264Profiles || __webpack_require__(604).once;
const futureIsPeerConnectionValid = hasValidPeerConnection(windowMock.RTCPeerConnection);
const NativeRTCIceCandidate = deps.NativeRTCIceCandidate || windowMock.RTCIceCandidate;
const NativeRTCSessionDescription = deps.NativeRTCSessionDescription || windowMock.RTCSessionDescription; // Helper function to forward Ice Candidates via +sendMessage+
const iceCandidateForwarder = (_ref) => {
let sendMessage = _ref.sendMessage,
logging = _ref.logging;
const sdpMids = {};
return event => {
if (event.candidate) {
// It would be better to read the mids from the SDP
sdpMids[event.candidate.sdpMid] = event.candidate.sdpMLineIndex;
sendMessage('candidate', {
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid || '',
sdpMLineIndex: event.candidate.sdpMLineIndex || 0
});
} else {
logging.debug('IceCandidateForwarder: No more ICE candidates.');
}
};
};
const noop = () => {};
/*
* Negotiates a WebRTC PeerConnection.
*
* Responsible for:
* * offer-answer exchange
* * iceCandidates
* * notification of remote streams being added/removed
*
*/
return function PeerConnection(options) {
if (options === void 0) {
options = {};
}
let hasRelayCandidates = false;
const _options = options,
_options$iceConfig = _options.iceConfig,
iceConfig = _options$iceConfig === void 0 ? {
servers: []
} : _options$iceConfig,
isPublisher = _options.isPublisher,
_options$logAnalytics = _options.logAnalyticsEvent,
logAnalyticsEvent = _options$logAnalytics === void 0 ? noop : _options$logAnalytics,
offerOverrides = _options.offerOverrides,
answerOverrides = _options.answerOverrides,
sdpTransformOptions = _options.sdpTransformOptions,
originalSendMessage = _options.sendMessage,
p2p = _options.p2p,
codecFlags = _options.codecFlags;
let offerAnswerPending = false; // Whether we have an offer out and are waiting for an answer
let regenerateOffer = false;
let processingOffer = false; // Whether we are currently processing an offer
let pendingOfferMessage; // An offer we received that is pending waiting on a previous offer
const replaceBaselineProfile = needsToSwapH264Profiles();
let offerMessagesReceived = 0; // number of offer or generateoffer messages received
let renegotiationInProgress = false;
const cleanupJobs = createCleanupJobs();
function sendMessage(type, payload) {
if (!hasRelayCandidates) {
const shouldCheckForRelayCandidates = ['candidate', 'offer', 'answer', 'pranswer'].indexOf(type) > -1;
if (shouldCheckForRelayCandidates) {
const message = type === 'candidate' ? payload.candidate : payload.sdp;
hasRelayCandidates = message.indexOf('typ relay') !== -1;
}
}
originalSendMessage(type, payload);
}
const startConnectingTime = now();
logAnalyticsEvent('createPeerConnection', 'Attempt');
const api = {};
api.id = uuid();
const logging = createLog("PeerConnection:" + api.id);
logging.debug('construct', {
id: api.id,
options
});
const sdpTransforms = merge({}, sdpTransformDefaults, sdpTransformOptions);
const shouldFilterCandidate = candidate => iceConfig.transportPolicy === 'relay' && candidate != null && candidate.candidate.indexOf('typ relay') === -1;
const config = omit(options, ['isPublisher', 'logAnalyticsEvent', 'offerOverrides', 'answerOverrides', 'sendMessage', 'sdpTransformOptions']);
let _peerConnection, _channels, _offer, _answer;
let _peerConnectionCompletionHandlers = [];
const _simulcastEnabled = (() => {
let value = config.overrideSimulcastEnabled || false;
return {
get() {
return value;
},
set(newValueParam) {
const newValue = Boolean(newValueParam) && config.capableSimulcastStreams > 1;
if (newValue !== value && config.overrideSimulcastEnabled === undefined) {
value = newValue;
api.generateOffer();
}
}
};
})();
const _iceRestartNeeded = (() => {
let value = false;
return {
get() {
return value;
},
set(newValueParam) {
const newValue = Boolean(newValueParam);
if (newValue !== value) {
value = newValue;
if (value && !api.iceConnectionStateIsConnected()) {
api.generateOffer();
} else {
logging.debug('iceRestart is going to wait until we disconnect and then restart ice');
}
}
}
};
})();
const _getStatsAdapter = getStatsAdapter(); // OPENTOK-27106: This _readyToSendOffer mechanism is a workaround for a P2P IE->FF issue where
// ice candidates sometimes take an excessive amount of time (~10 seconds) to be generated by the
// IE plugin. FF will time out if there is a delay like this between receiving the offer and
// receiving ice candidates, so the workaround holds back sending the offer until an ice candidate
// appears.
const _readyToSendOffer = {
clean() {
delete this.promise;
delete this.resolve;
delete this.reject;
}
};
_readyToSendOffer.promise = new Promise((resolve, reject) => {
_readyToSendOffer.resolve = resolve;
_readyToSendOffer.reject = reject;
});
let _iceProcessor = new IceCandidateProcessor();
let _state = 'new';
Object.defineProperty(api, 'signalingState', {
get() {
return _peerConnection.signalingState;
},
set(val) {
// obviously they should not be doing this, but we'll mimic what the browser does.
_peerConnection.signalingState = val;
return val;
}
});
eventing(api);
api.once('iceConnected', () => {
const proxyInfo = '';
const payload = {
pcc: parseInt(now() - startConnectingTime, 10),
hasRelayCandidates,
proxyInfo
};
if (_peerConnection && _peerConnection.proxyInfo) payload.proxyInfo = _peerConnection.proxyInfo;
logAnalyticsEvent('createPeerConnection', 'Success', payload);
});
_readyToSendOffer.resolve(); // Create and initialise the PeerConnection object. This deals with
// any differences between the various browser implementations and
// our own OTPlugin version.
//
// +completion+ is the function is call once we've either successfully
// created the PeerConnection or on failure.
//
const internalCreatePeerConnection = completion => {
logging.debug('internalCreatePeerConnection: called');
if (_peerConnection) {
logging.debug('internalCreatePeerConnection: resolving synchronously');
completion.call(null, null, _peerConnection);
return;
}
_peerConnectionCompletionHandlers.push(completion);
if (_peerConnectionCompletionHandlers.length > 1) {
// The PeerConnection is already being setup, just wait for
// it to be ready.
return;
}
const pcConstraints = {
optional: [// This should be unnecessary, but the plugin has issues if we remove it. This needs
// to be investigated.
// @todo now plugin is gone, can we remove it?
{
DtlsSrtpKeyAgreement: true
}]
};
logging.debug("Creating peer connection config \"" + JSON.stringify(config) + "\".");
if (iceConfig.servers.length === 0) {
// This should never happen unless something is misconfigured
logging.error('No ice servers present');
logAnalyticsEvent('Error', 'noIceServers');
}
if (iceConfig.transportPolicy === 'relay') {
const isTurnUrl = url => url && url.indexOf('turn') === 0;
iceConfig.servers = iceConfig.servers.map(providedServer => {
const server = cloneDeep(providedServer);
if (!Array.isArray(server.urls)) {
server.urls = [server.urls];
}
server.urls = server.urls.filter(isTurnUrl);
return server.urls.length === 0 ? undefined : server;
}).filter(server => server !== undefined);
}
config.iceTransportPolicy = iceConfig.transportPolicy;
config.iceServers = iceConfig.servers;
futureIsPeerConnectionValid.then(isValid => {
if (!isValid) {
logging.error('createPeerConnection: Invalid RTCPeerConnection object');
throw new Error('Failed to create valid RTCPeerConnection object');
}
return createPeerConnection({
window: windowMock,
config,
constraints: pcConstraints
});
}).then(pc => attachEventsToPeerConnection(null, pc), err => attachEventsToPeerConnection(err));
}; // An auxiliary function to internalCreatePeerConnection. This binds the various event
// callbacks once the peer connection is created.
//
// +err+ will be non-null if an err occured while creating the PeerConnection
// +pc+ will be the PeerConnection object itself.
//
// @todo if anything called in attachEventsToPeerConnection throws it will be
// silent
const attachEventsToPeerConnection = (err, pc) => {
if (err) {
triggerError({
reason: "Failed to create PeerConnection, exception: " + err,
prefix: 'NewPeerConnection'
});
_peerConnectionCompletionHandlers = [];
return;
}
logging.debug('OT attachEventsToPeerConnection');
_peerConnection = pc;
if (_iceProcessor) {
_iceProcessor.setPeerConnection(pc);
}
_channels = new PeerConnectionChannels(_peerConnection);
if (config.channels) {
_channels.addMany(config.channels);
}
const forwarder = iceCandidateForwarder({
sendMessage,
logging
});
const onIceCandidate = event => {
_readyToSendOffer.resolve();
if (shouldFilterCandidate(event.candidate)) {
logging.debug('Ignore candidate', event.candidate.candidate);
return;
}
forwarder(event);
};
let _previousIceState = _peerConnection.iceConnectionState;
const onIceConnectionStateChanged = event => {
if (_peerConnection) {
logging.debug('iceconnectionstatechange', _peerConnection.iceConnectionState);
if (_peerConnection.iceConnectionState === 'connected') {
api.emit('iceConnected');
} else if (_peerConnection.iceConnectionState === 'completed' && env.isLegacyEdge) {
// Start collecting stats later in Edge because it fails if you call it sooner
// This can probably be fixed better in Adapter.js
setTimeout(() => qos.startCollecting(_peerConnection), 1000);
}
} else {
logging.debug('iceconnectionstatechange on peerConnection that no longer exists', api);
}
const newIceState = event.target.iceConnectionState;
api.trigger('iceConnectionStateChange', newIceState);
if (_previousIceState !== 'disconnected' && newIceState === 'failed') {
// the sequence disconnected => failure would indicate an abrupt disconnection (e.g. remote
// peer closed the browser) or a network problem. We don't want to log that has a connection
// establishment failure. This behavior is seen only in Chrome 47+
triggerError({
reason: 'The stream was unable to connect due to a network error.' + ' Make sure your connection isn\'t blocked by a firewall.',
prefix: 'ICEWorkflow'
});
} else {
logAnalyticsEvent('attachEventsToPeerConnection', 'iceconnectionstatechange', newIceState);
}
if (newIceState === 'disconnected' && _iceRestartNeeded.get()) {
logging.debug('Restarting ice!');
api.generateOffer();
}
_previousIceState = newIceState;
};
const onNegotiationNeeded = () => {
logAnalyticsEvent('peerConnection:negotiationNeeded', 'Event');
if (isPublisher) {
api.generateOffer();
}
}; // Work around the fact that the safari adapter shim only
// fires 'addstream' event if onaddstream has been set
// https://github.com/opentok/webrtc-js/pull/2266#issuecomment-305647862
_peerConnection.onaddstream = noop;
_peerConnection.addEventListener('icecandidate', onIceCandidate);
_peerConnection.addEventListener('addstream', onRemoteStreamAdded);
_peerConnection.addEventListener('removestream', onRemoteStreamRemoved);
_peerConnection.addEventListener('signalingstatechange', routeStateChanged);
_peerConnection.addEventListener('negotiationneeded', onNegotiationNeeded);
_peerConnection.addEventListener('iceconnectionstatechange', onIceConnectionStateChanged);
cleanupJobs.add(() => {
if (!_peerConnection) {
return;
}
_peerConnection.onaddstream = null;
_peerConnection.removeEventListener('icecandidate', onIceCandidate);
_peerConnection.removeEventListener('addstream', onRemoteStreamAdded);
_peerConnection.removeEventListener('removestream', onRemoteStreamRemoved);
_peerConnection.removeEventListener('signalingstatechange', routeStateChanged);
_peerConnection.removeEventListener('negotiationneeded', onNegotiationNeeded);
_peerConnection.removeEventListener('iceconnectionstatechange', onIceConnectionStateChanged);
});
triggerPeerConnectionCompletion(null);
};
const triggerPeerConnectionCompletion = () => {
while (_peerConnectionCompletionHandlers.length) {
_peerConnectionCompletionHandlers.shift().call(null);
}
}; // Clean up the Peer Connection and trigger the close event.
// This function can be called safely multiple times, it will
// only trigger the close event once (per PeerConnection object)
const tearDownPeerConnection = () => {
// Our connection is dead, stop processing ICE candidates
if (_iceProcessor) {
_iceProcessor.destroy();
_iceProcessor = null;
}
_peerConnectionCompletionHandlers = [];
qos.stopCollecting();
cleanupJobs.releaseAll();
_readyToSendOffer.clean();
if (_peerConnection !== null) {
if (_peerConnection.destroy) {
// OTPlugin defines a destroy method on PCs. This allows
// the plugin to release any resources that it's holding.
_peerConnection.destroy();
}
_peerConnection = null;
api.trigger('close');
}
api.off();
};
const routeStateChanged = event => {
const newState = event.target.signalingState;
api.emit('signalingStateChange', newState);
if (newState === 'stable') {
api.emit('signalingStateStable');
}
if (newState && newState !== _state) {
_state = newState;
logging.debug("stateChange: " + _state);
switch (_state) {
case 'closed':
tearDownPeerConnection();
break;
default:
}
}
};
const qosCallback = parsedStats => {
parsedStats.dataChannels = _channels.sampleQos();
api.trigger('qos', {
parsedStats,
simulcastEnabled: _simulcastEnabled.get()
});
};
const getRemoteStreams = () => {
let streams;
if (_peerConnection.getRemoteStreams) {
streams = _peerConnection.getRemoteStreams();
} else if (_peerConnection.remoteStreams) {
streams = _peerConnection.remoteStreams;
} else {
throw new Error('Invalid Peer Connection object implements no ' + 'method for retrieving remote streams');
} // Force streams to be an Array, rather than a 'Sequence' object,
// which is browser dependent and does not behaviour like an Array
// in every case.
return Array.prototype.slice.call(streams);
}; // / PeerConnection signaling
const onRemoteStreamAdded = event => {
logAnalyticsEvent('createPeerConnection', 'StreamAdded');
api.trigger('streamAdded', event.stream);
};
const onRemoteStreamRemoved = event => {
logAnalyticsEvent('peerConnection:streamRemoved', 'Event');
api.trigger('streamRemoved', event.stream);
}; // ICE Negotiation messages
const reportError = (message, reason, prefix) => {
processingOffer = false;
triggerError({
reason: "PeerConnection.offerProcessor " + message + ": " + reason,
prefix
});
}; // Process an offer that
const processOffer = message => {
if (processingOffer) {
// If we get multiple pending offer messages we just handle the most recent one
pendingOfferMessage = message;
return;
}
processingOffer = true;
logAnalyticsEvent('peerConnection:processOffer', 'Event');
const offer = new NativeRTCSessionDescription({
type: 'offer',
sdp: message.content.sdp
}); // Relays +answer+ Answer
const relayAnswer = answer => {
processingOffer = false;
if (_iceProcessor) {
_iceProcessor.process();
}
sendMessage('answer', answer);
if (!env.isLegacyEdge) {
qos.startCollecting(_peerConnection, isPublisher);
}
if (pendingOfferMessage) {
processOffer(pendingOfferMessage);
pendingOfferMessage = null;
}
};
const onRemoteVideoSupported = supported => api.trigger('remoteVideoSupported', supported);
internalCreatePeerConnection(() => {
offerProcessor(_peerConnection, windowMock.RTCPeerConnection, windowMock.RTCSessionDescription, NativeRTCSessionDescription, sdpTransforms, offer, codecFlags, p2p, relayAnswer, reportError, onRemoteVideoSupported, replaceBaselineProfile);
});
};
const processAnswer = message => {
logAnalyticsEvent('peerConnection:processAnswer', 'Event');
const failure = errorReason => {
triggerError({
reason: "Error while setting RemoteDescription " + errorReason,
prefix: 'SetRemoteDescription'
});
};
if (!message.content.sdp) {
failure('Weird answer message, no SDP.');
return;
}
_answer = new NativeRTCSessionDescription({
type: 'answer',
sdp: applySdpTransform(sdpTransforms, 'remote', 'answer', assign({
replaceBaselineProfile
}, answerOverrides), message.content.sdp).local
});
(() => {
const success = () => {
logging.debug('processAnswer: setRemoteDescription Success');
if (_iceProcessor) {
_iceProcessor.process();
}
};
_peerConnection.setRemoteDescription(_answer).then(success).catch(failure);
offerAnswerPending = false;
if (regenerateOffer) {
regenerateOffer = false;
api.generateOffer();
}
})();
if (!env.isLegacyEdge) {
qos.startCollecting(_peerConnection, isPublisher);
}
};
const processSubscribe = () => {
offerAnswerPending = true;
logAnalyticsEvent('peerConnection:processSubscribe', 'Event');
logging.debug('processSubscribe: Sending offer to subscriber.');
const simulcastStreams = _simulcastEnabled.get() ? config.capableSimulcastStreams : 1;
internalCreatePeerConnection(() => {
subscribeProcessor({
peerConnection: _peerConnection,
NativeRTCSessionDescription,
sdpTransforms,
simulcastStreams,
offerOverrides,
offerConstraints: {
iceRestart: _iceRestartNeeded.get()
},
replaceBaselineProfile
}).then(offer => {
logging.debug('processSubscribe: got offer, waiting for ' + '_readyToSendOffer');
_offer = offer;
_readyToSendOffer.promise.then(() => {
logging.debug('processSubscribe: sending offer');
sendMessage('offer', _offer);
});
}, error => {
triggerError({
reason: "subscribeProcessor " + error.message + ": " + error.reason,
prefix: error.prefix
});
});
_iceRestartNeeded.set(false);
});
};
api.generateOffer = debounce(() => {
if (!offerAnswerPending) {
processSubscribe();
} else {
regenerateOffer = true;
}
}, 100);
const triggerError = (_ref2) => {
let reason = _ref2.reason,
prefix = _ref2.prefix;
logging.error(reason, 'in state', !_peerConnection ? '(none)' : {
connectionState: _peerConnection.connectionState,
iceConnectionState: _peerConnection.iceConnectionState,
iceGatheringState: _peerConnection.iceGatheringState,
signalingState: _peerConnection.signalingState
});
api.trigger('error', {
reason,
prefix
});
};
/*
* Add a track to the underlying PeerConnection
*
* @param {object} track - the track to add
* @param {object} stream - the stream to add it to
* @return {RTCRtpSender}
*/
api.addTrack = (track, stream) => {
const promise = new Promise((resolve, reject) => {
internalCreatePeerConnection(err => {
if (err) {
return reject(err);
}
resolve();
return undefined;
});
}).then(() => {
if (_peerConnection.addTrack) {
return _peerConnection.addTrack(track, stream);
}
const pcStream = _peerConnection.getLocalStreams()[0];
if (pcStream === undefined) {
throw new Error('PeerConnection has no existing streams, cannot addTrack');
}
pcStream.addTrack(track);
api.generateOffer();
return undefined;
}).then(() => new Promise(resolve => {
api.once('signalingStateStable', () => {
resolve();
});
}));
return promiseTimeout(promise, 15000, 'Renegotiation timed out');
};
function FakeRTCRtpSender(track) {
this.track = track;
}
/**
* Remove a track from the underlying PeerConnection
*
* @param {RTCRtpSender} RTCRtpSender - the RTCRtpSender to remove
*/
api.removeTrack = RTCRtpSender => {
const promise = Promise.resolve().then(() => {
if (RTCRtpSender instanceof FakeRTCRtpSender) {
_peerConnection.getLocalStreams()[0].removeTrack(RTCRtpSender.track);
api.generateOffer();
return undefined;
}
return _peerConnection.removeTrack(RTCRtpSender);
}).then(() => new Promise(resolve => {
api.once('signalingStateStable', () => {
resolve();
});
}));
return promiseTimeout(promise, 15000, 'Renegotiation timed out');
};
api.addLocalStream = webRTCStream => new Promise((resolve, reject) => {
internalCreatePeerConnection(err => {
if (err) {
reject(err);
return;
}
try {
_peerConnection.addStream(webRTCStream);
} catch (addStreamErr) {
reject(addStreamErr);
return;
}
resolve();
}, webRTCStream);
});
api.getLocalStreams = () => _peerConnection.getLocalStreams();
api.getSenders = () => {
if (_peerConnection.getSenders) {
return _peerConnection.getSenders();
}
return _peerConnection.getLocalStreams()[0].getTracks().map(track => new FakeRTCRtpSender(track));
};
api.disconnect = () => {
if (_iceProcessor) {
_iceProcessor.destroy();
_iceProcessor = null;
}
if (_peerConnection && _peerConnection.signalingState && _peerConnection.signalingState.toLowerCase() !== 'closed') {
_peerConnection.close();
if (env.name === 'Firefox') {
// FF seems to never go into the closed signalingState when the close
// method is called on a PeerConnection. This means that we need to call
// our cleanup code manually.
//
// * https://bugzilla.mozilla.org/show_bug.cgi?id=989936
//
setTimeout(tearDownPeerConnection);
}
}
};
api.iceRestart = () => _iceRestartNeeded.set(true);
api.processMessage = (type, message) => {
logging.debug("processMessage: Received " + type + " from " + message.fromAddress, message);
logging.debug(message);
switch (type) {
case 'generateoffer':
if (message.content && message.content.simulcastEnabled !== undefined) {
_simulcastEnabled.set(message.content.simulcastEnabled);
}
api.generateOffer();
trackRenegotiationAttempts();
break;
case 'offer':
processOffer(message);
trackRenegotiationAttempts();
break;
case 'answer':
case 'pranswer':
processAnswer(message);
break;
case 'candidate':
var iceCandidate = new NativeRTCIceCandidate(message.content);
if (shouldFilterCandidate(iceCandidate)) {
logging.debug('Ignore candidate', iceCandidate.candidate);
return api;
}
if (_iceProcessor) {
_iceProcessor.addIceCandidate(iceCandidate).catch(err => {
// Sometimes you might get an error adding an iceCandidate
// this does not mean we should fail, if we get a working candidate
// later then we should let it work
logging.warn("Error while adding ICE candidate: " + JSON.stringify(iceCandidate) + ": " + err.toString());
});
}
break;
default:
logging.debug("processMessage: Received an unexpected message of type " + type + " from " + message.fromAddress + ": " + JSON.stringify(message));
}
function trackRenegotiationAttempts() {
offerMessagesReceived += 1;
qos.handleOfferMessageReceived();
if (offerMessagesReceived > 1) {
logAnalyticsEvent('Renegotiation', 'Attempt');
renegotiationInProgress = true;
}
}
return api;
};
api.remoteStreams = () => _peerConnection ? getRemoteStreams() : [];
api.remoteTracks = () => {
if (!_peerConnection) {
return [];
}
if (_peerConnection.getReceivers) {
return Array.prototype.slice.apply(_peerConnection.getReceivers()).map(receiver => receiver.track);
}
return Array.prototype.concat.apply([], getRemoteStreams().map(stream => stream.getTracks()));
};
api.remoteDescription = () => _peerConnection.remoteDescription;
/*
* @param {function(DOMError, ArrayAudioContext
based audio level sampler. It returns the maximum value in the
* last 1024 samples.
*
* It is worth noting that the remote MediaStream
audio analysis is currently only
* available in FF.
*
* This implementation gracefully handles the case where the MediaStream
has not
* been set yet by returning a null
value until the stream is set. It is up to the
* call site to decide what to do with this value (most likely ignore it and retry later).
*
* @constructor
* @param {AudioContext} audioContext an audio context instance to get an analyser node
*/
module.exports = (_temp = /*#__PURE__*/function () {
function WebAudioAudioLevelSampler(audioContext) {
(0, _defineProperty2.default)(this, "_analyser", null);
(0, _defineProperty2.default)(this, "_timeDomainData", null);
(0, _defineProperty2.default)(this, "_sourceNode", null);
this._audioContext = audioContext;
}
var _proto = WebAudioAudioLevelSampler.prototype;
_proto._initAudioAnalyser = function _initAudioAnalyser(mediaStream) {
this._analyser = this._audioContext.createAnalyser();
this._sourceNode = this._audioContext.createMediaStreamSource(mediaStream);
this._sourceNode.connect(this._analyser);
this._timeDomainData = new Uint8Array(this._analyser.frequencyBinCount);
};
_proto.webRTCStream = function webRTCStream(mediaStream) {
if (this._sourceNode) {
this._sourceNode.disconnect(this._analyser);
}
if (this._audioContext.state === 'suspended') {
this._audioContext.resume();
}
this._initAudioAnalyser(mediaStream);
};
_proto.destroy = function destroy() {
if (this._sourceNode) {
this._sourceNode.disconnect(this._analyser);
}
this._timeDomainData = null;
};
_proto.sample = function sample(done) {
if (typeof done === 'function') {
throw new Error('sample no longer takes a callback');
}
if (this._analyser && this._timeDomainData) {
this._analyser.getByteTimeDomainData(this._timeDomainData); // varies from 0 to 255
let max = 0;
for (let idx = this._timeDomainData.length - 1; idx >= 0; idx -= 1) {
max = Math.max(max, Math.abs(this._timeDomainData[idx] - 128));
} // normalize the collected level to match the range delivered by
// the getStats' audioOutputLevel
return max / 128;
}
return null;
};
return WebAudioAudioLevelSampler;
}(), _temp);
/***/ }),
/* 142 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-underscore-dangle, no-use-before-define */
const domState = __webpack_require__(544);
const eventNames = __webpack_require__(24);
const eventing = __webpack_require__(5);
const Events = __webpack_require__(20)(); // Helper to synchronise several startup tasks and then dispatch a unified
// 'envLoaded' event.
function EnvironmentLoader() {
const environmentLoader = this;
eventing(environmentLoader);
function isReady() {
return domState.isDomLoaded() && !domState.isDomUnloaded();
}
function onLoaded() {
if (isReady()) {
environmentLoader.dispatchEvent(new Events.EnvLoadedEvent(eventNames.ENV_LOADED));
}
}
function onDomReady() {
domState.whenUnloaded.then(onDomUnload);
onLoaded();
}
function onDomUnload() {
environmentLoader.dispatchEvent(new Events.EnvLoadedEvent(eventNames.ENV_UNLOADED));
}
domState.whenLoaded.then(onDomReady);
this.onLoad = function (cb, context) {
if (isReady()) {
cb.call(context);
return;
}
environmentLoader.on(eventNames.ENV_LOADED, cb, context);
};
this.onUnload = function (cb, context) {
if (this.isUnloaded()) {
cb.call(context);
return;
}
environmentLoader.on(eventNames.ENV_UNLOADED, cb, context);
};
this.isUnloaded = function () {
return domState.isDomUnloaded();
};
}
module.exports = new EnvironmentLoader();
/***/ }),
/* 143 */
/***/ (function(module, exports, __webpack_require__) {
var baseMerge = __webpack_require__(545),
createAssigner = __webpack_require__(224);
/**
* This method is like `_.assign` except that it recursively merges own and
* inherited enumerable string keyed properties of source objects into the
* destination object. Source properties that resolve to `undefined` are
* skipped if a destination value exists. Array and plain object properties
* are merged recursively. Other objects and value types are overridden by
* assignment. Source objects are applied from left to right. Subsequent
* sources overwrite property assignments of previous sources.
*
* **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 0.5.0
* @category Object
* @param {Object} object The destination object.
* @param {...Object} [sources] The source objects.
* @returns {Object} Returns `object`.
* @example
*
* var object = {
* 'a': [{ 'b': 2 }, { 'd': 4 }]
* };
*
* var other = {
* 'a': [{ 'c': 3 }, { 'e': 5 }]
* };
*
* _.merge(object, other);
* // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
*/
var merge = createAssigner(function(object, source, srcIndex) {
baseMerge(object, source, srcIndex);
});
module.exports = merge;
/***/ }),
/* 144 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var objectAssign = __webpack_require__(549);
// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js
// original notice:
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh setStyle()
method of thePublisher object. (See the documentation for
* setStyle() to see the styles that define this object.)
* @return {Object} The object that defines the styles of the Publisher.
* @see setStyle()
* @method #getStyle
* @memberOf Publisher
*/
/**
* Returns an object that has the properties that define the current user interface controls of
* the Subscriber. You can modify the properties of this object and pass the object to the
* setStyle()
method of the Subscriber object. (See the documentation for
* setStyle() to see the styles that define this object.)
* @return {Object} The object that defines the styles of the Subscriber.
* @see setStyle()
* @method #getStyle
* @memberOf Subscriber
*/
// If +key+ is falsly then all styles will be returned.
self.getStyle = function (key) {
return _style.get(key);
};
/**
* Sets properties that define the appearance of some user interface controls of the Publisher.
*
* style
, it is an object that has the following
* properties:
*
*
*
* audioLevelDisplayMode
(String) — How to display the audio level
* indicator. Possible values are: "auto"
(the indicator is displayed when the
* video is disabled), "off"
(the indicator is not displayed), and
* "on"
(the indicator is always displayed).archiveStatusDisplayMode
(String) — How to display the archive
* status indicator. Possible values are: "auto"
(the indicator is displayed
* when the session is being recorded), "off"
(the indicator is not displayed).
* If you disable the archive status display indicator, you can display your own user
* interface notifications based on the archiveStarted
and
* archiveStopped
events dispatched by the Session object.backgroundImageURI
(String) — A URI for an image to display as
* the background image when a video is not displayed. (A video may not be displayed if
* you call publishVideo(false)
on the Publisher object). You can pass an http
* or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the
* data
URI scheme (instead of http or https) and pass in base-64-encrypted
* PNG data, such as that obtained from the
* Publisher.getImgData() method (for example, you could call
* myPublisher.setStyle("backgroundImageURI", myPublisher.getImgData())
).
* If the URL or the image data is invalid, the property is ignored (the attempt to set
* the image fails silently).buttonDisplayMode
(String) — How to display the microphone
* controls. Possible values are: "auto"
(controls are displayed when the
* stream is first displayed and when the user mouses over the display), "off"
* (controls are not displayed), and "on"
(controls are always displayed).nameDisplayMode
(String) Whether to display the stream name.
* Possible values are: "auto"
(the name is displayed when the stream is first
* displayed and when the user mouses over the display), "off"
(the name is not
* displayed), and "on"
(the name is always displayed).
For example, the following code passes one parameter to the method:
* *myPublisher.setStyle({nameDisplayMode: "off"});* *
If you pass two parameters, style
and value
, they are
* key-value pair that define one property of the display style. For example, the following
* code passes two parameter values to the method:
myPublisher.setStyle("nameDisplayMode", "off");* *
You can set the initial settings when you call the Session.publish()
* or OT.initPublisher()
method. Pass a style
property as part of the
* properties
parameter of the method.
The OT object dispatches an exception
event if you pass in an invalid style
* to the method. The code
property of the ExceptionEvent object is set to 1011.
style
passed in. Pass a value
* for this parameter only if the value of the style
parameter is a String.
*
* @see getStyle()
* @return {Publisher} The Publisher object
* @see setStyle()
*
* @see Session.publish()
* @see OT.initPublisher()
* @method #setStyle
* @memberOf Publisher
*/
/**
* Sets properties that define the appearance of some user interface controls of the Subscriber.
*
* You can either pass one parameter or two parameters to this method.
* *If you pass one parameter, style
, it is an object that has the following
* properties:
*
*
audioBlockedDisplayMode
(String) — Whether to display
* the default audio blocked icon in Subscribers (in browsers where audio
* autoplay is blocked). Possible values are: "auto"
(the default,
* icon is displayed when the audio is disabled) and "off"
(the icon
* is not displayed). Set this to "off"
if you want to display
* your own UI element showing that the audio is blocked. In response to an
* HTML element dispatching a click
event, you can call the
* OT.unblockAudio() method to start audio
* playback in this and all other blocked subscribers.audioLevelDisplayMode
(String) — How to display the audio level
* indicator. Possible values are: "auto"
(the indicator is displayed when the
* video is disabled), "off"
(the indicator is not displayed), and
* "on"
(the indicator is always displayed).backgroundImageURI
(String) — A URI for an image to display as
* the background image when a video is not displayed. (A video may not be displayed if
* you call subscribeToVideo(false)
on the Publisher object). You can pass an
* http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the
* data
URI scheme (instead of http or https) and pass in base-64-encrypted
* PNG data, such as that obtained from the
* Subscriber.getImgData() method (for example, you could call
* mySubscriber.setStyle("backgroundImageURI", mySubscriber.getImgData())
).
* If the URL or the image data is invalid, the property is ignored (the attempt to set
* the image fails silently).buttonDisplayMode
(String) — How to display the speaker
* controls. Possible values are: "auto"
(controls are displayed when the
* stream is first displayed and when the user mouses over the display), "off"
* (controls are not displayed), and "on"
(controls are always displayed).nameDisplayMode
(String) Whether to display the stream name.
* Possible values are: "auto"
(the name is displayed when the stream is first
* displayed and when the user mouses over the display), "off"
(the name is not
* displayed), and "on"
(the name is always displayed).videoDisabledDisplayMode
(String) Whether to display the video
* disabled indicator and video disabled warning icons for a Subscriber. These icons
* indicate that the video has been disabled (or is in risk of being disabled for
* the warning icon) due to poor stream quality. Possible values are: "auto"
* (the icons are automatically when the displayed video is disabled or in risk of being
* disabled due to poor stream quality), "off"
(do not display the icons), and
* "on"
(display the icons).For example, the following code passes one parameter to the method:
* *mySubscriber.setStyle({nameDisplayMode: "off"});* *
If you pass two parameters, style
and value
, they are key-value
* pair that define one property of the display style. For example, the following code passes
* two parameter values to the method:
mySubscriber.setStyle("nameDisplayMode", "off");* *
You can set the initial settings when you call the Session.subscribe()
method.
* Pass a style
property as part of the properties
parameter of the
* method.
The OT object dispatches an exception
event if you pass in an invalid style
* to the method. The code
property of the ExceptionEvent object is set to 1011.
style
passed in. Pass a value
* for this parameter only if the value of the style
parameter is a String.
*
* @returns {Subscriber} The Subscriber object.
*
* @see getStyle()
* @see setStyle()
*
* @see Session.subscribe()
* @method #setStyle
* @memberOf Subscriber
*/
self.setStyle = function (keyOrStyleHash, value, silent) {
if (_readOnly) {
logging.warn('Calling setStyle() has no effect because the' + 'showControls option was set to false');
return this;
}
let styleHash;
if (typeof keyOrStyleHash !== 'string') {
styleHash = keyOrStyleHash;
} else {
styleHash = {};
styleHash[keyOrStyleHash] = value;
}
if (styleHash.backgroundImageURI) {
styleHash.backgroundImageURI = fixBackgroundImageURI(styleHash.backgroundImageURI);
}
_style.setAll(styleHash, silent);
logSetStyleWithPayload(styleHashLogFilter(styleHash));
return this;
};
};
/***/ }),
/* 148 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const once = __webpack_require__(47);
const StaticConfig = __webpack_require__(28)();
const Analytics = __webpack_require__(54);
const APIKEY = __webpack_require__(69);
const EnvironmentLoader = __webpack_require__(142);
const hasOpenTokSupport = __webpack_require__(56).once;
const isWebSocketSupported = __webpack_require__(570);
const OTHelpers = __webpack_require__(4);
const logging = __webpack_require__(1)('systemRequirements');
const upgradeHtml = __webpack_require__(571);
const Modal = __webpack_require__(572);
const staticConfig = StaticConfig.onlyLocal(); // This will be logged before we even get the correct loggingUrl from
// config.opentok.com.
const analytics = new Analytics();
const logOnce = once(() => {
analytics.logEvent({
action: 'checkSystemRequirements',
variation: 'notHasRequirements',
partnerId: APIKEY.value,
payload: {
userAgent: OTHelpers.env.userAgent
}
});
});
let isDialogOpen = false;
module.exports = {
/**
* Checks if the system supports OpenTok for WebRTC. Note that this method is called
* automatically when you call OT.initPublisher()
or OT.initSession()
,
* and if the system doesn't support OpenTok, the OpenTok.js library displays a message to
* the user. Call the OT.checkSystemRequirements()
method before calling
* OT.initPublisher()
or OT.initSession()
if you do not want the
* library to display that message.
*
* @return {Number} Whether the system supports OpenTok for WebRTC (1) or not (0).
* @see OT.upgradeSystemRequirements()
* @method OT.checkSystemRequirements
* @memberof OT
*/
check() {
logging.debug('OT.checkSystemRequirements()'); // We use Number here to coerce a Boolean to 1 or 0
const systemRequirementsMet = Number(isWebSocketSupported && hasOpenTokSupport());
if (!systemRequirementsMet) {
logOnce();
}
return systemRequirementsMet;
},
/**
* Displays information about system requirements for OpenTok for WebRTC. This
* information is displayed in an iframe element that fills the browser window.
*
* Note: this information is displayed automatically when you call the
* OT.initSession()
or the OT.initPublisher()
method
* if the client does not support OpenTok for WebRTC.
*
* Note: A screen-sharing extension is not required in Chrome 72+ or Opera 59+. * The end user is prompted to grant access to the screen, just as they would be when granting * access to a camera. You only need a screen-sharing extension for older versions * of Opera and Chrome. *
* Use the OT.checkScreenSharingCapability()
method to check if an extension is
* required, registered, and installed.
*
* The OpenTok
* screensharing-extensions
* repo includes code for creating an extension for screen-sharing support.
*
* @param {String} kind Set this parameter to "chrome"
. Currently, you can only
* register a screen-sharing extension for older versions of Chrome and Opera.
*
* @param {String} id The ID for your screen-sharing extension. You can find this ID at
* chrome://extensions.
*
* @param {Number} version The version of the screen-sharing extension from the
* screensharing-extensions repo.
* Set this if you are using version 2 or later. For example, if you are using version 2, set this
* to 2. With version 2, the client can use the extension immediately after installing it, without
* reloading the page.
*
* @see OT.initPublisher()
* @see OT.checkScreenSharingCapability()
* @method OT.registerScreenSharingExtension
* @memberof OT
*/
screenSharing.registerExtension = function (kind) {
if (screenSharing.extensionClasses[kind] == null) {
throw new Error('Unsupported kind passed to OT.registerScreenSharingExtension');
}
for (var _len = arguments.length, initArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
initArgs[_key - 1] = arguments[_key];
}
const x = screenSharing.extensionClasses[kind].register(...initArgs);
screenSharing.extensionByKind[kind] = x;
};
const screenSharingPickHelper = () => {
const foundClass = find(Object.keys(screenSharing.extensionClasses).sort( // Try "electron" first. Reason being, even though `getDisplayMedia`
// is exposed in electron, developers must use the electron's
// `desktopCapturer` instead.
cls => cls === 'electron' ? -1 : 1), cls => screenSharing.extensionClasses[cls].isSupportedInThisBrowser);
if (foundClass === undefined) {
return {};
}
return {
name: foundClass,
proto: screenSharing.extensionClasses[foundClass],
instance: screenSharing.extensionByKind[foundClass]
};
};
screenSharing.pickHelper = () => screenSharingPickHelper();
/**
* Checks for support for publishing screen-sharing streams on the client browser. The object
* passed to the callback function defines whether screen sharing is supported, as well as
* which screen-sharing sources (application, screen, or window) are supported. It also indicates
* whether an extension is required, installed, and registered (if needed).
*
*
* OT.checkScreenSharingCapability(function(response) { * if (!response.supported || response.extensionRegistered === false) { * // This browser does not support screen sharing * } else if (response.extensionInstalled === false) { * // Prompt to install the extension * } else { * // Screen sharing is available. * } * }); **
* Chrome 72+, Firefox 52+, Edge 79+, and Opera 59+ have screen-sharing support built-in, with * no extension required. (Note that support for the OpenTok plugin for Internet Explorer is * removed in OpenTok 2.17.) To publish a screen-sharing video in older versions or older * versions of Chrome or Opera, the client adds an extension that enables publishing a * screen-sharing video stream on your domain. The OpenTok * screensharing-extensions * sample includes code for creating an extension for screen-sharing support in * older versions of Chrome and Opera. *
* For more information, see the * OpenTok Screen Sharing developer guide. * * @param {function} callback The callback invoked with the support options object passed as * the parameter. This object has the following properties that indicate support for publishing * screen-sharing streams in the client: *
*
extensionInstalled
(Boolean) — In older versions of Chrome and Opera,
* this is set to true
if the extension is installed and registered.
* In Chrome 72+, Firefox 52+, Opera 59+, Edge 79+, and Internet Explorer,
* this property is undefined.
* supported
(Boolean) — Set to true
if screen sharing
* is supported in the browser. Check the extensionRequired
property
* to see if the browser requires an extension for screen sharing.
* supportedSources
(Object) — An object with the following properties:
* application
, browser
, screen
, and
* window
. Each property is a Boolean value indicating support.
* In Firefox, you can set a specify the type of screen-sharing source
* by setting the videoSource
property of the options passed into the
* OT.initPublisher() method. Set the property to
* "application"
, "screen"
, or "window"
.
* In other browsers, setting the videoSource
property to any of these values
* results in a prompt that asks the user to determine the screen-sharing source.
* In Firefox, Chrome, and Opera, browser
, screen
, and
* window
are set to true
. In Chrome, only screen
and
* window
are set to true
.
* The options parameter also includes the following properties, which apply to screen-sharing * support in older versions of Chrome and Opera (in all other browsers these properties are * undefined): *
extensionRequired
(String) — Set to "chrome"
* in older versions of Chrome and Opera, which require a screen-sharing extension
* to be installed. This property is undefined in other browsers.
* extensionRegistered
(Boolean) — In older versions of Chrome and Opera,
* this property is set to true
if a screen-sharing extension is registered;
* otherwise it is set to false
. In other browsers (which do not require
* an extension), this property is undefined. Use the
* OT.registerScreenSharingExtension()
method to register a screen-sharing
* extension in older versions of Chrome or Opera.
* " + errorMsg + (helpMsg ? "
"; OTHelpers.addClass(this._container, classNames || 'OT_subscriber_error'); if (this._container.querySelector('p').offsetHeight > this._container.offsetHeight) { this._container.querySelector('span').style.display = 'none'; } } } /** * Destroy the video */ ; _proto.destroy = function destroy() { this.emit('destroy'); this.destroyVideo(); if (this._container) { OTHelpers.removeElement(this._container); this._container = null; } }; _proto.setBackgroundImageURI = function setBackgroundImageURI(bgImgURI) { OTHelpers.css(this._posterContainer, 'backgroundImage', "url(" + bgImgURI + ")"); OTHelpers.css(this._posterContainer, 'backgroundSize', 'contain'); OTHelpers.css(this._posterContainer, 'opacity', '1.0'); }; _proto.isAudioBlocked = function isAudioBlocked() { return Boolean(this._videoElementFacade && this._videoElementFacade.isAudioBlocked()); }; _proto.unblockAudio = function unblockAudio() { return this._videoElementFacade.unblockAudio(); }; _proto.userGesture = function userGesture() { if (this.isAudioBlocked()) { unblockAudio().then(() => logging.debug('Successfully unblocked audio'), err => logging.error('Error retrying audio on user interaction:', err)); } }; _proto.setAudioBlockedUi = function setAudioBlockedUi(audioBlocked) { if (!this._container) { return; } if (audioBlocked) { this._container.classList.add('OT_container-audio-blocked'); } else { this._container.classList.remove('OT_container-audio-blocked'); } } /** * Bind a video to a VideoElementFacade * * @param {MediaStream} webRTCStream the MediaStream to bind * @param {Object} options * @param {Function} options.error Error callback * @param {Float} options.audioVolume The initial audioVolume * @param {Boolean} options.muted The initial mute state * @param {String} [options.fallbackText] The default fallbackText * * @return {Promise " : '') + "click
event.
*
* You only need to call this method if you disable display of
* the Subscriber's default audio playback icon (by setting the
* style.audioBlockedDisplayMode
property of the
* options
parameter of the
* Session.subscribe() method).
* You may call this method in response to the user clicking an HTML element
* that you display on the page.
*
* The Subscriber dispatches an
* audioBlocked event
* if audio is blocked. In response to that event, you can display a UI
* element to notify the end user to respond to playback subscriber audio.
* Call the OT.unblockAudio()
method in response to the user
* clicking a DOM element.
*
* Note: Subscriber audio is also unblocked automatically if
* the local client gains access to the camera or microphone (for instance,
* in response to a successful call to OT.initPublisher()
).
*
* @returns Promise The promise resolves if audio playback succeeds
* on all blocked subscribers. The promise is rejected if playback fails.
* See Error for details.
*
* @see Subscriber.isAudioBlocked()
* @see The audioBlocked
* and audioUnblocked
* Subscriber events
*
* @method OT.unblockAudio
* @memberof OT
*/
return sub._.unblockAudio();
}));
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 163 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(Promise) {
var _interopRequireDefault = __webpack_require__(2);
var _regenerator = _interopRequireDefault(__webpack_require__(18));
var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));
var _extends2 = _interopRequireDefault(__webpack_require__(23));
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(__webpack_require__(644));
/* eslint-disable global-require, func-names */
/* eslint-disable no-use-before-define, no-prototype-builtins, no-underscore-dangle */
/* global MediaStreamTrack */
// @todo need to ensure logging for peer disconnected, and peer failures is intact
const get = __webpack_require__(51);
const assert = __webpack_require__(144);
const assign = __webpack_require__(6);
const cloneDeep = __webpack_require__(50);
const find = __webpack_require__(58);
const pick = __webpack_require__(42);
const once = __webpack_require__(47);
const uuid = __webpack_require__(15);
const capitalize = __webpack_require__(254);
const WeakMap = __webpack_require__(225);
const _require = __webpack_require__(161),
CancellationError = _require.CancellationError,
Cancellation = _require.default;
const env = __webpack_require__(3);
const setEncodersActiveStateDefault = __webpack_require__(645);
const promisify = __webpack_require__(293);
const getStatsHelpers = __webpack_require__(72);
const eventNames = __webpack_require__(24);
const eventing = __webpack_require__(5);
const Event = __webpack_require__(135);
const AnalyticsHelperDefault = __webpack_require__(54);
const IntervalRunnerDefault = __webpack_require__(71);
const createCleanupJobs = __webpack_require__(150);
const whitelistPublisherProperties = __webpack_require__(646);
const defaultWidgetView = __webpack_require__(160)();
const audioLevelBehaviour = __webpack_require__(647);
const blockCallsUntilComplete = __webpack_require__(648);
const unblockAudio = __webpack_require__(162);
const _require2 = __webpack_require__(103)(),
getMediaDevices = _require2.getMediaDevices;
const createCanvasVideoTrack = __webpack_require__(649);
const _require3 = __webpack_require__(164),
getProxyUrl = _require3.getProxyUrl;
module.exports = function PublisherFactory(_ref) {
if (_ref === void 0) {
_ref = {};
}
let _ref2 = _ref,
_ref2$FORCE_DISCONNEC = _ref2.FORCE_DISCONNECT_OLD_PEER_CONNECTIONS_DELAY,
FORCE_DISCONNECT_OLD_PEER_CONNECTIONS_DELAY = _ref2$FORCE_DISCONNEC === void 0 ? 30000 : _ref2$FORCE_DISCONNEC,
deps = (0, _objectWithoutPropertiesLoose2.default)(_ref2, ["FORCE_DISCONNECT_OLD_PEER_CONNECTIONS_DELAY"]);
['processPubOptions'].forEach(key => {
assert(deps[key], key + " dependency must be injected into Publisher");
});
const AnalyticsHelper = deps.AnalyticsHelper || AnalyticsHelperDefault;
const calculateCapableSimulcastStreams = deps.calculateCapableSimulcastStreams || __webpack_require__(248);
const createChromeMixin = deps.createChromeMixin || __webpack_require__(271)();
const deviceHelpers = deps.deviceHelpers || __webpack_require__(103)();
const EnvironmentLoader = deps.EnvironmentLoader || __webpack_require__(142);
const Errors = deps.Errors || __webpack_require__(8);
const Events = deps.Events || __webpack_require__(20)();
const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(11);
const interpretPeerConnectionError = deps.interpretPeerConnectionError || __webpack_require__(158)();
const IntervalRunner = deps.IntervalRunner || IntervalRunnerDefault;
const logging = deps.logging || __webpack_require__(1)('Publisher');
const Microphone = deps.Microphone || __webpack_require__(281)();
const otError = deps.otError || __webpack_require__(12)();
const OTErrorClass = deps.OTErrorClass || __webpack_require__(33);
const OTHelpers = deps.OTHelpers || __webpack_require__(4);
const parseIceServers = deps.parseIceServers || __webpack_require__(98).parseIceServers;
const PUBLISH_MAX_DELAY = deps.PUBLISH_MAX_DELAY || __webpack_require__(260);
const PublisherPeerConnection = deps.PublisherPeerConnection || __webpack_require__(284)();
const PublishingState = deps.PublishingState || __webpack_require__(286)();
const StreamChannel = deps.StreamChannel || __webpack_require__(145);
const systemRequirements = deps.systemRequirements || __webpack_require__(148);
const VideoOrientation = deps.VideoOrientation || __webpack_require__(146)();
const WidgetView = deps.WidgetView || defaultWidgetView;
const windowMock = deps.global || (typeof window !== undefined ? window : global);
const createSendMethod = deps.createSendMethod || __webpack_require__(291);
const setEncodersActiveState = deps.setEncodersActiveState || setEncodersActiveStateDefault;
const processPubOptions = deps.processPubOptions;
/**
* The Publisher object provides the mechanism through which control of the
* published stream is accomplished. Calling the OT.initPublisher()
method
* creates a Publisher object.
The following code instantiates a session, and publishes an audio-video stream * upon connection to the session:
* ** var apiKey = ''; // Replace with your API key. See https://tokbox.com/account * var sessionID = ''; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/. * var token = ''; // Replace with a generated token that has been assigned the moderator role. * // See https://tokbox.com/developer/guides/create-token/. * * var session = OT.initSession(apiKey, sessionID); * session.connect(token, function(error) { * if (error) { * console.log(error.message); * } else { * // This example assumes that a DOM element with the ID 'publisherElement' exists * var publisherProperties = {width: 400, height:300, name:"Bob's stream"}; * publisher = OT.initPublisher('publisherElement', publisherProperties); * session.publish(publisher); * } * }); ** *
This example creates a Publisher object and adds its video to a DOM element
* with the ID publisherElement
by calling the OT.initPublisher()
* method. It then publishes a stream to the session by calling
* the publish()
method of the Session object.
accessAllowed
event when
* the user grants access. The Publisher object dispatches an accessDenied
event
* when the user denies access.
* @property {Element} element The HTML DOM element containing the Publisher. (Note:
* when you set the insertDefaultUI
option to false
in the call to
* OT.initPublisher, the element
property
* is undefined.)
* @property {String} id The DOM ID of the Publisher.
* @property {Stream} stream The {@link Stream} object corresponding the stream of
* the Publisher.
* @property {Session} session The {@link Session} to which the Publisher belongs.
*
* @see OT.initPublisher
* @see Session.publish()
*
* @class Publisher
* @augments EventDispatcher
*/
const Publisher = function Publisher(options) {
var _this = this;
if (options === void 0) {
options = {};
}
let privateEvents = eventing({});
const peerConnectionMetaMap = new WeakMap();
/**
* @typedef {Object} peerConnectionMeta
* @property {String} remoteConnectionId The connection id of the remote side
* @property {String} remoteSubscriberId The subscriber id of the remote side
* @property {Number} peerPriority The priority for this peer connection
* @property {String} peerId The peerId of this peer connection
* @property {String} peerConnectionId Our local identifier for this peer connection
*/
/**
* Retrieve meta information for this peer connection
* @param {PublisherPeerConnection} peerConnection
* @returns {peerConnectionMeta} meta data regarding this peer connection
*/
const getPeerConnectionMeta = peerConnection => peerConnectionMetaMap.get(peerConnection);
const setPeerConnectionMeta = (peerConnection, value) => peerConnectionMetaMap.set(peerConnection, value);
eventing(this);
const streamCleanupJobs = createCleanupJobs();
const proxyUrl = getProxyUrl();
/** @type AnalyticsHelperDefault */
let analytics = new AnalyticsHelper({
proxyUrl
}); // Check that the client meets the minimum requirements, if they don't the upgrade
// flow will be triggered.
if (!systemRequirements.check()) {
systemRequirements.upgrade();
}
/** @type {WidgetView|null} */
let widgetView;
let lastRequestedStreamId;
let webRTCStream;
let publishStartTime;
let microphone;
let state;
let rumorIceServers;
let attemptStartTime;
let audioDevices;
let videoDevices;
let selectedVideoInputDeviceId;
let selectedAudioInputDeviceId;
let didPublishComplete = false;
let currentPeerPriority = 0;
/** @type IntervalRunnerDefault | undefined */
let connectivityAttemptPinger; // previousSession mimics the publisher.session variable except it's never set to null
// this allows analytics to refer to it in cases where we disconnect/destroy
// and go to log analytics and publisher.session has been set to null
let previousSession;
const getLastSession = () => this.session || previousSession || {
isConnected() {
return false;
}
};
this.once('publishComplete', err => {
if (!err) {
didPublishComplete = true;
}
});
this.on('audioAcquisitionProblem', (_ref3) => {
let method = _ref3.method;
logAnalyticsEvent('publisher:audioAcquisitionProblem', 'Event', {
didPublishComplete,
method
});
});
function getCommonAnalyticsFields() {
return {
connectionId: getLastSession().isConnected() ? getLastSession().connection.connectionId : null,
streamId: lastRequestedStreamId,
widgetType: 'Publisher'
};
}
const onStreamAvailableError = plainError => {
const names = Object.keys(Errors).map(shortName => Errors[shortName]);
const error = otError(names.indexOf(plainError.name) > -1 ? plainError.name : Errors.MEDIA_ERR_ABORTED, plainError, ExceptionCodes.UNABLE_TO_PUBLISH);
logging.error("onStreamAvailableError " + error.name + ": " + error.message);
state.set('Failed');
if (widgetView) {
widgetView.destroy();
widgetView = null;
}
const logOptions = {
failureReason: 'GetUserMedia',
failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,
failureMessage: "OT.Publisher failed to access camera/mic: " + error.message
};
logConnectivityEvent('Failure', {}, logOptions);
OTErrorClass.handleJsException({
error,
errorMsg: logOptions.failureReason,
code: logOptions.failureCode,
target: this,
analytics
});
this.trigger('publishComplete', error);
};
const onScreenSharingError = errorParam => {
const error = cloneDeep(errorParam);
error.code = ExceptionCodes.UNABLE_TO_PUBLISH;
logging.error("OT.Publisher.onScreenSharingError " + error.message);
state.set('Failed');
error.message = "Screensharing: " + error.message;
this.trigger('publishComplete', error);
logConnectivityEvent('Failure', {}, {
failureReason: 'ScreenSharing',
failureMessage: error.message
});
if (widgetView) {
widgetView.destroy();
widgetView = null;
}
}; // The user has clicked the 'deny' button in the allow access dialog, or it's
// set to always deny, or the access was denied due to HTTP restrictions;
const onAccessDenied = errorParam => {
const error = cloneDeep(errorParam);
let isIframe;
try {
isIframe = window.self !== window.top;
} catch (err) {// ignore errors, (some browsers throw a security error when accessing cross domain)
}
if ((typeof window !== undefined ? window : global).location.protocol !== 'https:') {
if (isScreenSharing) {
/*
* in http:// the browser will deny permission without asking the
* user. There is also no way to tell if it was denied by the
* user, or prevented from the browser.
*/
error.message += ' Note: https:// is required for screen sharing.';
} else if (OTHelpers.env.name === 'Chrome' && OTHelpers.env.hostName !== 'localhost') {
error.message += ' Note: Chrome requires HTTPS for camera and microphone access.';
}
}
if (isIframe && !isScreenSharing) {
error.message += ' Note: Check that the iframe has the allow attribute for camera and microphone';
}
logging.error(error.message);
state.set('Failed'); // Note: The substring 'Publisher Access Denied:' is part of our api contract for now.
// https://tokbox.com/developer/guides/publish-stream/js/#troubleshooting
error.message = "OT.Publisher Access Denied: Permission Denied: " + error.message;
error.code = ExceptionCodes.UNABLE_TO_PUBLISH;
if (widgetView) {
widgetView.destroy();
widgetView = null;
}
logConnectivityEvent('Cancel', {
reason: 'AccessDenied'
});
this.trigger('publishComplete', error);
this.dispatchEvent(new Event(eventNames.ACCESS_DENIED));
};
const userMediaError = error => {
const isPermissionError = error.name === Errors.USER_MEDIA_ACCESS_DENIED || error.name === Errors.NOT_SUPPORTED && error.originalMessage.match(/Only secure origins/);
if (isPermissionError) {
onAccessDenied(error);
} else if (processPubOptions.isScreenSharing) {
onScreenSharingError(error);
} else {
onStreamAvailableError(error);
}
throw error;
};
const onAccessDialogOpened = () => {
logAnalyticsEvent('accessDialog', 'Opened');
this.dispatchEvent(new Event(eventNames.ACCESS_DIALOG_OPENED, true));
};
const onAccessDialogClosed = () => {
logAnalyticsEvent('accessDialog', 'Closed');
this.dispatchEvent(new Event(eventNames.ACCESS_DIALOG_CLOSED, false));
};
const guid = uuid();
const peerConnectionsAsync = {};
let loaded = false;
let previousAnalyticsStats = {};
let audioAcquisitionProblemDetected = false;
let processedOptions = processPubOptions(options, 'OT.Publisher', () => state && state.isDestroyed());
processedOptions.on({
accessDialogOpened: onAccessDialogOpened,
accessDialogClosed: onAccessDialogClosed
});
const _processedOptions = processedOptions,
isScreenSharing = _processedOptions.isScreenSharing,
isCustomAudioTrack = _processedOptions.isCustomAudioTrack,
isCustomVideoTrack = _processedOptions.isCustomVideoTrack,
shouldAllowAudio = _processedOptions.shouldAllowAudio,
properties = _processedOptions.properties,
getUserMedia = _processedOptions.getUserMedia; // start with undefined
Object.defineProperty(this, 'loudness', {
writable: false,
value: undefined,
configurable: true
});
function removeTrackListeners(trackListeners) {
trackListeners.forEach(off => off());
}
const listenWithOff = (obj, event, listener) => {
if (!obj.addEventListener) {
// noop
return () => {};
}
obj.addEventListener(event, listener);
return () => obj.removeEventListener(event, listener);
};
(function handleAudioEnded() {
const trackListeners = [];
privateEvents.on('streamDestroy', () => removeTrackListeners(trackListeners));
privateEvents.on('streamChange', () => {
removeTrackListeners(trackListeners);
const newListeners = webRTCStream.getAudioTracks().map(track => listenWithOff(track, 'ended', () => {
// chrome audio acquisition issue
audioAcquisitionProblemDetected = true;
this.trigger('audioAcquisitionProblem', {
method: 'trackEndedEvent'
});
}));
trackListeners.splice(0, trackListeners.length, ...newListeners);
});
}).call(this);
(function handleMuteTrack() {
const trackListeners = [];
privateEvents.on('streamDestroy', () => removeTrackListeners(trackListeners));
privateEvents.on('streamChange', () => {
removeTrackListeners(trackListeners); // Screensharing in Chrome sometimes triggers 'mute' and 'unmute'
// repeatedly for now reason OPENTOK-37818
// https://bugs.chromium.org/p/chromium/issues/detail?id=931033
if (!isScreenSharing) {
webRTCStream.getTracks().forEach(track => {
if (track.addEventListener) {
trackListeners.push(listenWithOff(track, 'mute', refreshAudioVideoUI));
trackListeners.push(listenWithOff(track, 'unmute', refreshAudioVideoUI));
}
});
}
});
})(); // / Private Methods
const logAnalyticsEvent = options.logAnalyticsEvent || ((action, variation, payload, logOptions, throttle) => {
let stats = assign({
action,
variation,
payload
}, getCommonAnalyticsFields(), logOptions);
if (variation === 'Failure') {
stats = assign(previousAnalyticsStats, stats);
}
previousAnalyticsStats = pick(stats, 'sessionId', 'connectionId', 'partnerId');
analytics.logEvent(stats, throttle);
});
const logConnectivityEvent = function logConnectivityEvent(variation, payload, logOptions) {
if (payload === void 0) {
payload = {};
}
if (logOptions === void 0) {
logOptions = {};
}
if (logOptions.failureReason === 'Non-fatal') {
// we don't want to log because it was a non-fatal failure
return;
}
if (variation === 'Attempt') {
attemptStartTime = new Date().getTime();
if (connectivityAttemptPinger) {
connectivityAttemptPinger.stop();
logging.error('_connectivityAttemptPinger should have been cleaned up');
}
connectivityAttemptPinger = new IntervalRunner(() => {
logAnalyticsEvent('Publish', 'Attempting', payload, (0, _extends2.default)({}, getCommonAnalyticsFields(), {}, logOptions));
}, 1 / 5, 6);
}
if (variation === 'Failure' || variation === 'Success' || variation === 'Cancel') {
if (connectivityAttemptPinger) {
connectivityAttemptPinger.stop();
connectivityAttemptPinger = undefined;
} else {
logging.warn("Received connectivity event: \"" + variation + "\" without \"Attempt\"");
}
logAnalyticsEvent('Publish', variation, (0, _extends2.default)({
videoInputDevices: videoDevices,
audioInputDevices: audioDevices,
videoInputDeviceCount: videoDevices ? videoDevices.length : undefined,
audioInputDeviceCount: audioDevices ? audioDevices.length : undefined,
selectedVideoInputDeviceId,
selectedAudioInputDeviceId
}, payload), (0, _extends2.default)({
attemptDuration: new Date().getTime() - attemptStartTime
}, logOptions));
} else {
logAnalyticsEvent('Publish', variation, payload, logOptions);
}
};
const logRepublish = (variation, payload) => {
logAnalyticsEvent('ICERestart', variation, payload);
}; // logs an analytics event for getStats on the first call
const notifyGetStatsCalled = once(() => {
logAnalyticsEvent('GetStats', 'Called');
});
const recordQOS = (_ref4) => {
let parsedStats = _ref4.parsedStats,
simulcastEnabled = _ref4.simulcastEnabled,
remoteConnectionId = _ref4.remoteConnectionId,
peerPriority = _ref4.peerPriority,
peerId = _ref4.peerId;
const QoSBlob = {
peerPriority,
peerId,
widgetType: 'Publisher',
connectionId: this.session && this.session.isConnected() ? this.session.connection.connectionId : null,
streamId: lastRequestedStreamId,
width: widgetView.width,
height: widgetView.height,
audioTrack: webRTCStream && webRTCStream.getAudioTracks().length > 0,
hasAudio: hasAudio(),
publishAudio: properties.publishAudio,
videoTrack: webRTCStream && webRTCStream.getVideoTracks().length > 0,
hasVideo: hasVideo(),
publishVideo: properties.publishVideo,
audioSource: isCustomAudioTrack ? 'Custom' : undefined,
videoSource: isScreenSharing && options.videoSource || isCustomVideoTrack && 'Custom' || properties.constraints.video && 'Camera' || null,
duration: publishStartTime ? Math.round((new Date().getTime() - publishStartTime.getTime()) / 1000) : 0,
remoteConnectionId,
scalableVideo: simulcastEnabled
};
const videoDimensions = {
videoWidth: this.videoWidth(),
videoHeight: this.videoHeight()
};
const videoParsedStats = assign(parsedStats, videoDimensions);
analytics.logQOS(assign(QoSBlob, parsedStats));
this.trigger('qos', videoParsedStats);
}; // Returns the video dimensions. Which could either be the ones that
// the developer specific in the videoDimensions property, or just
// whatever the video element reports.
//
// If all else fails then we'll just default to 640x480
//
const getVideoDimensions = () => {
let streamWidth;
let streamHeight;
const video = widgetView && widgetView.video(); // We set the streamWidth and streamHeight to be the minimum of the requested
// resolution and the actual resolution.
if (properties.videoDimensions) {
streamWidth = Math.min(properties.videoDimensions.width, video && video.videoWidth() || 640);
streamHeight = Math.min(properties.videoDimensions.height, video && video.videoHeight() || 480);
} else {
streamWidth = video && video.videoWidth() || 640;
streamHeight = video && video.videoHeight() || 480;
}
return {
width: streamWidth,
height: streamHeight
};
}; // / Private Events
const stateChangeFailed = changeFailed => {
logging.error('OT.Publisher State Change Failed: ', changeFailed.message);
logging.debug(changeFailed);
};
const onLoaded = () => {
if (state.isDestroyed()) {
// The publisher was destroyed before loading finished
if (widgetView) {
widgetView.destroyVideo();
}
return;
}
logging.debug('OT.Publisher.onLoaded; resolution:', this.videoWidth() + "x" + this.videoHeight());
state.set('MediaBound'); // Try unblock audio on all subscribers
unblockAudio().catch(logging.error); // If we have a session and we haven't created the stream yet then
// wait until that is complete before hiding the loading spinner
widgetView.loading(this.session ? !this.stream : false);
loaded = true;
};
const onLoadFailure = plainError => {
// eslint-disable-next-line no-param-reassign
const err = otError(Errors.CONNECT_FAILED, plainError, ExceptionCodes.P2P_CONNECTION_FAILED);
err.message = "OT.Publisher PeerConnection Error: " + err.message;
logConnectivityEvent('Failure', {}, {
failureReason: 'PeerConnectionError',
failureCode: err.code,
failureMessage: err.message
});
state.set('Failed');
this.trigger('publishComplete', err);
OTErrorClass.handleJsException({
error: err,
target: this,
analytics
});
}; // Clean up our LocalMediaStream
const cleanupLocalStream = () => {
if (webRTCStream) {
privateEvents.emit('streamDestroy'); // Stop revokes our access cam and mic access for this instance
// of localMediaStream.
if (windowMock.MediaStreamTrack && windowMock.MediaStreamTrack.prototype.stop) {
// Newer spec
webRTCStream.getTracks().forEach(track => track.stop());
} else {
// Older spec
webRTCStream.stop();
}
}
};
const bindVideo = /*#__PURE__*/function () {
var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
var videoContainerOptions;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
videoContainerOptions = {
muted: true
};
if (widgetView) {
_context.next = 3;
break;
}
throw new Error('Cannot bind video after widget view has been destroyed');
case 3:
return _context.abrupt("return", widgetView.bindVideo(webRTCStream, videoContainerOptions));
case 4:
case "end":
return _context.stop();
}
}, _callee);
}));
return function bindVideo() {
return _ref5.apply(this, arguments);
};
}();
const onStreamAvailable = webOTStream => {
logging.debug('OT.Publisher.onStreamAvailable');
state.set('BindingMedia');
cleanupLocalStream();
webRTCStream = webOTStream;
privateEvents.emit('streamChange');
const findSelectedDeviceId = (tracks, devices) => {
// Store the device labels to log later
let selectedDeviceId;
tracks.forEach(track => {
if (track.deviceId) {
selectedDeviceId = track.deviceId.toString();
} else if (track.label && devices) {
const selectedDevice = find(devices, el => el.label === track.label);
if (selectedDevice) {
selectedDeviceId = selectedDevice.deviceId;
}
}
});
return selectedDeviceId;
};
selectedVideoInputDeviceId = findSelectedDeviceId(webRTCStream.getVideoTracks(), videoDevices);
selectedAudioInputDeviceId = findSelectedDeviceId(webRTCStream.getAudioTracks(), audioDevices);
microphone = new Microphone(webRTCStream, !properties.publishAudio);
updateVideo();
updateAudio();
this.accessAllowed = true;
this.dispatchEvent(new Event(eventNames.ACCESS_ALLOWED, false));
};
const onPublishingTimeout = session => {
logging.error('OT.Publisher.onPublishingTimeout');
let errorName;
let errorMessage;
if (audioAcquisitionProblemDetected) {
errorName = Errors.CHROME_MICROPHONE_ACQUISITION_ERROR;
errorMessage = 'Unable to publish because your browser failed to get access to your ' + 'microphone. You may need to fully quit and restart your browser to get it to work. ' + 'See https://bugs.chromium.org/p/webrtc/issues/detail?id=4799 for more details.';
} else {
errorName = Errors.TIMEOUT;
errorMessage = 'Could not publish in a reasonable amount of time';
}
const logOptions = {
failureReason: 'ICEWorkflow',
failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,
failureMessage: 'OT.Publisher failed to publish in a reasonable amount of time (timeout)'
};
logConnectivityEvent('Failure', {}, logOptions);
OTErrorClass.handleJsException({
errorMsg: logOptions.failureReason,
code: logOptions.failureCode,
target: this,
analytics
});
if (session.isConnected() && this.streamId) {
session._.streamDestroy(this.streamId);
} // Disconnect immediately, rather than wait for the WebSocket to
// reply to our destroyStream message.
this.disconnect();
this.session = null; // We're back to being a stand-alone publisher again.
if (!state.isDestroyed()) {
state.set('MediaBound');
}
this.trigger('publishComplete', otError(errorName, new Error(errorMessage), ExceptionCodes.UNABLE_TO_PUBLISH));
};
const onVideoError = plainErr => {
// eslint-disable-next-line no-param-reassign
const err = otError(Errors.MEDIA_ERR_DECODE, plainErr, ExceptionCodes.UNABLE_TO_PUBLISH);
err.message = "OT.Publisher while playing stream: " + err.message;
logging.error('OT.Publisher.onVideoError:', err);
logAnalyticsEvent('stream', null, {
reason: err.message
}); // Check if attempting to publish *before* overwriting the state
const isAttemptingToPublish = state.isAttemptingToPublish();
state.set('Failed');
if (isAttemptingToPublish) {
this.trigger('publishComplete', err);
} else {
// FIXME: This emits a string instead of an error here for backwards compatibility despite
// being undocumented. When possible we should remove access to this and other undocumented
// events, and restore emitting actual errors here.
this.trigger('error', err.message);
}
OTErrorClass.handleJsException({
error: err,
target: this,
analytics
});
};
this._removePeerConnection = peerConnection => {
const _getPeerConnectionMet = getPeerConnectionMeta(peerConnection),
peerConnectionId = _getPeerConnectionMet.peerConnectionId;
delete peerConnectionsAsync[peerConnectionId];
peerConnection.destroy();
};
this._removeSubscriber = subscriberId => {
getPeerConnectionsBySubscriber(subscriberId).then(peerConnections => {
peerConnections.forEach(pc => this._removePeerConnection(pc));
});
};
const onPeerDisconnected = peerConnection => {
logging.debug('Subscriber has been disconnected from the Publisher\'s PeerConnection');
const _getPeerConnectionMet2 = getPeerConnectionMeta(peerConnection),
peerPriority = _getPeerConnectionMet2.peerPriority,
remoteSubscriberId = _getPeerConnectionMet2.remoteSubscriberId;
const _getPeerConnectionMet3 = getPeerConnectionMeta(peerConnection),
peerConnectionId = _getPeerConnectionMet3.peerConnectionId;
logAnalyticsEvent('disconnect', 'PeerConnection', {
subscriberConnection: peerConnectionId
});
if (peerPriority < currentPeerPriority) {
// cleanup lower-priority peer connection that is expected to go away
this._removePeerConnection(peerConnection);
} else {
this._removeSubscriber(remoteSubscriberId);
}
}; // @todo find out if we get onPeerDisconnected when a failure occurs.
const onPeerConnectionFailure = /*#__PURE__*/function () {
var _ref7 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(peerConnection, _ref6) {
var reason, prefix, sessionInfo, _ref8, _ref8$remoteConnectio, remoteConnectionId, peerConnectionId, error, payload, logOptions, pc;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
reason = _ref6.reason, prefix = _ref6.prefix;
sessionInfo = _this.session && _this.session.sessionInfo;
if (!(prefix === 'ICEWorkflow' && sessionInfo && sessionInfo.reconnection && loaded)) {
_context2.next = 5;
break;
}
// @todo not sure this is the right thing to do
logging.debug('Ignoring peer connection failure due to possibility of reconnection.');
return _context2.abrupt("return");
case 5:
_ref8 = getPeerConnectionMeta(peerConnection) || {}, _ref8$remoteConnectio = _ref8.remoteConnectionId, remoteConnectionId = _ref8$remoteConnectio === void 0 ? '(not found)' : _ref8$remoteConnectio, peerConnectionId = _ref8.peerConnectionId;
error = interpretPeerConnectionError(undefined, reason, prefix, remoteConnectionId, 'Publisher');
payload = {
hasRelayCandidates: peerConnection && peerConnection.hasRelayCandidates()
};
logOptions = {
failureReason: prefix || 'PeerConnectionError',
failureCode: error.code,
failureMessage: error.message
};
if (state.isPublishing()) {
// We're already publishing so this is a Non-fatal failure, must be p2p and one of our
// peerconnections failed
logOptions.reason = 'Non-fatal';
} else {
_this.trigger('publishComplete', error);
}
logConnectivityEvent('Failure', payload, logOptions);
OTErrorClass.handleJsException({
errorMsg: "OT.Publisher PeerConnection Error: " + reason,
code: error.code,
target: _this,
analytics
});
_context2.next = 14;
return peerConnectionsAsync[peerConnectionId];
case 14:
pc = _context2.sent;
pc.destroy();
delete peerConnectionsAsync[peerConnectionId];
case 17:
case "end":
return _context2.stop();
}
}, _callee2);
}));
return function onPeerConnectionFailure(_x, _x2) {
return _ref7.apply(this, arguments);
};
}();
const onIceRestartSuccess = peerConnection => {
const _getPeerConnectionMet4 = getPeerConnectionMeta(peerConnection),
remoteConnectionId = _getPeerConnectionMet4.remoteConnectionId;
logRepublish('Success', {
remoteConnectionId
});
};
const onIceRestartFailure = peerConnection => {
const _getPeerConnectionMet5 = getPeerConnectionMeta(peerConnection),
remoteConnectionId = _getPeerConnectionMet5.remoteConnectionId;
logRepublish('Failure', {
reason: 'ICEWorkflow',
message: 'OT.Publisher PeerConnection Error: ' + 'The stream was unable to connect due to a network error.' + ' Make sure your connection isn\'t blocked by a firewall.',
remoteConnectionId
});
}; // / Private Helpers
// Assigns +stream+ to this publisher. The publisher listens for a bunch of events on the stream
// so it can respond to changes.
const assignStream = stream => {
// the Publisher only expects a stream in the PublishingToSession state
if (state.current !== 'PublishingToSession') {
throw new Error('assignStream called when publisher is not successfully publishing');
}
streamCleanupJobs.releaseAll();
this.stream = stream;
this.stream.on('destroyed', this.disconnect, this);
streamCleanupJobs.add(() => {
if (this.stream) {
this.stream.off('destroyed', this.disconnect, this);
}
});
state.set('Publishing');
widgetView.loading(!loaded);
publishStartTime = new Date();
this.dispatchEvent(new Events.StreamEvent('streamCreated', stream, null, false));
logConnectivityEvent('Success');
this.trigger('publishComplete', null, this);
};
/**
* Provides the peer connection associated to the given peerConnectionId.
*
* It there is no PC associated it creates a new one and stores it so that the next call returns
* the same instance.
*
* @param {Object} configuration
* @param {string} configuration.peerConnectionId
* @returns {Promisevalue
is true
; stops publishing audio
* (if it is currently being published) when the value
is false
.
*
* @param {Boolean} value Whether to start publishing audio (true
)
* or not (false
).
*
* @see OT.initPublisher()
* @see Stream.hasAudio
* @see StreamPropertyChangedEvent
* @method #publishAudio
* @memberOf Publisher
*/
this.publishAudio = value => {
logAnalyticsEvent('publishAudio', 'Attempt', {
publishAudio: value
});
properties.publishAudio = value;
try {
updateAudio();
logAnalyticsEvent('publishAudio', 'Success', {
publishAudio: value
});
} catch (e) {
logAnalyticsEvent('publishAudio', 'Failure', {
message: e.message
});
}
return this;
};
let updateVideoSenderParametersSentinel; // keeps track of if the client has called mediaStreamTrack.stop(), so that we don't restart
// the camera if they then call publishVideo(true)
let isTrackManuallyStopped = false;
const updateVideo = () => {
const shouldSendVideo = haveWorkingTracks('video') && properties.publishVideo;
if (env.name === 'Chrome' && env.version >= 69) {
(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
var executionSentinel, peerConnections;
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
if (updateVideoSenderParametersSentinel) {
updateVideoSenderParametersSentinel.cancel();
}
updateVideoSenderParametersSentinel = new Cancellation();
executionSentinel = updateVideoSenderParametersSentinel;
_context3.next = 5;
return getAllPeerConnections();
case 5:
peerConnections = _context3.sent;
if (!executionSentinel.isCanceled()) {
// only proceed if we weren't canceled during the async operation above
peerConnections.forEach(peerConnection => {
setEncodersActiveState(peerConnection, shouldSendVideo);
});
}
case 7:
case "end":
return _context3.stop();
}
}, _callee3);
}))();
}
if (webRTCStream) {
webRTCStream.getVideoTracks().forEach(track => {
track.enabled = shouldSendVideo; // eslint-disable-line no-param-reassign
if (track.isCreatedCanvas) {
// eslint-disable-next-line no-param-reassign
track.enabled = false;
}
});
}
refreshAudioVideoUI();
};
let currentDeviceId;
this._toggleVideo = blockCallsUntilComplete( /*#__PURE__*/function () {
var _ref13 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(shouldHaveVideo) {
var oldTrack, _properties$videoDime, videoDimensions, canvasTrack, vidDevices, newTrack;
return _regenerator.default.wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
if (!(isScreenSharing || isCustomVideoTrack || isTrackManuallyStopped)) {
_context4.next = 2;
break;
}
return _context4.abrupt("return");
case 2:
oldTrack = getCurrentTrack();
if (oldTrack) {
_context4.next = 5;
break;
}
throw otError(Errors.NOT_SUPPORTED, new Error('Publisher._toggleVideo cannot toggleVideo when you have no video source.'));
case 5:
if (!(oldTrack.readyState === 'ended')) {
_context4.next = 8;
break;
}
isTrackManuallyStopped = true;
return _context4.abrupt("return");
case 8:
// create a canvas and grab the track from it to pass into video
// resize the canvas so that we don't emit a 'streamPropertyChanged' event
_properties$videoDime = properties.videoDimensions, videoDimensions = _properties$videoDime === void 0 ? getVideoDimensions() : _properties$videoDime;
_context4.prev = 9;
canvasTrack = createCanvasVideoTrack(videoDimensions);
_context4.next = 16;
break;
case 13:
_context4.prev = 13;
_context4.t0 = _context4["catch"](9);
return _context4.abrupt("return");
case 16:
_context4.next = 18;
return getVideoDevices();
case 18:
vidDevices = _context4.sent;
if (OTHelpers.env.name === 'Chrome' && OTHelpers.env.userAgent.toLowerCase().indexOf('android') > -1) {
// On Chrome on Android you need to stop the previous video track OPENTOK-37206
if (oldTrack && oldTrack.stop) {
oldTrack.stop();
}
} // store the current deviceId to reaquire the video later
if (!shouldHaveVideo) {
currentDeviceId = vidDevices.find(device => device.label === oldTrack.label).deviceId;
}
if (!(currentDeviceId && vidDevices.findIndex(device => device.deviceId === currentDeviceId) === -1)) {
_context4.next = 23;
break;
}
throw otError(Errors.NO_DEVICES_FOUND, new Error('Previous device no longer available - deviceId not found'));
case 23:
privateEvents.emit('streamDestroy');
if (!shouldHaveVideo) {
_context4.next = 30;
break;
}
_context4.next = 27;
return getTrackFromDeviceId(currentDeviceId);
case 27:
_context4.t1 = _context4.sent;
_context4.next = 31;
break;
case 30:
_context4.t1 = canvasTrack;
case 31:
newTrack = _context4.t1;
_context4.next = 34;
return replaceTrackAndUpdate(oldTrack, newTrack);
case 34:
case "end":
return _context4.stop();
}
}, _callee4, null, [[9, 13]]);
}));
return function (_x3) {
return _ref13.apply(this, arguments);
};
}());
/**
* Starts publishing video (if it is currently not being published)
* when the value
is true
; stops publishing video
* (if it is currently being published) when the value
is false
.
*
* @param {Boolean} value Whether to start publishing video (true
)
* or not (false
).
*
* @see OT.initPublisher()
* @see Stream.hasVideo
* @see StreamPropertyChangedEvent
* @method #publishVideo
* @memberOf Publisher
*/
this.publishVideo = value => {
logAnalyticsEvent('publishVideo', 'Attempt', {
publishVideo: value
});
properties.publishVideo = value;
try {
this._toggleVideo(properties.publishVideo);
updateVideo();
logAnalyticsEvent('publishVideo', 'Success', {
publishVideo: value
});
} catch (e) {
logAnalyticsEvent('publishVideo', 'Failure', {
message: e.message
});
}
return this;
};
/**
* Deletes the Publisher object and removes it from the HTML DOM.
*
* The Publisher object dispatches a destroyed
event when the DOM
* element is removed.
*
You can use the string as the value for a data URL scheme passed to the src parameter of * an image file, as in the following:
* ** var imgData = publisher.getImgData(); * * var img = document.createElement("img"); * img.setAttribute("src", "data:image/png;base64," + imgData); * var imgWin = window.open("about:blank", "Screenshot"); * imgWin.document.write("<body></body>"); * imgWin.document.body.appendChild(img); ** * @method #getImgData * @memberOf Publisher * @return {String} The base-64 encoded string. Returns an empty string if there is no video. */ this.getImgData = function () { if (!loaded) { logging.error('OT.Publisher.getImgData: Cannot getImgData before the Publisher is publishing.'); return null; } const video = widgetView && widgetView.video(); return video ? video.imgData() : null; }; const setNewStream = newStream => { cleanupLocalStream(); webRTCStream = newStream; privateEvents.emit('streamChange'); microphone = new Microphone(webRTCStream, !properties.publishAudio); }; const defaultReplaceTrackLogic = peerConnection => { peerConnection.getSenders().forEach(sender => { if (sender.track.kind === 'audio' && webRTCStream.getAudioTracks().length) { return sender.replaceTrack(webRTCStream.getAudioTracks()[0]); } else if (sender.track.kind === 'video' && webRTCStream.getVideoTracks().length) { return sender.replaceTrack(webRTCStream.getVideoTracks()[0]); } return undefined; }); }; const replaceTracks = function replaceTracks(replaceTrackLogic) { if (replaceTrackLogic === void 0) { replaceTrackLogic = defaultReplaceTrackLogic; } return getAllPeerConnections().then(peerConnections => { const tasks = []; peerConnections.map(replaceTrackLogic); return Promise.all(tasks); }); }; { let videoIndex = 0; /** * Switches the video input source used by the publisher to the next one in the list * of available devices. *
* This will result in an error (the Promise returned by the method is rejected) in the * following conditions: *
videoSource
* option of the OT.initPublisher() method was
* set to null
, false
, a MediaStreamTrack object, or
* "screen"
).
* deviceId
property set to the device ID of the camera used:
*
* * publisher.cycleVideo().then(console.log); * // Output: {deviceId: "967a86e52..."} ** * If there is an error, the promise is rejected. */ this.cycleVideo = blockCallsUntilComplete( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() { var oldTrack, vidDevices, hasOtherVideoDevices, newVideoDevice, deviceId, newTrack; return _regenerator.default.wrap(function _callee5$(_context5) { while (1) switch (_context5.prev = _context5.next) { case 0: if (!(OTHelpers.env.isLegacyEdge || !windowMock.RTCRtpSender || typeof windowMock.RTCRtpSender.prototype.replaceTrack !== 'function')) { _context5.next = 2; break; } throw otError(Errors.UNSUPPORTED_BROWSER, new Error('Publisher#cycleVideo is not supported in your browser.'), ExceptionCodes.UNABLE_TO_PUBLISH); case 2: if (!(isCustomVideoTrack || isScreenSharing)) { _context5.next = 4; break; } throw otError(Errors.NOT_SUPPORTED, new Error('Publisher#cycleVideo: The publisher is not using a camera video source')); case 4: oldTrack = getCurrentTrack(); if (oldTrack) { _context5.next = 7; break; } throw otError(Errors.NOT_SUPPORTED, new Error('Publisher#cycleVideo cannot cycleVideo when you have no video source.')); case 7: videoIndex += 1; _context5.next = 10; return getVideoDevices(); case 10: vidDevices = _context5.sent; // different devices return the cameras in different orders hasOtherVideoDevices = vidDevices.filter(device => device.label !== oldTrack.label).length > 0; if (hasOtherVideoDevices) { while (vidDevices[videoIndex % vidDevices.length].label === oldTrack.label) { videoIndex += 1; } } if (OTHelpers.env.name === 'Chrome' && OTHelpers.env.userAgent.toLowerCase().indexOf('android') > -1) { // On Chrome on Android you need to stop the previous video track OPENTOK-37206 if (oldTrack && oldTrack.stop) { oldTrack.stop(); } } privateEvents.emit('streamDestroy'); newVideoDevice = vidDevices[videoIndex % vidDevices.length]; deviceId = newVideoDevice.deviceId; // ToggleVideo needs to be able to track the deviceId currentDeviceId = deviceId; // we don't want to restart the camera if !publishVideo if (properties.publishVideo) { _context5.next = 20; break; } return _context5.abrupt("return", { deviceId }); case 20: _context5.next = 22; return getTrackFromDeviceId(deviceId); case 22: newTrack = _context5.sent; _context5.next = 25; return replaceTrackAndUpdate(oldTrack, newTrack); case 25: isTrackManuallyStopped = false; return _context5.abrupt("return", { deviceId }); case 27: case "end": return _context5.stop(); } }, _callee5); }))); } const replaceTrackAndUpdate = /*#__PURE__*/function () { var _ref15 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(oldTrack, newTrack) { var pcs, video; return _regenerator.default.wrap(function _callee6$(_context6) { while (1) switch (_context6.prev = _context6.next) { case 0: _context6.next = 2; return getAllPeerConnections(); case 2: pcs = _context6.sent; _context6.next = 5; return Promise.all(pcs.map(pc => pc.findAndReplaceTrack(oldTrack, newTrack))); case 5: webRTCStream.addTrack(newTrack); webRTCStream.removeTrack(oldTrack); if (oldTrack && oldTrack.stop) { oldTrack.stop(); } if (OTHelpers.env.name === 'Firefox' || OTHelpers.env.name === 'Safari') { // Local video freezes on old stream without this for some reason _this.videoElement().srcObject = null; _this.videoElement().srcObject = webRTCStream; } video = widgetView && widgetView.video(); if (video) { video.refreshTracks(); } privateEvents.emit('streamChange'); updateVideo(); case 13: case "end": return _context6.stop(); } }, _callee6); })); return function replaceTrackAndUpdate(_x4, _x5) { return _ref15.apply(this, arguments); }; }(); const getTrackFromDeviceId = /*#__PURE__*/function () { var _ref16 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(deviceId) { var newOptions, _processedOptions2, getUserMediaHelper, newVideoStream; return _regenerator.default.wrap(function _callee7$(_context7) { while (1) switch (_context7.prev = _context7.next) { case 0: newOptions = cloneDeep(options); newOptions.audioSource = null; newOptions.videoSource = deviceId; processedOptions = processPubOptions(newOptions, 'OT.Publisher.getTrackFromDeviceId', () => state && state.isDestroyed()); processedOptions.on({ accessDialogOpened: onAccessDialogOpened, accessDialogClosed: onAccessDialogClosed }); _processedOptions2 = processedOptions, getUserMediaHelper = _processedOptions2.getUserMedia; _context7.next = 8; return getUserMediaHelper().catch(userMediaError); case 8: newVideoStream = _context7.sent; return _context7.abrupt("return", newVideoStream.getVideoTracks()[0]); case 10: case "end": return _context7.stop(); } }, _callee7); })); return function getTrackFromDeviceId(_x6) { return _ref16.apply(this, arguments); }; }(); const getCurrentTrack = () => { const _webRTCStream$getVide = webRTCStream.getVideoTracks(), currentTrack = _webRTCStream$getVide[0]; return currentTrack; }; const getVideoDevices = /*#__PURE__*/function () { var _ref17 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() { var devices, vidDevices; return _regenerator.default.wrap(function _callee8$(_context8) { while (1) switch (_context8.prev = _context8.next) { case 0: _context8.next = 2; return deviceHelpers.shouldAskForDevices(); case 2: devices = _context8.sent; vidDevices = devices.videoDevices; if (!(!devices.video || !vidDevices || !vidDevices.length)) { _context8.next = 6; break; } throw otError(Errors.NO_DEVICES_FOUND, new Error('No video devices available'), ExceptionCodes.UNABLE_TO_PUBLISH); case 6: return _context8.abrupt("return", vidDevices); case 7: case "end": return _context8.stop(); } }, _callee8); })); return function getVideoDevices() { return _ref17.apply(this, arguments); }; }(); const replaceAudioTrack = (oldTrack, newTrack) => { if (newTrack) { webRTCStream.addTrack(newTrack); } if (oldTrack) { webRTCStream.removeTrack(oldTrack); } const video = widgetView && widgetView.video(); if (video) { video.refreshTracks(); } if (chromeMixin) { if (newTrack && !oldTrack) { chromeMixin.addAudioTrack(); } if (oldTrack && !newTrack) { chromeMixin.removeAudioTrack(); } } if (oldTrack && oldTrack.stop) { oldTrack.stop(); } if (newTrack) { // Turn the audio back on if the audio track stopped because it was disconnected updateAudio(); microphone = new Microphone(webRTCStream, !properties.publishAudio); } privateEvents.emit('streamChange'); refreshAudioVideoUI(); }; /** * Switches the audio input source used by the publisher. You can set the *
audioSource
to a device ID (string) or audio MediaStreamTrack object.
* * This will result in an error (the Promise returned by the method is rejected) in the * following conditions: *
audioSource
* option of the OT.initPublisher() method was
* set to null
or false
).
* audioSource
value is not a string or MediaStreamTrack object.
* audioSource
string is not a valid audio input device available
* to the browser.
* resolution
property passed in as the properties
property
* the options passed into the OT.initPublisher()
method, if the browser
* does not support the requested resolution.
*
* @method #videoWidth
* @memberOf Publisher
* @return {Number} the width, in pixels, of the Publisher video.
*/
this.videoWidth = function () {
const video = widgetView && widgetView.video();
return video ? video.videoWidth() : undefined;
};
/**
* Returns the height, in pixels, of the Publisher video. This may differ from the
* resolution
property passed in as the properties
property
* the options passed into the OT.initPublisher()
method, if the browser
* does not support the requested resolution.
*
* @method #videoHeight
* @memberOf Publisher
* @return {Number} the height, in pixels, of the Publisher video.
*/
this.videoHeight = function () {
const video = widgetView && widgetView.video();
return video ? video.videoHeight() : undefined;
};
/**
* Returns the details on the publisher's stream quality, including the following:
*
* error
(Error) — Upon successful completion
* the method, this is undefined. An error results if the publisher is not connected to a
* session or if it is not publishing audio or video.statsArray
(Array) — An array of objects defining the current
* audio-video statistics for the publisher. For a publisher in a routed session (one that
* uses the OpenTok
* Media Router), this array includes one object, defining the statistics for the single
* audio-media stream that is sent to the OpenTok Media Router. In a relayed session, the
* array includes an object for each subscriber to the published stream. Each object in the
* array contains a stats
property that includes the following properties:
*
* *
audio.bytesSent
(Number) — The total number of audio bytes
* sent to the subscriber (or to the OpenTok Media Router)audio.packetsLost
(Number) — The total number audio packets
* that did not reach the subscriber (or to the OpenTok Media Router)audio.packetsSent
(Number) — The total number of audio
* packets sent to the subscriber (or to the OpenTok Media Router)timestamp
(Number) — The timestamp, in milliseconds since
* the Unix epoch, for when these stats were gatheredvideo.bytesSent
(Number) — The total video bytes sent to
* the subscriber (or to the OpenTok Media Router)video.packetsLost
(Number) — The total number of video packets
* that did not reach the subscriber (or to the OpenTok Media Router)video.packetsSent
(Number) — The total number of video
* packets sent to the subscribervideo.frameRate
(Number) — The current video frame rateAdditionally, for a publisher in a relayed session, each object in the array contains * the following two properties: * *
connectionId
(String) — The unique ID of the client's
* connection, which matches the id
property of the connection
* property of the connectionCreated
* event that the Session object dispatched for the remote client.subscriberId
(String) — The unique ID of the subscriber, which
* matches the id
property of the Subscriber object in the subscribing
* client's app.These two properties are undefined for a publisher in a routed session. * *
accessAllowed
property which indicates whether the user
* has granted access to the camera and microphone.
* @see Event
* @name accessAllowed
* @event
* @memberof Publisher
*/
/**
* Dispatched when the user has clicked the Deny button, preventing the
* app from having access to the camera and microphone.
*
* Note: On macOS 10.15+ (Catalina), to publish a screen-sharing stream
* the user must grant the browser access to the screen in macOS System Preferences >
* Security & Privacy > Privacy > Screen Recording. Otherwise,
* the Publisher will dispatch an accessDenied
event.
*
* @see Event
* @name accessDenied
* @event
* @memberof Publisher
*/
/**
* Dispatched when the Allow/Deny dialog box is opened. (This is the dialog box in which
* the user can grant the app access to the camera and microphone.)
* @see Event
* @name accessDialogOpened
* @event
* @memberof Publisher
*/
/**
* Dispatched when the Allow/Deny box is closed. (This is the dialog box in which the
* user can grant the app access to the camera and microphone.)
* @see Event
* @name accessDialogClosed
* @event
* @memberof Publisher
*/
/**
* Dispatched periodically to indicate the publisher's audio level. The event is dispatched
* up to 60 times per second, depending on the browser. The audioLevel
property
* of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more
* information.
*
* The following example adjusts the value of a meter element that shows volume of the * publisher. Note that the audio level is adjusted logarithmically and a moving average * is applied: *
*
* var movingAvg = null; * publisher.on('audioLevelUpdated', function(event) { * if (movingAvg === null || movingAvg <= event.audioLevel) { * movingAvg = event.audioLevel; * } else { * movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel; * } * * // 1.5 scaling to map the -30 - 0 dBm range to [0,1] * var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1; * logLevel = Math.min(Math.max(logLevel, 0), 1); * document.getElementById('publisherMeter').value = logLevel; * }); **
This example shows the algorithm used by the default audio level indicator displayed
* in an audio-only Publisher.
*
* @name audioLevelUpdated
* @event
* @memberof Publisher
* @see AudioLevelUpdatedEvent
*/
/**
* The publisher has started streaming to the session.
* @name streamCreated
* @event
* @memberof Publisher
* @see StreamEvent
* @see Session.publish()
*/
/**
* The publisher has stopped streaming to the session. The default behavior is that
* the Publisher object is removed from the HTML DOM. The Publisher object dispatches a
* destroyed
event when the element is removed from the HTML DOM. If you call the
* preventDefault()
method of the event object in the event listener, the default
* behavior is prevented, and you can, optionally, retain the Publisher for reuse or clean it up
* using your own code.
* @name streamDestroyed
* @event
* @memberof Publisher
* @see StreamEvent
*/
/**
* Dispatched when the Publisher element is removed from the HTML DOM. When this event
* is dispatched, you may choose to adjust or remove HTML DOM elements related to the publisher.
* @name destroyed
* @event
* @memberof Publisher
*/
/**
* Dispatched when the video dimensions of the video change. This can only occur in when the
* stream.videoType
property is set to "screen"
(for a screen-sharing
* video stream), when the user resizes the window being captured. This event object has a
* newValue
property and an oldValue
property, representing the new and
* old dimensions of the video. Each of these has a height
property and a
* width
property, representing the height and width, in pixels.
* @name videoDimensionsChanged
* @event
* @memberof Publisher
* @see VideoDimensionsChangedEvent
*/
/**
* Dispatched when the Publisher's video element is created. Add a listener for this event when
* you set the insertDefaultUI
option to false
in the call to the
* OT.initPublisher() method. The element
* property of the event object is a reference to the Publisher's video
element
* (or in Internet Explorer the object
element containing the video). Add it to
* the HTML DOM to display the video. When you set the insertDefaultUI
option to
* false
, the video
(or object
) element is not
* automatically inserted into the DOM.
*
* Add a listener for this event only if you have set the insertDefaultUI
option to
* false
. If you have not set insertDefaultUI
option to
* false
, do not move the video
(or object
) element in
* in the HTML DOM. Doing so causes the Publisher object to be destroyed.
*
* @name videoElementCreated
* @event
* @memberof Publisher
* @see VideoElementCreatedEvent
*/
/**
* The user publishing the stream has stopped sharing one or all media
* types (video, audio and/or screen). This can occur when a user disconnects a camera or
* microphone used as a media source for the Publisher. Or it can occur when a user closes
* a when the video and audio sources of the stream are MediaStreamTrack elements and
* tracks are stopped or destroyed.
*
* @name mediaStopped
* @event
* @memberof Publisher
* @see MediaStoppedEvent
*/
return Publisher;
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 164 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const sessionObjects = __webpack_require__(22);
const ProxyUrlWarnings = __webpack_require__(294);
let proxyUrl;
module.exports = {
setProxyUrl: url => {
const hasSessionInitalized = sessionObjects.sessions.length() > 0;
const hasPublisherInitalized = sessionObjects.publishers.length() > 0;
if (hasPublisherInitalized || hasSessionInitalized) {
// @TODO WILL NEED TO UPDATE THESE console.warns TO EXCEPTIONS IN 2.18.0
console.warn(ProxyUrlWarnings.TEARDOWN_REQUIRED);
} else if (proxyUrl) {
console.warn(ProxyUrlWarnings.ALREADY_SET);
} else {
// eslint-disable-next-line no-param-reassign
proxyUrl = url;
}
},
getProxyUrl: () => proxyUrl,
// this method should only be exposed to tests
clearProxyUrl: () => {
proxyUrl = undefined;
}
};
/***/ }),
/* 165 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const hasOpenTokSupport = __webpack_require__(56).once;
/**
* A class defining properties of the capabilities
property of a
* Session object. See Session.capabilities.
*
* All Capabilities properties are undefined until you have connected to a session
* and the Session object has dispatched the sessionConnected
event.
*
* For more information on token roles, see the
* Token Creation Overview.
*
* @class Capabilities
*
* @property {Number} forceDisconnect Specifies whether you can call
* the Session.forceDisconnect()
method (1) or not (0). To call the
* Session.forceDisconnect()
method,
* the user must have a token that is assigned the role of moderator.
* @property {Number} forceUnpublish Specifies whether you can call
* the Session.forceUnpublish()
method (1) or not (0). To call the
* Session.forceUnpublish()
method, the user must have a token that
* is assigned the role of moderator.
* @property {Number} publish Specifies whether you can publish to the session (1) or not (0).
* The ability to publish is based on a few factors. To publish, the user must have a token that
* is assigned a role that supports publishing. There must be a connected camera and microphone.
* @property {Number} subscribe Specifies whether you can subscribe to streams
* in the session (1) or not (0). Currently, this capability is available for all users on all
* platforms.
*/
module.exports = function Capabilities(permissions) {
this.publish = permissions.indexOf('publish') !== -1 ? 1 : 0;
this.subscribe = permissions.indexOf('subscribe') !== -1 ? 1 : 0;
this.forceUnpublish = permissions.indexOf('forceunpublish') !== -1 ? 1 : 0;
this.forceDisconnect = permissions.indexOf('forcedisconnect') !== -1 ? 1 : 0;
this.supportsWebRTC = hasOpenTokSupport() ? 1 : 0;
this.permittedTo = action => Object.prototype.hasOwnProperty.call(this, action) && this[action] === 1;
};
/***/ }),
/* 166 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
exports.__esModule = true;
exports.STATUS = exports.PONG = exports.PING = exports.DISCONNECT = exports.CONNECT = exports.MESSAGE = exports.UNSUBSCRIBE = exports.SUBSCRIBE = void 0;
// Rumor Messaging for JS
//
// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork
//
// @todo Rumor {
// Add error codes for all the error cases
// Add Dependability commands
// }
// This is used to subscribe to address/addresses. The address/addresses the
// client specifies here is registered on the server. Once any message is sent to
// that address/addresses, the client receives that message.
const SUBSCRIBE = 0; // This is used to unsubscribe to address / addresses. Once the client unsubscribe
// to an address, it will stop getting messages sent to that address.
exports.SUBSCRIBE = SUBSCRIBE;
const UNSUBSCRIBE = 1; // This is used to send messages to arbitrary address/ addresses. Messages can be
// anything and Rumor will not care about what is included.
exports.UNSUBSCRIBE = UNSUBSCRIBE;
const MESSAGE = 2; // This will be the first message that the client sends to the server. It includes
// the uniqueId for that client connection and a disconnect_notify address that will
// be notified once the client disconnects.
exports.MESSAGE = MESSAGE;
const CONNECT = 3; // This will be the message used by the server to notify an address that a
// client disconnected.
exports.CONNECT = CONNECT;
const DISCONNECT = 4; // Enhancements to support Keepalives
exports.DISCONNECT = DISCONNECT;
const PING = 7;
exports.PING = PING;
const PONG = 8;
exports.PONG = PONG;
const STATUS = 9;
exports.STATUS = STATUS;
/***/ }),
/* 167 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const assign = __webpack_require__(6);
const Capabilities = __webpack_require__(165);
const eventing = __webpack_require__(5);
const Events = __webpack_require__(20)();
const connectionCapabilities = __webpack_require__(676);
/**
* The Connection object represents a connection to an OpenTok session. Each client that connects
* to a session has a unique connection, with a unique connection ID (represented by the
* id
property of the Connection object for the client).
*
* The Session object has a connection
property that is a Connection object.
* It represents the local client's connection. (A client only has a connection once the
* client has successfully called the connect()
method of the {@link Session}
* object.)
*
* The Session object dispatches a connectionCreated
event when each client (including
* your own) connects to a session (and for clients that are present in the session when you
* connect). The connectionCreated
event object has a connection
* property, which is a Connection object corresponding to the client the event pertains to.
*
* The Stream object has a connection
property that is a Connection object.
* It represents the connection of the client that is publishing the stream.
*
* @class Connection
* @property {String} connectionId The ID of this connection.
* @property {Number} creationTime The timestamp for the creation of the connection. This
* value is calculated in milliseconds.
* You can convert this value to a Date object by calling new Date(creationTime)
,
* where creationTime
* is the creationTime
property of the Connection object.
* @property {String} data A string containing metadata describing the
* connection. When you generate a user token, you can define connection data (see the
* Token creation overview).
*/
function Connection(id, creationTime, data, capabilitiesHash, permissionsHash) {
let destroyedReason;
this.id = id;
this.connectionId = id;
this.creationTime = creationTime ? Number(creationTime) : null;
this.data = data;
this.capabilities = connectionCapabilities(capabilitiesHash);
this.permissions = new Capabilities(permissionsHash);
this.quality = null;
eventing(this);
this.destroy = (reason, quiet) => {
destroyedReason = reason || 'clientDisconnected';
if (quiet !== true) {
this.dispatchEvent(new Events.DestroyedEvent( // This should be eventNames.CONNECTION_DESTROYED, but
// the value of that is currently shared with Session
'destroyed', this, destroyedReason));
}
};
this.destroyed = () => destroyedReason !== undefined;
this.destroyedReason = () => destroyedReason;
}
Connection.fromHash = (_ref) => {
let id = _ref.id,
creationTime = _ref.creationTime,
data = _ref.data,
capablities = _ref.capablities,
permissions = _ref.permissions;
return new Connection(id, creationTime, data, assign(capablities || {}, {
supportsWebRTC: true
}), permissions || []);
};
module.exports = Connection;
/***/ }),
/* 168 */
/***/ (function(module, exports) {
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof (typeof window !== undefined ? window : global) == 'object' && (typeof window !== undefined ? window : global) && (typeof window !== undefined ? window : global).Object === Object && (typeof window !== undefined ? window : global);
module.exports = freeGlobal;
/***/ }),
/* 169 */
/***/ (function(module, exports) {
/** Used for built-in method references. */
var funcProto = Function.prototype;
/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;
/**
* Converts `func` to its source code.
*
* @private
* @param {Function} func The function to convert.
* @returns {string} Returns the source code.
*/
function toSource(func) {
if (func != null) {
try {
return funcToString.call(func);
} catch (e) {}
try {
return (func + '');
} catch (e) {}
}
return '';
}
module.exports = toSource;
/***/ }),
/* 170 */
/***/ (function(module, exports, __webpack_require__) {
var getNative = __webpack_require__(36);
var defineProperty = (function() {
try {
var func = getNative(Object, 'defineProperty');
func({}, '', {});
return func;
} catch (e) {}
}());
module.exports = defineProperty;
/***/ }),
/* 171 */
/***/ (function(module, exports, __webpack_require__) {
var copyObject = __webpack_require__(38),
keys = __webpack_require__(27);
/**
* The base implementation of `_.assign` without support for multiple sources
* or `customizer` functions.
*
* @private
* @param {Object} object The destination object.
* @param {Object} source The source object.
* @returns {Object} Returns `object`.
*/
function baseAssign(object, source) {
return object && copyObject(source, keys(source), object);
}
module.exports = baseAssign;
/***/ }),
/* 172 */
/***/ (function(module, exports, __webpack_require__) {
var baseTimes = __webpack_require__(355),
isArguments = __webpack_require__(61),
isArray = __webpack_require__(9),
isBuffer = __webpack_require__(62),
isIndex = __webpack_require__(63),
isTypedArray = __webpack_require__(82);
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Creates an array of the enumerable property names of the array-like `value`.
*
* @private
* @param {*} value The value to query.
* @param {boolean} inherited Specify returning inherited property names.
* @returns {Array} Returns the array of property names.
*/
function arrayLikeKeys(value, inherited) {
var isArr = isArray(value),
isArg = !isArr && isArguments(value),
isBuff = !isArr && !isArg && isBuffer(value),
isType = !isArr && !isArg && !isBuff && isTypedArray(value),
skipIndexes = isArr || isArg || isBuff || isType,
result = skipIndexes ? baseTimes(value.length, String) : [],
length = result.length;
for (var key in value) {
if ((inherited || hasOwnProperty.call(value, key)) &&
!(skipIndexes && (
// Safari 9 has enumerable `arguments.length` in strict mode.
key == 'length' ||
// Node.js 0.10 has enumerable non-index properties on buffers.
(isBuff && (key == 'offset' || key == 'parent')) ||
// PhantomJS 2 has enumerable non-index properties on typed arrays.
(isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
// Skip index properties.
isIndex(key, length)
))) {
result.push(key);
}
}
return result;
}
module.exports = arrayLikeKeys;
/***/ }),
/* 173 */
/***/ (function(module, exports) {
/**
* Creates a unary function that invokes `func` with its argument transformed.
*
* @private
* @param {Function} func The function to wrap.
* @param {Function} transform The argument transform.
* @returns {Function} Returns the new function.
*/
function overArg(func, transform) {
return function(arg) {
return func(transform(arg));
};
}
module.exports = overArg;
/***/ }),
/* 174 */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(10);
/** Detect free variable `exports`. */
var freeExports = true && exports && !exports.nodeType && exports;
/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;
/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined,
allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined;
/**
* Creates a clone of `buffer`.
*
* @private
* @param {Buffer} buffer The buffer to clone.
* @param {boolean} [isDeep] Specify a deep clone.
* @returns {Buffer} Returns the cloned buffer.
*/
function cloneBuffer(buffer, isDeep) {
if (isDeep) {
return buffer.slice();
}
var length = buffer.length,
result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
buffer.copy(result);
return result;
}
module.exports = cloneBuffer;
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(81)(module)))
/***/ }),
/* 175 */
/***/ (function(module, exports) {
/**
* This method returns a new empty array.
*
* @static
* @memberOf _
* @since 4.13.0
* @category Util
* @returns {Array} Returns the new empty array.
* @example
*
* var arrays = _.times(2, _.stubArray);
*
* console.log(arrays);
* // => [[], []]
*
* console.log(arrays[0] === arrays[1]);
* // => false
*/
function stubArray() {
return [];
}
module.exports = stubArray;
/***/ }),
/* 176 */
/***/ (function(module, exports, __webpack_require__) {
var arrayPush = __webpack_require__(113),
getPrototype = __webpack_require__(114),
getSymbols = __webpack_require__(112),
stubArray = __webpack_require__(175);
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols;
/**
* Creates an array of the own and inherited enumerable symbols of `object`.
*
* @private
* @param {Object} object The object to query.
* @returns {Array} Returns the array of symbols.
*/
var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
var result = [];
while (object) {
arrayPush(result, getSymbols(object));
object = getPrototype(object);
}
return result;
};
module.exports = getSymbolsIn;
/***/ }),
/* 177 */
/***/ (function(module, exports, __webpack_require__) {
var baseGetAllKeys = __webpack_require__(178),
getSymbols = __webpack_require__(112),
keys = __webpack_require__(27);
/**
* Creates an array of own enumerable property names and symbols of `object`.
*
* @private
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names and symbols.
*/
function getAllKeys(object) {
return baseGetAllKeys(object, keys, getSymbols);
}
module.exports = getAllKeys;
/***/ }),
/* 178 */
/***/ (function(module, exports, __webpack_require__) {
var arrayPush = __webpack_require__(113),
isArray = __webpack_require__(9);
/**
* The base implementation of `getAllKeys` and `getAllKeysIn` which uses
* `keysFunc` and `symbolsFunc` to get the enumerable property names and
* symbols of `object`.
*
* @private
* @param {Object} object The object to query.
* @param {Function} keysFunc The function to get the keys of `object`.
* @param {Function} symbolsFunc The function to get the symbols of `object`.
* @returns {Array} Returns the array of property names and symbols.
*/
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
var result = keysFunc(object);
return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}
module.exports = baseGetAllKeys;
/***/ }),
/* 179 */
/***/ (function(module, exports, __webpack_require__) {
var baseGetAllKeys = __webpack_require__(178),
getSymbolsIn = __webpack_require__(176),
keysIn = __webpack_require__(66);
/**
* Creates an array of own and inherited enumerable property names and
* symbols of `object`.
*
* @private
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names and symbols.
*/
function getAllKeysIn(object) {
return baseGetAllKeys(object, keysIn, getSymbolsIn);
}
module.exports = getAllKeysIn;
/***/ }),
/* 180 */
/***/ (function(module, exports, __webpack_require__) {
var getNative = __webpack_require__(36),
root = __webpack_require__(10);
/* Built-in method references that are verified to be native. */
var Set = getNative(root, 'Set');
module.exports = Set;
/***/ }),
/* 181 */
/***/ (function(module, exports, __webpack_require__) {
var getNative = __webpack_require__(36),
root = __webpack_require__(10);
/* Built-in method references that are verified to be native. */
var WeakMap = getNative(root, 'WeakMap');
module.exports = WeakMap;
/***/ }),
/* 182 */
/***/ (function(module, exports, __webpack_require__) {
var root = __webpack_require__(10);
/** Built-in value references. */
var Uint8Array = root.Uint8Array;
module.exports = Uint8Array;
/***/ }),
/* 183 */
/***/ (function(module, exports, __webpack_require__) {
var cloneArrayBuffer = __webpack_require__(115);
/**
* Creates a clone of `typedArray`.
*
* @private
* @param {Object} typedArray The typed array to clone.
* @param {boolean} [isDeep] Specify a deep clone.
* @returns {Object} Returns the cloned typed array.
*/
function cloneTypedArray(typedArray, isDeep) {
var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
}
module.exports = cloneTypedArray;
/***/ }),
/* 184 */
/***/ (function(module, exports, __webpack_require__) {
var baseCreate = __webpack_require__(83),
getPrototype = __webpack_require__(114),
isPrototype = __webpack_require__(65);
/**
* Initializes an object clone.
*
* @private
* @param {Object} object The object to clone.
* @returns {Object} Returns the initialized clone.
*/
function initCloneObject(object) {
return (typeof object.constructor == 'function' && !isPrototype(object))
? baseCreate(getPrototype(object))
: {};
}
module.exports = initCloneObject;
/***/ }),
/* 185 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable global-require */
const staticConfigFactory = __webpack_require__(28);
const StaticConfig = staticConfigFactory();
/**
* @typedef errorReportingFactoryDeps
* @property {StaticConfig} staticConfig
*/
module.exports = function errorReportingFactory(
/** @type errorReportingFactoryDeps */
deps) {
if (deps === void 0) {
deps = {};
}
const APIKEY = deps.APIKEY || __webpack_require__(69);
const logging = deps.logging || __webpack_require__(1)('errorReporting');
const staticConfig = deps.staticConfig || StaticConfig.onlyLocal();
const isEnabled = staticConfig.enableErrorReporting;
let context = deps.global;
if (!context && typeof window !== 'undefined') {
context = window;
}
let Raven = deps.Raven;
if (!Raven && context) {
Raven = __webpack_require__(518);
}
const whitelistUrlPattern = /(opentok|ot|tb|tokbox)(?:\.min)?\.js/;
/*
* Blocks error reporting until it has been explicitly enabled.
*
* The otError tag workaround is necessary because raven-js will
* automatically wrap global error listeners. We prevent this by
* only accepting errors that have the otError tag set by our
* generateTags() function. It is deleted because it's not used
* after this point.
*/
const shouldSendCallback = errorData => {
if (!isEnabled) {
return false;
}
if (errorData && errorData.tags && errorData.tags.otError) {
delete errorData.tags.otError; // eslint-disable-line no-param-reassign
return true;
}
return false;
};
const installRaven = () => {
if (Raven) {
Raven.config(staticConfig.sentryDSN, {
whitelistUrls: [whitelistUrlPattern],
logger: 'webrtc-js',
release: staticConfig.version,
autoBreadcrumbs: false,
shouldSendCallback
}).install().noConflict();
}
};
/*
* The rawError object must be normalised to an instance of Error because
* raven-js will cast anything else to a string. This prevents objects
* like ExceptionEvent from being reported as "[object Object]".
*
* @param {Error|Object|String} rawError
* @return {Error|String}
*/
const normaliseError = rawError => {
let error;
if (rawError instanceof Error || typeof rawError === 'string') {
error = rawError;
} else if (rawError == null) {
error = new Error('Undefined or null error was reported!');
} else {
error = new Error(rawError.message);
['name', 'title', 'code', 'stack'].forEach(key => {
if (rawError[key]) {
error[key] = rawError[key];
}
});
}
if (error instanceof Error && !error.stack) {
try {
throw error;
} catch (e) {// IE populates the error's .stack when it is thrown, nothing to do here
}
}
return error;
};
/*
* We generate a fingerprint because some unrelated errors can end up
* being grouped together as the same issue, particularly normalised
* errors that end up with identical stacktraces.
* https://docs.sentry.io/learn/rollups/#customize-grouping-with-fingerprints
*/
const generateFingerprint = error => {
const fingerprint = ['{{ default }}'];
if (typeof error === 'string') {
fingerprint.push("message::" + error);
} else if (error != null) {
['message', 'name', 'title', 'code'].forEach(key => {
if (error[key]) {
fingerprint.push(key + "::" + error[key]);
}
});
}
return fingerprint;
};
const generateTags = error => {
const tags = {
otError: true,
partnerId: APIKEY.value,
buildHash: staticConfig.buildHash
};
if (error instanceof Error) {
['name', 'title', 'code'].forEach(key => {
if (error[key]) {
tags["error." + key] = error[key];
}
});
}
return tags;
};
const errorReporting = {
/*
* Reports an error to Sentry.io if error reporting has been enabled.
*
* @param {Error|Object|String} rawError
* @return {String} Report event ID
*/
send(rawError) {
if (!isEnabled || !Raven) {
return null;
}
const error = normaliseError(rawError);
const options = {
fingerprint: generateFingerprint(error),
tags: generateTags(error)
};
Raven.captureException(error, options);
const lastId = Raven.lastEventId();
logging.debug("Reported error with ID: " + lastId, error, options);
return lastId;
}
};
const attachGlobalListener = () => {
context.addEventListener('error', e => {
if (e.error == null) {
// unfortunately we can't do much about errors we know nothing about
return;
}
errorReporting.send(e.error);
});
};
if (isEnabled) {
attachGlobalListener();
installRaven();
}
return errorReporting;
};
/***/ }),
/* 186 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(377);
/***/ }),
/* 187 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
};
/***/ }),
/* 188 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(Promise) {
var utils = __webpack_require__(16);
var settle = __webpack_require__(381);
var buildURL = __webpack_require__(383);
var parseHeaders = __webpack_require__(384);
var isURLSameOrigin = __webpack_require__(385);
var createError = __webpack_require__(189);
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
var requestData = config.data;
var requestHeaders = config.headers;
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
var request = new XMLHttpRequest();
// HTTP basic authentication
if (config.auth) {
var username = config.auth.username || '';
var password = config.auth.password || '';
requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
}
request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
// Set the request timeout in MS
request.timeout = config.timeout;
// Listen for ready state
request.onreadystatechange = function handleLoad() {
if (!request || request.readyState !== 4) {
return;
}
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
// Prepare the response
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
settle(resolve, reject, response);
// Clean up request
request = null;
};
// Handle low level network errors
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config, null, request));
// Clean up request
request = null;
};
// Handle timeout
request.ontimeout = function handleTimeout() {
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
request));
// Clean up request
request = null;
};
// Add xsrf header
// This is only done if running in a standard browser environment.
// Specifically not if we're in a web worker, or react-native.
if (utils.isStandardBrowserEnv()) {
var cookies = __webpack_require__(386);
// Add xsrf header
var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
// Add headers to the request
if ('setRequestHeader' in request) {
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
// Remove Content-Type if data is undefined
delete requestHeaders[key];
} else {
// Otherwise add header to the request
request.setRequestHeader(key, val);
}
});
}
// Add withCredentials to request if needed
if (config.withCredentials) {
request.withCredentials = true;
}
// Add responseType to request if needed
if (config.responseType) {
try {
request.responseType = config.responseType;
} catch (e) {
// Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
// But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
if (config.responseType !== 'json') {
throw e;
}
}
}
// Handle progress if needed
if (typeof config.onDownloadProgress === 'function') {
request.addEventListener('progress', config.onDownloadProgress);
}
// Not all browsers support upload events
if (typeof config.onUploadProgress === 'function' && request.upload) {
request.upload.addEventListener('progress', config.onUploadProgress);
}
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
if (requestData === undefined) {
requestData = null;
}
// Send the request
request.send(requestData);
});
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 189 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var enhanceError = __webpack_require__(382);
/**
* Create an Error with the specified message, config, error code, request and response.
*
* @param {string} message The error message.
* @param {Object} config The config.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
* @param {Object} [request] The request.
* @param {Object} [response] The response.
* @returns {Error} The created error.
*/
module.exports = function createError(message, config, code, request, response) {
var error = new Error(message);
return enhanceError(error, config, code, request, response);
};
/***/ }),
/* 190 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function isCancel(value) {
return !!(value && value.__CANCEL__);
};
/***/ }),
/* 191 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/**
* A `Cancel` is an object that is thrown when an operation is canceled.
*
* @class
* @param {string=} message The message.
*/
function Cancel(message) {
this.message = message;
}
Cancel.prototype.toString = function toString() {
return 'Cancel' + (this.message ? ': ' + this.message : '');
};
Cancel.prototype.__CANCEL__ = true;
module.exports = Cancel;
/***/ }),
/* 192 */
/***/ (function(module, exports, __webpack_require__) {
var baseConvert = __webpack_require__(395),
util = __webpack_require__(397);
/**
* Converts `func` of `name` to an immutable auto-curried iteratee-first data-last
* version with conversion `options` applied. If `name` is an object its methods
* will be converted.
*
* @param {string} name The name of the function to wrap.
* @param {Function} [func] The function to wrap.
* @param {Object} [options] The options object. See `baseConvert` for more details.
* @returns {Function|Object} Returns the converted function or object.
*/
function convert(name, func, options) {
return baseConvert(util, name, func, options);
}
module.exports = convert;
/***/ }),
/* 193 */
/***/ (function(module, exports, __webpack_require__) {
var identity = __webpack_require__(84),
metaMap = __webpack_require__(194);
/**
* The base implementation of `setData` without support for hot loop shorting.
*
* @private
* @param {Function} func The function to associate metadata with.
* @param {*} data The metadata.
* @returns {Function} Returns `func`.
*/
var baseSetData = !metaMap ? identity : function(func, data) {
metaMap.set(func, data);
return func;
};
module.exports = baseSetData;
/***/ }),
/* 194 */
/***/ (function(module, exports, __webpack_require__) {
var WeakMap = __webpack_require__(181);
/** Used to store function metadata. */
var metaMap = WeakMap && new WeakMap;
module.exports = metaMap;
/***/ }),
/* 195 */
/***/ (function(module, exports, __webpack_require__) {
var composeArgs = __webpack_require__(196),
composeArgsRight = __webpack_require__(197),
countHolders = __webpack_require__(401),
createCtor = __webpack_require__(85),
createRecurry = __webpack_require__(198),
getHolder = __webpack_require__(206),
reorder = __webpack_require__(415),
replaceHolders = __webpack_require__(123),
root = __webpack_require__(10);
/** Used to compose bitmasks for function metadata. */
var WRAP_BIND_FLAG = 1,
WRAP_BIND_KEY_FLAG = 2,
WRAP_CURRY_FLAG = 8,
WRAP_CURRY_RIGHT_FLAG = 16,
WRAP_ARY_FLAG = 128,
WRAP_FLIP_FLAG = 512;
/**
* Creates a function that wraps `func` to invoke it with optional `this`
* binding of `thisArg`, partial application, and currying.
*
* @private
* @param {Function|string} func The function or method name to wrap.
* @param {number} bitmask The bitmask flags. See `createWrap` for more details.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {Array} [partials] The arguments to prepend to those provided to
* the new function.
* @param {Array} [holders] The `partials` placeholder indexes.
* @param {Array} [partialsRight] The arguments to append to those provided
* to the new function.
* @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
* @param {Array} [argPos] The argument positions of the new function.
* @param {number} [ary] The arity cap of `func`.
* @param {number} [arity] The arity of `func`.
* @returns {Function} Returns the new wrapped function.
*/
function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
var isAry = bitmask & WRAP_ARY_FLAG,
isBind = bitmask & WRAP_BIND_FLAG,
isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
isFlip = bitmask & WRAP_FLIP_FLAG,
Ctor = isBindKey ? undefined : createCtor(func);
function wrapper() {
var length = arguments.length,
args = Array(length),
index = length;
while (index--) {
args[index] = arguments[index];
}
if (isCurried) {
var placeholder = getHolder(wrapper),
holdersCount = countHolders(args, placeholder);
}
if (partials) {
args = composeArgs(args, partials, holders, isCurried);
}
if (partialsRight) {
args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
}
length -= holdersCount;
if (isCurried && length < arity) {
var newHolders = replaceHolders(args, placeholder);
return createRecurry(
func, bitmask, createHybrid, wrapper.placeholder, thisArg,
args, newHolders, argPos, ary, arity - length
);
}
var thisBinding = isBind ? thisArg : this,
fn = isBindKey ? thisBinding[func] : func;
length = args.length;
if (argPos) {
args = reorder(args, argPos);
} else if (isFlip && length > 1) {
args.reverse();
}
if (isAry && ary < length) {
args.length = ary;
}
if (this && this !== root && this instanceof wrapper) {
fn = Ctor || createCtor(fn);
}
return fn.apply(thisBinding, args);
}
return wrapper;
}
module.exports = createHybrid;
/***/ }),
/* 196 */
/***/ (function(module, exports) {
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
/**
* Creates an array that is the composition of partially applied arguments,
* placeholders, and provided arguments into a single array of arguments.
*
* @private
* @param {Array} args The provided arguments.
* @param {Array} partials The arguments to prepend to those provided.
* @param {Array} holders The `partials` placeholder indexes.
* @params {boolean} [isCurried] Specify composing for a curried function.
* @returns {Array} Returns the new array of composed arguments.
*/
function composeArgs(args, partials, holders, isCurried) {
var argsIndex = -1,
argsLength = args.length,
holdersLength = holders.length,
leftIndex = -1,
leftLength = partials.length,
rangeLength = nativeMax(argsLength - holdersLength, 0),
result = Array(leftLength + rangeLength),
isUncurried = !isCurried;
while (++leftIndex < leftLength) {
result[leftIndex] = partials[leftIndex];
}
while (++argsIndex < holdersLength) {
if (isUncurried || argsIndex < argsLength) {
result[holders[argsIndex]] = args[argsIndex];
}
}
while (rangeLength--) {
result[leftIndex++] = args[argsIndex++];
}
return result;
}
module.exports = composeArgs;
/***/ }),
/* 197 */
/***/ (function(module, exports) {
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
/**
* This function is like `composeArgs` except that the arguments composition
* is tailored for `_.partialRight`.
*
* @private
* @param {Array} args The provided arguments.
* @param {Array} partials The arguments to append to those provided.
* @param {Array} holders The `partials` placeholder indexes.
* @params {boolean} [isCurried] Specify composing for a curried function.
* @returns {Array} Returns the new array of composed arguments.
*/
function composeArgsRight(args, partials, holders, isCurried) {
var argsIndex = -1,
argsLength = args.length,
holdersIndex = -1,
holdersLength = holders.length,
rightIndex = -1,
rightLength = partials.length,
rangeLength = nativeMax(argsLength - holdersLength, 0),
result = Array(rangeLength + rightLength),
isUncurried = !isCurried;
while (++argsIndex < rangeLength) {
result[argsIndex] = args[argsIndex];
}
var offset = argsIndex;
while (++rightIndex < rightLength) {
result[offset + rightIndex] = partials[rightIndex];
}
while (++holdersIndex < holdersLength) {
if (isUncurried || argsIndex < argsLength) {
result[offset + holders[holdersIndex]] = args[argsIndex++];
}
}
return result;
}
module.exports = composeArgsRight;
/***/ }),
/* 198 */
/***/ (function(module, exports, __webpack_require__) {
var isLaziable = __webpack_require__(402),
setData = __webpack_require__(202),
setWrapToString = __webpack_require__(204);
/** Used to compose bitmasks for function metadata. */
var WRAP_BIND_FLAG = 1,
WRAP_BIND_KEY_FLAG = 2,
WRAP_CURRY_BOUND_FLAG = 4,
WRAP_CURRY_FLAG = 8,
WRAP_PARTIAL_FLAG = 32,
WRAP_PARTIAL_RIGHT_FLAG = 64;
/**
* Creates a function that wraps `func` to continue currying.
*
* @private
* @param {Function} func The function to wrap.
* @param {number} bitmask The bitmask flags. See `createWrap` for more details.
* @param {Function} wrapFunc The function to create the `func` wrapper.
* @param {*} placeholder The placeholder value.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {Array} [partials] The arguments to prepend to those provided to
* the new function.
* @param {Array} [holders] The `partials` placeholder indexes.
* @param {Array} [argPos] The argument positions of the new function.
* @param {number} [ary] The arity cap of `func`.
* @param {number} [arity] The arity of `func`.
* @returns {Function} Returns the new wrapped function.
*/
function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
var isCurry = bitmask & WRAP_CURRY_FLAG,
newHolders = isCurry ? holders : undefined,
newHoldersRight = isCurry ? undefined : holders,
newPartials = isCurry ? partials : undefined,
newPartialsRight = isCurry ? undefined : partials;
bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);
bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);
if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
}
var newData = [
func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
newHoldersRight, argPos, ary, arity
];
var result = wrapFunc.apply(undefined, newData);
if (isLaziable(func)) {
setData(result, newData);
}
result.placeholder = placeholder;
return setWrapToString(result, func, bitmask);
}
module.exports = createRecurry;
/***/ }),
/* 199 */
/***/ (function(module, exports, __webpack_require__) {
var metaMap = __webpack_require__(194),
noop = __webpack_require__(200);
/**
* Gets metadata for `func`.
*
* @private
* @param {Function} func The function to query.
* @returns {*} Returns the metadata for `func`.
*/
var getData = !metaMap ? noop : function(func) {
return metaMap.get(func);
};
module.exports = getData;
/***/ }),
/* 200 */
/***/ (function(module, exports) {
/**
* This method returns `undefined`.
*
* @static
* @memberOf _
* @since 2.3.0
* @category Util
* @example
*
* _.times(2, _.noop);
* // => [undefined, undefined]
*/
function noop() {
// No operation performed.
}
module.exports = noop;
/***/ }),
/* 201 */
/***/ (function(module, exports, __webpack_require__) {
var baseCreate = __webpack_require__(83),
baseLodash = __webpack_require__(121);
/**
* The base constructor for creating `lodash` wrapper objects.
*
* @private
* @param {*} value The value to wrap.
* @param {boolean} [chainAll] Enable explicit method chain sequences.
*/
function LodashWrapper(value, chainAll) {
this.__wrapped__ = value;
this.__actions__ = [];
this.__chain__ = !!chainAll;
this.__index__ = 0;
this.__values__ = undefined;
}
LodashWrapper.prototype = baseCreate(baseLodash.prototype);
LodashWrapper.prototype.constructor = LodashWrapper;
module.exports = LodashWrapper;
/***/ }),
/* 202 */
/***/ (function(module, exports, __webpack_require__) {
var baseSetData = __webpack_require__(193),
shortOut = __webpack_require__(203);
/**
* Sets metadata for `func`.
*
* **Note:** If this function becomes hot, i.e. is invoked a lot in a short
* period of time, it will trip its breaker and transition to an identity
* function to avoid garbage collection pauses in V8. See
* [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
* for more details.
*
* @private
* @param {Function} func The function to associate metadata with.
* @param {*} data The metadata.
* @returns {Function} Returns `func`.
*/
var setData = shortOut(baseSetData);
module.exports = setData;
/***/ }),
/* 203 */
/***/ (function(module, exports) {
/** Used to detect hot functions by number of calls within a span of milliseconds. */
var HOT_COUNT = 800,
HOT_SPAN = 16;
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeNow = Date.now;
/**
* Creates a function that'll short out and invoke `identity` instead
* of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
* milliseconds.
*
* @private
* @param {Function} func The function to restrict.
* @returns {Function} Returns the new shortable function.
*/
function shortOut(func) {
var count = 0,
lastCalled = 0;
return function() {
var stamp = nativeNow(),
remaining = HOT_SPAN - (stamp - lastCalled);
lastCalled = stamp;
if (remaining > 0) {
if (++count >= HOT_COUNT) {
return arguments[0];
}
} else {
count = 0;
}
return func.apply(undefined, arguments);
};
}
module.exports = shortOut;
/***/ }),
/* 204 */
/***/ (function(module, exports, __webpack_require__) {
var getWrapDetails = __webpack_require__(407),
insertWrapDetails = __webpack_require__(408),
setToString = __webpack_require__(122),
updateWrapDetails = __webpack_require__(411);
/**
* Sets the `toString` method of `wrapper` to mimic the source of `reference`
* with wrapper details in a comment at the top of the source body.
*
* @private
* @param {Function} wrapper The function to modify.
* @param {Function} reference The reference function.
* @param {number} bitmask The bitmask flags. See `createWrap` for more details.
* @returns {Function} Returns `wrapper`.
*/
function setWrapToString(wrapper, reference, bitmask) {
var source = (reference + '');
return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
}
module.exports = setWrapToString;
/***/ }),
/* 205 */
/***/ (function(module, exports) {
/**
* The base implementation of `_.findIndex` and `_.findLastIndex` without
* support for iteratee shorthands.
*
* @private
* @param {Array} array The array to inspect.
* @param {Function} predicate The function invoked per iteration.
* @param {number} fromIndex The index to search from.
* @param {boolean} [fromRight] Specify iterating from right to left.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseFindIndex(array, predicate, fromIndex, fromRight) {
var length = array.length,
index = fromIndex + (fromRight ? 1 : -1);
while ((fromRight ? index-- : ++index < length)) {
if (predicate(array[index], index, array)) {
return index;
}
}
return -1;
}
module.exports = baseFindIndex;
/***/ }),
/* 206 */
/***/ (function(module, exports) {
/**
* Gets the argument placeholder value for `func`.
*
* @private
* @param {Function} func The function to inspect.
* @returns {*} Returns the placeholder value.
*/
function getHolder(func) {
var object = func;
return object.placeholder;
}
module.exports = getHolder;
/***/ }),
/* 207 */
/***/ (function(module, exports, __webpack_require__) {
var isObject = __webpack_require__(7),
isSymbol = __webpack_require__(68);
/** Used as references for various `Number` constants. */
var NAN = 0 / 0;
/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;
/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;
/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;
/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;
/**
* Converts `value` to a number.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to process.
* @returns {number} Returns the number.
* @example
*
* _.toNumber(3.2);
* // => 3.2
*
* _.toNumber(Number.MIN_VALUE);
* // => 5e-324
*
* _.toNumber(Infinity);
* // => Infinity
*
* _.toNumber('3.2');
* // => 3.2
*/
function toNumber(value) {
if (typeof value == 'number') {
return value;
}
if (isSymbol(value)) {
return NAN;
}
if (isObject(value)) {
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
value = isObject(other) ? (other + '') : other;
}
if (typeof value != 'string') {
return value === 0 ? value : +value;
}
value = value.replace(reTrim, '');
var isBinary = reIsBinary.test(value);
return (isBinary || reIsOctal.test(value))
? freeParseInt(value.slice(2), isBinary ? 2 : 8)
: (reIsBadHex.test(value) ? NAN : +value);
}
module.exports = toNumber;
/***/ }),
/* 208 */
/***/ (function(module, exports, __webpack_require__) {
var baseIsEqualDeep = __webpack_require__(425),
isObjectLike = __webpack_require__(14);
/**
* The base implementation of `_.isEqual` which supports partial comparisons
* and tracks traversed objects.
*
* @private
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
* @param {boolean} bitmask The bitmask flags.
* 1 - Unordered comparison
* 2 - Partial comparison
* @param {Function} [customizer] The function to customize comparisons.
* @param {Object} [stack] Tracks traversed `value` and `other` objects.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
*/
function baseIsEqual(value, other, bitmask, customizer, stack) {
if (value === other) {
return true;
}
if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
return value !== value && other !== other;
}
return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
}
module.exports = baseIsEqual;
/***/ }),
/* 209 */
/***/ (function(module, exports, __webpack_require__) {
var SetCache = __webpack_require__(88),
arraySome = __webpack_require__(210),
cacheHas = __webpack_require__(89);
/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
COMPARE_UNORDERED_FLAG = 2;
/**
* A specialized version of `baseIsEqualDeep` for arrays with support for
* partial deep comparisons.
*
* @private
* @param {Array} array The array to compare.
* @param {Array} other The other array to compare.
* @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
* @param {Function} customizer The function to customize comparisons.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Object} stack Tracks traversed `array` and `other` objects.
* @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
*/
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
arrLength = array.length,
othLength = other.length;
if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
return false;
}
// Assume cyclic values are equal.
var stacked = stack.get(array);
if (stacked && stack.get(other)) {
return stacked == other;
}
var index = -1,
result = true,
seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;
stack.set(array, other);
stack.set(other, array);
// Ignore non-index properties.
while (++index < arrLength) {
var arrValue = array[index],
othValue = other[index];
if (customizer) {
var compared = isPartial
? customizer(othValue, arrValue, index, other, array, stack)
: customizer(arrValue, othValue, index, array, other, stack);
}
if (compared !== undefined) {
if (compared) {
continue;
}
result = false;
break;
}
// Recursively compare arrays (susceptible to call stack limits).
if (seen) {
if (!arraySome(other, function(othValue, othIndex) {
if (!cacheHas(seen, othIndex) &&
(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
return seen.push(othIndex);
}
})) {
result = false;
break;
}
} else if (!(
arrValue === othValue ||
equalFunc(arrValue, othValue, bitmask, customizer, stack)
)) {
result = false;
break;
}
}
stack['delete'](array);
stack['delete'](other);
return result;
}
module.exports = equalArrays;
/***/ }),
/* 210 */
/***/ (function(module, exports) {
/**
* A specialized version of `_.some` for arrays without support for iteratee
* shorthands.
*
* @private
* @param {Array} [array] The array to iterate over.
* @param {Function} predicate The function invoked per iteration.
* @returns {boolean} Returns `true` if any element passes the predicate check,
* else `false`.
*/
function arraySome(array, predicate) {
var index = -1,
length = array == null ? 0 : array.length;
while (++index < length) {
if (predicate(array[index], index, array)) {
return true;
}
}
return false;
}
module.exports = arraySome;
/***/ }),
/* 211 */
/***/ (function(module, exports) {
/**
* Converts `map` to its key-value pairs.
*
* @private
* @param {Object} map The map to convert.
* @returns {Array} Returns the key-value pairs.
*/
function mapToArray(map) {
var index = -1,
result = Array(map.size);
map.forEach(function(value, key) {
result[++index] = [key, value];
});
return result;
}
module.exports = mapToArray;
/***/ }),
/* 212 */
/***/ (function(module, exports, __webpack_require__) {
var isObject = __webpack_require__(7);
/**
* Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` if suitable for strict
* equality comparisons, else `false`.
*/
function isStrictComparable(value) {
return value === value && !isObject(value);
}
module.exports = isStrictComparable;
/***/ }),
/* 213 */
/***/ (function(module, exports) {
/**
* A specialized version of `matchesProperty` for source values suitable
* for strict equality comparisons, i.e. `===`.
*
* @private
* @param {string} key The key of the property to get.
* @param {*} srcValue The value to match.
* @returns {Function} Returns the new spec function.
*/
function matchesStrictComparable(key, srcValue) {
return function(object) {
if (object == null) {
return false;
}
return object[key] === srcValue &&
(srcValue !== undefined || (key in Object(object)));
};
}
module.exports = matchesStrictComparable;
/***/ }),
/* 214 */
/***/ (function(module, exports, __webpack_require__) {
var memoizeCapped = __webpack_require__(432);
/** Used to match property names within property paths. */
var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
/** Used to match backslashes in property paths. */
var reEscapeChar = /\\(\\)?/g;
/**
* Converts `string` to a property path array.
*
* @private
* @param {string} string The string to convert.
* @returns {Array} Returns the property path array.
*/
var stringToPath = memoizeCapped(function(string) {
var result = [];
if (string.charCodeAt(0) === 46 /* . */) {
result.push('');
}
string.replace(rePropName, function(match, number, quote, subString) {
result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
});
return result;
});
module.exports = stringToPath;
/***/ }),
/* 215 */
/***/ (function(module, exports, __webpack_require__) {
var MapCache = __webpack_require__(107);
/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';
/**
* Creates a function that memoizes the result of `func`. If `resolver` is
* provided, it determines the cache key for storing the result based on the
* arguments provided to the memoized function. By default, the first argument
* provided to the memoized function is used as the map cache key. The `func`
* is invoked with the `this` binding of the memoized function.
*
* **Note:** The cache is exposed as the `cache` property on the memoized
* function. Its creation may be customized by replacing the `_.memoize.Cache`
* constructor with one whose instances implement the
* [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
* method interface of `clear`, `delete`, `get`, `has`, and `set`.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Function
* @param {Function} func The function to have its output memoized.
* @param {Function} [resolver] The function to resolve the cache key.
* @returns {Function} Returns the new memoized function.
* @example
*
* var object = { 'a': 1, 'b': 2 };
* var other = { 'c': 3, 'd': 4 };
*
* var values = _.memoize(_.values);
* values(object);
* // => [1, 2]
*
* values(other);
* // => [3, 4]
*
* object.a = 2;
* values(object);
* // => [1, 2]
*
* // Modify the result cache.
* values.cache.set(object, ['a', 'b']);
* values(object);
* // => ['a', 'b']
*
* // Replace `_.memoize.Cache`.
* _.memoize.Cache = WeakMap;
*/
function memoize(func, resolver) {
if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
throw new TypeError(FUNC_ERROR_TEXT);
}
var memoized = function() {
var args = arguments,
key = resolver ? resolver.apply(this, args) : args[0],
cache = memoized.cache;
if (cache.has(key)) {
return cache.get(key);
}
var result = func.apply(this, args);
memoized.cache = cache.set(key, result) || cache;
return result;
};
memoized.cache = new (memoize.Cache || MapCache);
return memoized;
}
// Expose `MapCache`.
memoize.Cache = MapCache;
module.exports = memoize;
/***/ }),
/* 216 */
/***/ (function(module, exports, __webpack_require__) {
var baseHasIn = __webpack_require__(434),
hasPath = __webpack_require__(435);
/**
* Checks if `path` is a direct or inherited property of `object`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Object
* @param {Object} object The object to query.
* @param {Array|string} path The path to check.
* @returns {boolean} Returns `true` if `path` exists, else `false`.
* @example
*
* var object = _.create({ 'a': _.create({ 'b': 2 }) });
*
* _.hasIn(object, 'a');
* // => true
*
* _.hasIn(object, 'a.b');
* // => true
*
* _.hasIn(object, ['a', 'b']);
* // => true
*
* _.hasIn(object, 'b');
* // => false
*/
function hasIn(object, path) {
return object != null && hasPath(object, path, baseHasIn);
}
module.exports = hasIn;
/***/ }),
/* 217 */
/***/ (function(module, exports, __webpack_require__) {
var arrayPush = __webpack_require__(113),
isFlattenable = __webpack_require__(441);
/**
* The base implementation of `_.flatten` with support for restricting flattening.
*
* @private
* @param {Array} array The array to flatten.
* @param {number} depth The maximum recursion depth.
* @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
* @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
* @param {Array} [result=[]] The initial result value.
* @returns {Array} Returns the new flattened array.
*/
function baseFlatten(array, depth, predicate, isStrict, result) {
var index = -1,
length = array.length;
predicate || (predicate = isFlattenable);
result || (result = []);
while (++index < length) {
var value = array[index];
if (depth > 0 && predicate(value)) {
if (depth > 1) {
// Recursively flatten arrays (susceptible to call stack limits).
baseFlatten(value, depth - 1, predicate, isStrict, result);
} else {
arrayPush(result, value);
}
} else if (!isStrict) {
result[result.length] = value;
}
}
return result;
}
module.exports = baseFlatten;
/***/ }),
/* 218 */
/***/ (function(module, exports, __webpack_require__) {
var apply = __webpack_require__(119);
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
/**
* A specialized version of `baseRest` which transforms the rest array.
*
* @private
* @param {Function} func The function to apply a rest parameter to.
* @param {number} [start=func.length-1] The start position of the rest parameter.
* @param {Function} transform The rest array transform.
* @returns {Function} Returns the new function.
*/
function overRest(func, start, transform) {
start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
return function() {
var args = arguments,
index = -1,
length = nativeMax(args.length - start, 0),
array = Array(length);
while (++index < length) {
array[index] = args[start + index];
}
index = -1;
var otherArgs = Array(start + 1);
while (++index < start) {
otherArgs[index] = args[index];
}
otherArgs[start] = transform(array);
return apply(func, this, otherArgs);
};
}
module.exports = overRest;
/***/ }),
/* 219 */
/***/ (function(module, exports, __webpack_require__) {
var createBaseFor = __webpack_require__(448);
/**
* The base implementation of `baseForOwn` which iterates over `object`
* properties returned by `keysFunc` and invokes `iteratee` for each property.
* Iteratee functions may exit iteration early by explicitly returning `false`.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
var baseFor = createBaseFor();
module.exports = baseFor;
/***/ }),
/* 220 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// Copyright Joyent, Inc. and other Node contributors.
//
// 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:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// 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 punycode = __webpack_require__(452);
var util = __webpack_require__(453);
exports.parse = urlParse;
exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;
exports.Url = Url;
function Url() {
this.protocol = null;
this.slashes = null;
this.auth = null;
this.host = null;
this.port = null;
this.hostname = null;
this.hash = null;
this.search = null;
this.query = null;
this.pathname = null;
this.path = null;
this.href = null;
}
// Reference: RFC 3986, RFC 1808, RFC 2396
// define these here so at least they only have to be
// compiled once on the first module load.
var protocolPattern = /^([a-z0-9.+-]+:)/i,
portPattern = /:[0-9]*$/,
// Special case for a simple path URL
simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
// RFC 2396: characters reserved for delimiting URLs.
// We actually just auto-escape these.
delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
// RFC 2396: characters not allowed for various reasons.
unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
// Allowed by RFCs, but cause of XSS attacks. Always escape these.
autoEscape = ['\''].concat(unwise),
// Characters that are never ever allowed in a hostname.
// Note that any invalid chars are also handled, but these
// are the ones that are *expected* to be seen, so we fast-path
// them.
nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
hostEndingChars = ['/', '?', '#'],
hostnameMaxLen = 255,
hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
// protocols that can allow "unsafe" and "unwise" chars.
unsafeProtocol = {
'javascript': true,
'javascript:': true
},
// protocols that never have a hostname.
hostlessProtocol = {
'javascript': true,
'javascript:': true
},
// protocols that always contain a // bit.
slashedProtocol = {
'http': true,
'https': true,
'ftp': true,
'gopher': true,
'file': true,
'http:': true,
'https:': true,
'ftp:': true,
'gopher:': true,
'file:': true
},
querystring = __webpack_require__(454);
function urlParse(url, parseQueryString, slashesDenoteHost) {
if (url && util.isObject(url) && url instanceof Url) return url;
var u = new Url;
u.parse(url, parseQueryString, slashesDenoteHost);
return u;
}
Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
if (!util.isString(url)) {
throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
}
// Copy chrome, IE, opera backslash-handling behavior.
// Back slashes before the query string get converted to forward slashes
// See: https://code.google.com/p/chromium/issues/detail?id=25916
var queryIndex = url.indexOf('?'),
splitter =
(queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
uSplit = url.split(splitter),
slashRegex = /\\/g;
uSplit[0] = uSplit[0].replace(slashRegex, '/');
url = uSplit.join(splitter);
var rest = url;
// trim before proceeding.
// This is to support parse stuff like " http://foo.com \n"
rest = rest.trim();
if (!slashesDenoteHost && url.split('#').length === 1) {
// Try fast path regexp
var simplePath = simplePathPattern.exec(rest);
if (simplePath) {
this.path = rest;
this.href = rest;
this.pathname = simplePath[1];
if (simplePath[2]) {
this.search = simplePath[2];
if (parseQueryString) {
this.query = querystring.parse(this.search.substr(1));
} else {
this.query = this.search.substr(1);
}
} else if (parseQueryString) {
this.search = '';
this.query = {};
}
return this;
}
}
var proto = protocolPattern.exec(rest);
if (proto) {
proto = proto[0];
var lowerProto = proto.toLowerCase();
this.protocol = lowerProto;
rest = rest.substr(proto.length);
}
// figure out if it's got a host
// user@server is *always* interpreted as a hostname, and url
// resolution will treat //foo/bar as host=foo,path=bar because that's
// how the browser resolves relative URLs.
if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
var slashes = rest.substr(0, 2) === '//';
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.substr(2);
this.slashes = true;
}
}
if (!hostlessProtocol[proto] &&
(slashes || (proto && !slashedProtocol[proto]))) {
// there's a hostname.
// the first instance of /, ?, ;, or # ends the host.
//
// If there is an @ in the hostname, then non-host chars *are* allowed
// to the left of the last @ sign, unless some host-ending character
// comes *before* the @-sign.
// URLs are obnoxious.
//
// ex:
// http://a@b@c/ => user:a@b host:c
// http://a@b?@c => user:a host:c path:/?@c
// v0.12 TODO(isaacs): This is not quite how Chrome does things.
// Review our test case against browsers more comprehensively.
// find the first instance of any hostEndingChars
var hostEnd = -1;
for (var i = 0; i < hostEndingChars.length; i++) {
var hec = rest.indexOf(hostEndingChars[i]);
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
hostEnd = hec;
}
// at this point, either we have an explicit point where the
// auth portion cannot go past, or the last @ char is the decider.
var auth, atSign;
if (hostEnd === -1) {
// atSign can be anywhere.
atSign = rest.lastIndexOf('@');
} else {
// atSign must be in auth portion.
// http://a@b/c@d => host:b auth:a path:/c@d
atSign = rest.lastIndexOf('@', hostEnd);
}
// Now we have a portion which is definitely the auth.
// Pull that off.
if (atSign !== -1) {
auth = rest.slice(0, atSign);
rest = rest.slice(atSign + 1);
this.auth = decodeURIComponent(auth);
}
// the host is the remaining to the left of the first non-host char
hostEnd = -1;
for (var i = 0; i < nonHostChars.length; i++) {
var hec = rest.indexOf(nonHostChars[i]);
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
hostEnd = hec;
}
// if we still have not hit it, then the entire thing is a host.
if (hostEnd === -1)
hostEnd = rest.length;
this.host = rest.slice(0, hostEnd);
rest = rest.slice(hostEnd);
// pull out port.
this.parseHost();
// we've indicated that there is a hostname,
// so even if it's empty, it has to be present.
this.hostname = this.hostname || '';
// if hostname begins with [ and ends with ]
// assume that it's an IPv6 address.
var ipv6Hostname = this.hostname[0] === '[' &&
this.hostname[this.hostname.length - 1] === ']';
// validate a little.
if (!ipv6Hostname) {
var hostparts = this.hostname.split(/\./);
for (var i = 0, l = hostparts.length; i < l; i++) {
var part = hostparts[i];
if (!part) continue;
if (!part.match(hostnamePartPattern)) {
var newpart = '';
for (var j = 0, k = part.length; j < k; j++) {
if (part.charCodeAt(j) > 127) {
// we replace non-ASCII char with a temporary placeholder
// we need this to make sure size of hostname is not
// broken by replacing non-ASCII by nothing
newpart += 'x';
} else {
newpart += part[j];
}
}
// we test again with ASCII char only
if (!newpart.match(hostnamePartPattern)) {
var validParts = hostparts.slice(0, i);
var notHost = hostparts.slice(i + 1);
var bit = part.match(hostnamePartStart);
if (bit) {
validParts.push(bit[1]);
notHost.unshift(bit[2]);
}
if (notHost.length) {
rest = '/' + notHost.join('.') + rest;
}
this.hostname = validParts.join('.');
break;
}
}
}
}
if (this.hostname.length > hostnameMaxLen) {
this.hostname = '';
} else {
// hostnames are always lower case.
this.hostname = this.hostname.toLowerCase();
}
if (!ipv6Hostname) {
// IDNA Support: Returns a punycoded representation of "domain".
// It only converts parts of the domain name that
// have non-ASCII characters, i.e. it doesn't matter if
// you call it with a domain that already is ASCII-only.
this.hostname = punycode.toASCII(this.hostname);
}
var p = this.port ? ':' + this.port : '';
var h = this.hostname || '';
this.host = h + p;
this.href += this.host;
// strip [ and ] from the hostname
// the host field still retains them, though
if (ipv6Hostname) {
this.hostname = this.hostname.substr(1, this.hostname.length - 2);
if (rest[0] !== '/') {
rest = '/' + rest;
}
}
}
// now rest is set to the post-host stuff.
// chop off any delim chars.
if (!unsafeProtocol[lowerProto]) {
// First, make 100% sure that any "autoEscape" chars get
// escaped, even if encodeURIComponent doesn't think they
// need to be.
for (var i = 0, l = autoEscape.length; i < l; i++) {
var ae = autoEscape[i];
if (rest.indexOf(ae) === -1)
continue;
var esc = encodeURIComponent(ae);
if (esc === ae) {
esc = escape(ae);
}
rest = rest.split(ae).join(esc);
}
}
// chop off from the tail first.
var hash = rest.indexOf('#');
if (hash !== -1) {
// got a fragment string.
this.hash = rest.substr(hash);
rest = rest.slice(0, hash);
}
var qm = rest.indexOf('?');
if (qm !== -1) {
this.search = rest.substr(qm);
this.query = rest.substr(qm + 1);
if (parseQueryString) {
this.query = querystring.parse(this.query);
}
rest = rest.slice(0, qm);
} else if (parseQueryString) {
// no query string, but parseQueryString still requested
this.search = '';
this.query = {};
}
if (rest) this.pathname = rest;
if (slashedProtocol[lowerProto] &&
this.hostname && !this.pathname) {
this.pathname = '/';
}
//to support http.request
if (this.pathname || this.search) {
var p = this.pathname || '';
var s = this.search || '';
this.path = p + s;
}
// finally, reconstruct the href based on what has been validated.
this.href = this.format();
return this;
};
// format a parsed object into a url string
function urlFormat(obj) {
// ensure it's an object, and not a string url.
// If it's an obj, this is a no-op.
// this way, you can call url_format() on strings
// to clean up potentially wonky urls.
if (util.isString(obj)) obj = urlParse(obj);
if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
return obj.format();
}
Url.prototype.format = function() {
var auth = this.auth || '';
if (auth) {
auth = encodeURIComponent(auth);
auth = auth.replace(/%3A/i, ':');
auth += '@';
}
var protocol = this.protocol || '',
pathname = this.pathname || '',
hash = this.hash || '',
host = false,
query = '';
if (this.host) {
host = auth + this.host;
} else if (this.hostname) {
host = auth + (this.hostname.indexOf(':') === -1 ?
this.hostname :
'[' + this.hostname + ']');
if (this.port) {
host += ':' + this.port;
}
}
if (this.query &&
util.isObject(this.query) &&
Object.keys(this.query).length) {
query = querystring.stringify(this.query);
}
var search = this.search || (query && ('?' + query)) || '';
if (protocol && protocol.substr(-1) !== ':') protocol += ':';
// only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
// unless they had them to begin with.
if (this.slashes ||
(!protocol || slashedProtocol[protocol]) && host !== false) {
host = '//' + (host || '');
if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
} else if (!host) {
host = '';
}
if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
if (search && search.charAt(0) !== '?') search = '?' + search;
pathname = pathname.replace(/[?#]/g, function(match) {
return encodeURIComponent(match);
});
search = search.replace('#', '%23');
return protocol + host + pathname + search + hash;
};
function urlResolve(source, relative) {
return urlParse(source, false, true).resolve(relative);
}
Url.prototype.resolve = function(relative) {
return this.resolveObject(urlParse(relative, false, true)).format();
};
function urlResolveObject(source, relative) {
if (!source) return relative;
return urlParse(source, false, true).resolveObject(relative);
}
Url.prototype.resolveObject = function(relative) {
if (util.isString(relative)) {
var rel = new Url();
rel.parse(relative, false, true);
relative = rel;
}
var result = new Url();
var tkeys = Object.keys(this);
for (var tk = 0; tk < tkeys.length; tk++) {
var tkey = tkeys[tk];
result[tkey] = this[tkey];
}
// hash is always overridden, no matter what.
// even href="" will remove it.
result.hash = relative.hash;
// if the relative url is empty, then there's nothing left to do here.
if (relative.href === '') {
result.href = result.format();
return result;
}
// hrefs like //foo/bar always cut to the protocol.
if (relative.slashes && !relative.protocol) {
// take everything except the protocol from relative
var rkeys = Object.keys(relative);
for (var rk = 0; rk < rkeys.length; rk++) {
var rkey = rkeys[rk];
if (rkey !== 'protocol')
result[rkey] = relative[rkey];
}
//urlParse appends trailing / to urls like http://www.example.com
if (slashedProtocol[result.protocol] &&
result.hostname && !result.pathname) {
result.path = result.pathname = '/';
}
result.href = result.format();
return result;
}
if (relative.protocol && relative.protocol !== result.protocol) {
// if it's a known url protocol, then changing
// the protocol does weird things
// first, if it's not file:, then we MUST have a host,
// and if there was a path
// to begin with, then we MUST have a path.
// if it is file:, then the host is dropped,
// because that's known to be hostless.
// anything else is assumed to be absolute.
if (!slashedProtocol[relative.protocol]) {
var keys = Object.keys(relative);
for (var v = 0; v < keys.length; v++) {
var k = keys[v];
result[k] = relative[k];
}
result.href = result.format();
return result;
}
result.protocol = relative.protocol;
if (!relative.host && !hostlessProtocol[relative.protocol]) {
var relPath = (relative.pathname || '').split('/');
while (relPath.length && !(relative.host = relPath.shift()));
if (!relative.host) relative.host = '';
if (!relative.hostname) relative.hostname = '';
if (relPath[0] !== '') relPath.unshift('');
if (relPath.length < 2) relPath.unshift('');
result.pathname = relPath.join('/');
} else {
result.pathname = relative.pathname;
}
result.search = relative.search;
result.query = relative.query;
result.host = relative.host || '';
result.auth = relative.auth;
result.hostname = relative.hostname || relative.host;
result.port = relative.port;
// to support http.request
if (result.pathname || result.search) {
var p = result.pathname || '';
var s = result.search || '';
result.path = p + s;
}
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
}
var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
isRelAbs = (
relative.host ||
relative.pathname && relative.pathname.charAt(0) === '/'
),
mustEndAbs = (isRelAbs || isSourceAbs ||
(result.host && relative.pathname)),
removeAllDots = mustEndAbs,
srcPath = result.pathname && result.pathname.split('/') || [],
relPath = relative.pathname && relative.pathname.split('/') || [],
psychotic = result.protocol && !slashedProtocol[result.protocol];
// if the url is a non-slashed url, then relative
// links like ../.. should be able
// to crawl up to the hostname, as well. This is strange.
// result.protocol has already been set by now.
// Later on, put the first path part into the host field.
if (psychotic) {
result.hostname = '';
result.port = null;
if (result.host) {
if (srcPath[0] === '') srcPath[0] = result.host;
else srcPath.unshift(result.host);
}
result.host = '';
if (relative.protocol) {
relative.hostname = null;
relative.port = null;
if (relative.host) {
if (relPath[0] === '') relPath[0] = relative.host;
else relPath.unshift(relative.host);
}
relative.host = null;
}
mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
}
if (isRelAbs) {
// it's absolute.
result.host = (relative.host || relative.host === '') ?
relative.host : result.host;
result.hostname = (relative.hostname || relative.hostname === '') ?
relative.hostname : result.hostname;
result.search = relative.search;
result.query = relative.query;
srcPath = relPath;
// fall through to the dot-handling below.
} else if (relPath.length) {
// it's relative
// throw away the existing file, and take the new path instead.
if (!srcPath) srcPath = [];
srcPath.pop();
srcPath = srcPath.concat(relPath);
result.search = relative.search;
result.query = relative.query;
} else if (!util.isNullOrUndefined(relative.search)) {
// just pull out the search.
// like href='?foo'.
// Put this after the other two cases because it simplifies the booleans
if (psychotic) {
result.hostname = result.host = srcPath.shift();
//occationaly the auth can get stuck only in host
//this especially happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
var authInHost = result.host && result.host.indexOf('@') > 0 ?
result.host.split('@') : false;
if (authInHost) {
result.auth = authInHost.shift();
result.host = result.hostname = authInHost.shift();
}
}
result.search = relative.search;
result.query = relative.query;
//to support http.request
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
}
result.href = result.format();
return result;
}
if (!srcPath.length) {
// no path at all. easy.
// we've already handled the other stuff above.
result.pathname = null;
//to support http.request
if (result.search) {
result.path = '/' + result.search;
} else {
result.path = null;
}
result.href = result.format();
return result;
}
// if a url ENDs in . or .., then it must get a trailing slash.
// however, if it ends in anything else non-slashy,
// then it must NOT get a trailing slash.
var last = srcPath.slice(-1)[0];
var hasTrailingSlash = (
(result.host || relative.host || srcPath.length > 1) &&
(last === '.' || last === '..') || last === '');
// strip single dots, resolve double dots to parent dir
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = srcPath.length; i >= 0; i--) {
last = srcPath[i];
if (last === '.') {
srcPath.splice(i, 1);
} else if (last === '..') {
srcPath.splice(i, 1);
up++;
} else if (up) {
srcPath.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (!mustEndAbs && !removeAllDots) {
for (; up--; up) {
srcPath.unshift('..');
}
}
if (mustEndAbs && srcPath[0] !== '' &&
(!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
srcPath.unshift('');
}
if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
srcPath.push('');
}
var isAbsolute = srcPath[0] === '' ||
(srcPath[0] && srcPath[0].charAt(0) === '/');
// put the host back
if (psychotic) {
result.hostname = result.host = isAbsolute ? '' :
srcPath.length ? srcPath.shift() : '';
//occationaly the auth can get stuck only in host
//this especially happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
var authInHost = result.host && result.host.indexOf('@') > 0 ?
result.host.split('@') : false;
if (authInHost) {
result.auth = authInHost.shift();
result.host = result.hostname = authInHost.shift();
}
}
mustEndAbs = mustEndAbs || (result.host && srcPath.length);
if (mustEndAbs && !isAbsolute) {
srcPath.unshift('');
}
if (!srcPath.length) {
result.pathname = null;
result.path = null;
} else {
result.pathname = srcPath.join('/');
}
//to support request.http
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
}
result.auth = relative.auth || result.auth;
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
};
Url.prototype.parseHost = function() {
var host = this.host;
var port = portPattern.exec(host);
if (port) {
port = port[0];
if (port !== ':') {
this.port = port.substr(1);
}
host = host.substr(0, host.length - port.length);
}
if (host) this.hostname = host;
};
/***/ }),
/* 221 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const ElementCollection = __webpack_require__(222);
module.exports = function (selector, context) {
return new ElementCollection(selector, context);
};
/***/ }),
/* 222 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable global-require, no-param-reassign, no-void */
/* eslint-disable no-underscore-dangle, prefer-rest-params, prefer-const */
const isFunction = __webpack_require__(13);
const extensions = {
attributes: __webpack_require__(457),
css: __webpack_require__(458),
classNames: __webpack_require__(459),
observers: __webpack_require__(461)
};
const idRegex = /^#([\w-]*)$/; // A helper for converting a NodeList to a JS Array
const nodeListToArray = function nodeListToArray(nodes) {
return Array.prototype.slice.call(nodes);
};
const selectorToElementArray = function selectorToElementArray(selector, context) {
let elements;
if (typeof selector === 'undefined') {
return [];
}
if (typeof selector === 'string') {
elements = [];
const idSelector = idRegex.exec(selector);
context = context || document;
if (idSelector && idSelector[1]) {
const element = context.getElementById(idSelector[1]);
if (element) {
elements.push(element);
}
} else {
elements = context.querySelectorAll(selector);
}
} else if (selector && (selector.nodeType || (typeof window !== undefined ? window : global).XMLHttpRequest && selector instanceof (typeof window !== undefined ? window : global).XMLHttpRequest || selector === (typeof window !== undefined ? window : global))) {
// allow OTHelpers(DOMNode) and OTHelpers(xmlHttpRequest)
elements = [selector];
context = selector;
} else if (Array.isArray(selector)) {
elements = selector.slice();
context = null;
} else {
elements = nodeListToArray(elements);
}
return elements;
}; // ElementCollection contains the result of calling OTHelpers.
//
// It has the following properties:
// length
// first
// last
//
// It also has a get method that can be used to access elements in the collection
//
// var videos = OTHelpers('video');
// var firstElement = videos.get(0); // identical to videos.first
// var lastElement = videos.get(videos.length-1); // identical to videos.last
// var allVideos = videos.get();
//
//
// The collection also implements the following helper methods:
// some, forEach, map, filter, find,
// appendTo, after, before, remove, empty,
// attr, center, width, height,
// addClass, removeClass, hasClass, toggleClass,
// on, off, once,
// observeStyleChanges, observeNodeOrChildNodeRemoval
//
// Mostly the usage should be obvious. When in doubt, assume it functions like
// the jQuery equivalent.
//
const ElementCollection = function ElementCollection(selector, context) {
const elements = selectorToElementArray(selector, context);
this.context = context;
this.toArray = function () {
return elements;
};
this.length = elements.length;
this.first = elements[0];
this.last = elements[elements.length - 1];
this.get = function (index) {
if (index === void 0) {
return elements;
}
return elements[index];
};
};
module.exports = ElementCollection;
ElementCollection._attachToOTHelpers = {}; // @remove
ElementCollection._attachToOTHelpers.removeElement = function (element) {
return new ElementCollection(element).remove();
};
ElementCollection.prototype.getAsArray = function () {
let _collection = this.get();
if (!isFunction(_collection.forEach)) {
// It's possibly something Array-ish that isn't quite an
// Array. Something like arguments or a NodeList
_collection = nodeListToArray(_collection);
}
return _collection;
};
ElementCollection.prototype.some = function (iter, context) {
return this.getAsArray().some(iter, context);
};
ElementCollection.prototype.forEach = function (fn, context) {
this.getAsArray().forEach(fn, context);
return this;
};
ElementCollection.prototype.map = function (fn, context) {
return new ElementCollection(this.getAsArray().map(fn, context), this.context);
};
ElementCollection.prototype.filter = function (fn, context) {
return new ElementCollection(this.getAsArray().filter(fn, context), this.context);
};
ElementCollection.prototype.find = function (selector) {
return new ElementCollection(selector, this.first);
}; // Helper function for adding event listeners to dom elements.
// WARNING: This doesn't preserve event types, your handler could
// be getting all kinds of different parameters depending on the browser.
// You also may have different scopes depending on the browser and bubbling
// and cancelable are not supported.
ElementCollection.prototype.on = function (eventName, handler) {
return this.forEach(element => {
element.addEventListener(eventName, handler, false);
});
}; // Helper function for removing event listeners from dom elements.
ElementCollection.prototype.off = function (eventName, handler) {
return this.forEach(element => {
element.removeEventListener(eventName, handler, false);
});
};
ElementCollection.prototype.once = function (eventName, handler) {
const removeAfterTrigger = function () {
this.off(eventName, removeAfterTrigger);
handler(...arguments);
}.bind(this);
return this.on(eventName, removeAfterTrigger);
};
ElementCollection.prototype.appendTo = function (parentElement) {
if (!parentElement) {
throw new Error('appendTo requires a DOMElement to append to.');
}
return this.forEach(child => {
parentElement.appendChild(child);
});
};
ElementCollection.prototype.append = function () {
const parentElement = this.first;
if (!parentElement) {
return this;
}
Array.prototype.forEach.call(arguments, child => {
parentElement.appendChild(child);
});
return this;
};
ElementCollection.prototype.prepend = function () {
if (arguments.length === 0) {
return this;
}
const parentElement = this.first;
let elementsToPrepend;
if (!parentElement) {
return this;
}
elementsToPrepend = Array.prototype.slice.call(arguments);
if (!parentElement.firstElementChild) {
parentElement.appendChild(elementsToPrepend.shift());
}
elementsToPrepend.forEach(element => {
parentElement.insertBefore(element, parentElement.firstElementChild);
});
return this;
};
ElementCollection.prototype.after = function (prevElement) {
if (!prevElement) {
throw new Error('after requires a DOMElement to insert after');
}
return this.forEach(element => {
if (element.parentElement) {
if (prevElement !== element.parentNode.lastChild) {
element.parentElement.insertBefore(element, prevElement);
} else {
element.parentElement.appendChild(element);
}
}
});
};
ElementCollection.prototype.before = function (nextElement) {
if (!nextElement) {
throw new Error('before requires a DOMElement to insert before');
}
return this.forEach(element => {
if (element.parentElement) {
element.parentElement.insertBefore(element, nextElement);
}
});
};
ElementCollection.prototype.remove = function () {
return this.forEach(element => {
if (element.parentNode) {
element.parentNode.removeChild(element);
}
});
};
ElementCollection.prototype.empty = function () {
return this.forEach(element => {
// elements is a "live" NodesList collection. Meaning that the collection
// itself will be mutated as we remove elements from the DOM. This means
// that "while there are still elements" is safer than "iterate over each
// element" as the collection length and the elements indices will be modified
// with each iteration.
while (element.firstChild) {
element.removeChild(element.firstChild);
}
});
}; // Detects when an element is not part of the document flow because
// it or one of it's ancesters has display:none.
ElementCollection.prototype.isDisplayNone = function () {
return this.some(element => {
if ((element.offsetWidth === 0 || element.offsetHeight === 0) && new ElementCollection(element).css('display') === 'none') {
return true;
}
if (element.parentNode && element.parentNode.style) {
return new ElementCollection(element.parentNode).isDisplayNone();
}
return false;
});
};
const findElementWithDisplayNone = function findElementWithDisplayNone(element) {
if ((element.offsetWidth === 0 || element.offsetHeight === 0) && new ElementCollection(element).css('display') === 'none') {
return element;
}
if (element.parentNode && element.parentNode.style) {
return findElementWithDisplayNone(element.parentNode);
}
return null;
}; // @remove
ElementCollection._attachToOTHelpers.emptyElement = function (element) {
return new ElementCollection(element).empty();
};
ElementCollection._attachToOTHelpers._findElementWithDisplayNone = findElementWithDisplayNone;
extensions.css(ElementCollection, findElementWithDisplayNone);
extensions.attributes(ElementCollection);
extensions.classNames(ElementCollection);
extensions.observers(ElementCollection); // TODO: Deprecation logging?
// @remove
['on', 'off', 'isDisplayNone', 'show', 'hide', 'css', 'makeVisibleAndYield', 'addClass', 'removeClass'].forEach(methodName => {
ElementCollection._attachToOTHelpers[methodName] = function ()
/* arguments */
{
const args = Array.prototype.slice.apply(arguments);
const element = args.shift();
const collection = new ElementCollection(element);
return ElementCollection.prototype[methodName].apply(collection, args);
};
});
/***/ }),
/* 223 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const logging = __webpack_require__(1)('deprecation');
module.exports = function setDeprecatedProperty(object, key, _temp) {
let _ref = _temp === void 0 ? {} : _temp,
defaultValue = _ref.value,
_ref$name = _ref.name,
name = _ref$name === void 0 ? "" + key : _ref$name,
_ref$getWarning = _ref.getWarning,
getWarning = _ref$getWarning === void 0 ? '' : _ref$getWarning,
_ref$setWarning = _ref.setWarning,
setWarning = _ref$setWarning === void 0 ? '' : _ref$setWarning,
_ref$canSet = _ref.canSet,
canSet = _ref$canSet === void 0 ? false : _ref$canSet,
_ref$warnOnSet = _ref.warnOnSet,
warnOnSet = _ref$warnOnSet === void 0 ? false : _ref$warnOnSet;
let value = defaultValue;
let hasWarnedOnGet = false;
let hasWarnedOnSet = !warnOnSet;
const propertyDescriptor = {
get() {
if (!hasWarnedOnGet) {
hasWarnedOnGet = true;
logging.warn((name + " is deprecated, and will be removed in the future. " + getWarning).trim());
}
return value;
},
set(newValue) {
if (canSet) {
value = newValue;
}
if (!hasWarnedOnSet) {
hasWarnedOnSet = true;
if (canSet) {
logging.warn((name + " is deprecated. " + setWarning).trim());
} else {
logging.warn((name + " is deprecated, and will be removed in the future. " + setWarning).trim());
}
}
}
};
Object.defineProperty(object, key, propertyDescriptor);
};
/***/ }),
/* 224 */
/***/ (function(module, exports, __webpack_require__) {
var baseRest = __webpack_require__(94),
isIterateeCall = __webpack_require__(129);
/**
* Creates a function like `_.assign`.
*
* @private
* @param {Function} assigner The function to assign values.
* @returns {Function} Returns the new assigner function.
*/
function createAssigner(assigner) {
return baseRest(function(object, sources) {
var index = -1,
length = sources.length,
customizer = length > 1 ? sources[length - 1] : undefined,
guard = length > 2 ? sources[2] : undefined;
customizer = (assigner.length > 3 && typeof customizer == 'function')
? (length--, customizer)
: undefined;
if (guard && isIterateeCall(sources[0], sources[1], guard)) {
customizer = length < 3 ? undefined : customizer;
length = 1;
}
object = Object(object);
while (++index < length) {
var source = sources[index];
if (source) {
assigner(object, source, index, customizer);
}
}
return object;
});
}
module.exports = createAssigner;
/***/ }),
/* 225 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = __webpack_require__(464)() ? WeakMap : __webpack_require__(465);
/***/ }),
/* 226 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var create = Object.create, getPrototypeOf = Object.getPrototypeOf, plainObject = {};
module.exports = function (/* CustomCreate*/) {
var setPrototypeOf = Object.setPrototypeOf, customCreate = arguments[0] || create;
if (typeof setPrototypeOf !== "function") return false;
return getPrototypeOf(setPrototypeOf(customCreate(null), plainObject)) === plainObject;
};
/***/ }),
/* 227 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* eslint no-proto: "off" */
// Big thanks to @WebReflection for sorting this out
// https://gist.github.com/WebReflection/5593554
var isObject = __webpack_require__(228)
, value = __webpack_require__(31)
, objIsPrototypeOf = Object.prototype.isPrototypeOf
, defineProperty = Object.defineProperty
, nullDesc = { configurable: true, enumerable: false, writable: true, value: undefined }
, validate;
validate = function (obj, prototype) {
value(obj);
if (prototype === null || isObject(prototype)) return obj;
throw new TypeError("Prototype must be null or an object");
};
module.exports = (function (status) {
var fn, set;
if (!status) return null;
if (status.level === 2) {
if (status.set) {
set = status.set;
fn = function (obj, prototype) {
set.call(validate(obj, prototype), prototype);
return obj;
};
} else {
fn = function (obj, prototype) {
validate(obj, prototype).__proto__ = prototype;
return obj;
};
}
} else {
fn = function self(obj, prototype) {
var isNullBase;
validate(obj, prototype);
isNullBase = objIsPrototypeOf.call(self.nullPolyfill, obj);
if (isNullBase) delete self.nullPolyfill.__proto__;
if (prototype === null) prototype = self.nullPolyfill;
obj.__proto__ = prototype;
if (isNullBase) defineProperty(self.nullPolyfill, "__proto__", nullDesc);
return obj;
};
}
return Object.defineProperty(fn, "level", {
configurable: false,
enumerable: false,
writable: false,
value: status.level
});
})(
(function () {
var tmpObj1 = Object.create(null)
, tmpObj2 = {}
, set
, desc = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__");
if (desc) {
try {
set = desc.set; // Opera crashes at this point
set.call(tmpObj1, tmpObj2);
} catch (ignore) {}
if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { set: set, level: 2 };
}
tmpObj1.__proto__ = tmpObj2;
if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 2 };
tmpObj1 = {};
tmpObj1.__proto__ = tmpObj2;
if (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 1 };
return false;
})()
);
__webpack_require__(467);
/***/ }),
/* 228 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var isValue = __webpack_require__(44);
var map = { function: true, object: true };
module.exports = function (value) { return (isValue(value) && map[typeof value]) || false; };
/***/ }),
/* 229 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var isFunction = __webpack_require__(470);
var classRe = /^\s*class[\s{/}]/, functionToString = Function.prototype.toString;
module.exports = function (value) {
if (!isFunction(value)) return false;
if (classRe.test(functionToString.call(value))) return false;
return true;
};
/***/ }),
/* 230 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var isValue = __webpack_require__(44);
var forEach = Array.prototype.forEach, create = Object.create;
var process = function (src, obj) {
var key;
for (key in src) obj[key] = src[key];
};
// eslint-disable-next-line no-unused-vars
module.exports = function (opts1/*, …options*/) {
var result = create(null);
forEach.call(arguments, function (options) {
if (!isValue(options)) return;
process(Object(options), result);
});
return result;
};
/***/ }),
/* 231 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = __webpack_require__(477)() ? String.prototype.contains : __webpack_require__(478);
/***/ }),
/* 232 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var isArguments = __webpack_require__(95)
, isString = __webpack_require__(96)
, ArrayIterator = __webpack_require__(479)
, StringIterator = __webpack_require__(508)
, iterable = __webpack_require__(509)
, iteratorSymbol = __webpack_require__(45).iterator;
module.exports = function (obj) {
if (typeof iterable(obj)[iteratorSymbol] === "function") return obj[iteratorSymbol]();
if (isArguments(obj)) return new ArrayIterator(obj);
if (isString(obj)) return new StringIterator(obj);
return new ArrayIterator(obj);
};
/***/ }),
/* 233 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var isSymbol = __webpack_require__(484);
module.exports = function (value) {
if (!isSymbol(value)) throw new TypeError(value + " is not a symbol");
return value;
};
/***/ }),
/* 234 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var clear = __webpack_require__(488)
, assign = __webpack_require__(132)
, callable = __webpack_require__(70)
, value = __webpack_require__(31)
, d = __webpack_require__(32)
, autoBind = __webpack_require__(489)
, Symbol = __webpack_require__(45);
var defineProperty = Object.defineProperty, defineProperties = Object.defineProperties, Iterator;
module.exports = Iterator = function (list, context) {
if (!(this instanceof Iterator)) throw new TypeError("Constructor requires 'new'");
defineProperties(this, {
__list__: d("w", value(list)),
__context__: d("w", context),
__nextIndex__: d("w", 0)
});
if (!context) return;
callable(context.on);
context.on("_add", this._onAdd);
context.on("_delete", this._onDelete);
context.on("_clear", this._onClear);
};
// Internal %IteratorPrototype% doesn't expose its constructor
delete Iterator.prototype.constructor;
defineProperties(
Iterator.prototype,
assign(
{
_next: d(function () {
var i;
if (!this.__list__) return undefined;
if (this.__redo__) {
i = this.__redo__.shift();
if (i !== undefined) return i;
}
if (this.__nextIndex__ < this.__list__.length) return this.__nextIndex__++;
this._unBind();
return undefined;
}),
next: d(function () {
return this._createResult(this._next());
}),
_createResult: d(function (i) {
if (i === undefined) return { done: true, value: undefined };
return { done: false, value: this._resolve(i) };
}),
_resolve: d(function (i) {
return this.__list__[i];
}),
_unBind: d(function () {
this.__list__ = null;
delete this.__redo__;
if (!this.__context__) return;
this.__context__.off("_add", this._onAdd);
this.__context__.off("_delete", this._onDelete);
this.__context__.off("_clear", this._onClear);
this.__context__ = null;
}),
toString: d(function () {
return "[object " + (this[Symbol.toStringTag] || "Object") + "]";
})
},
autoBind({
_onAdd: d(function (index) {
if (index >= this.__nextIndex__) return;
++this.__nextIndex__;
if (!this.__redo__) {
defineProperty(this, "__redo__", d("c", [index]));
return;
}
this.__redo__.forEach(function (redo, i) {
if (redo >= index) this.__redo__[i] = ++redo;
}, this);
this.__redo__.push(index);
}),
_onDelete: d(function (index) {
var i;
if (index >= this.__nextIndex__) return;
--this.__nextIndex__;
if (!this.__redo__) return;
i = this.__redo__.indexOf(index);
if (i !== -1) this.__redo__.splice(i, 1);
this.__redo__.forEach(function (redo, j) {
if (redo > index) this.__redo__[j] = --redo;
}, this);
}),
_onClear: d(function () {
if (this.__redo__) clear.call(this.__redo__);
this.__nextIndex__ = 0;
})
})
)
);
defineProperty(
Iterator.prototype,
Symbol.iterator,
d(function () {
return this;
})
);
/***/ }),
/* 235 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var isValue = __webpack_require__(53)
, isObject = __webpack_require__(131)
, stringCoerce = __webpack_require__(491)
, toShortString = __webpack_require__(492);
var resolveMessage = function (message, value) {
return message.replace("%v", toShortString(value));
};
module.exports = function (value, defaultMessage, inputOptions) {
if (!isObject(inputOptions)) throw new TypeError(resolveMessage(defaultMessage, value));
if (!isValue(value)) {
if ("default" in inputOptions) return inputOptions["default"];
if (inputOptions.isOptional) return null;
}
var errorMessage = stringCoerce(inputOptions.errorMessage);
if (!isValue(errorMessage)) errorMessage = defaultMessage;
throw new TypeError(resolveMessage(errorMessage, value));
};
/***/ }),
/* 236 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-underscore-dangle, no-void, no-param-reassign */
/* eslint-disable no-restricted-syntax, no-continue */
const $ = __webpack_require__(221);
const env = __webpack_require__(3);
const makeEverythingAttachToOTHelpers = __webpack_require__(237);
const assign = __webpack_require__(6);
const browserAjax = {};
module.exports = browserAjax;
function formatPostData(data) {
// , contentType
// If it's a string, we assume it's properly encoded
if (typeof data === 'string') {
return data;
}
const queryString = [];
Object.keys(data).forEach(key => {
queryString.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
});
return queryString.join('&').replace(/\+/g, '%20');
}
if (env.name !== 'Node') {
browserAjax.request = function (url, options, callback) {
const request = new (typeof window !== undefined ? window : global).XMLHttpRequest();
const _options = options || {};
const _method = _options.method;
if (!_method) {
callback(new Error('No HTTP method specified in options'));
return;
}
if (options.overrideMimeType) {
if (request.overrideMimeType) {
request.overrideMimeType(options.overrideMimeType);
}
delete options.overrideMimeType;
} // Setup callbacks to correctly respond to success and error callbacks. This includes
// interpreting the responses HTTP status, which XmlHttpRequest seems to ignore
// by default.
if (callback) {
$(request).on('load', event => {
const status = event.target.status; // We need to detect things that XMLHttpRequest considers a success,
// but we consider to be failures.
if (status >= 200 && (status < 300 || status === 304)) {
callback(null, event);
} else {
callback(event);
}
});
$(request).on('error', callback);
}
request.open(options.method, url, true);
if (!_options.headers) {
_options.headers = {};
}
for (const name in _options.headers) {
if (!Object.prototype.hasOwnProperty.call(_options.headers, name)) {
continue;
}
request.setRequestHeader(name, _options.headers[name]);
}
request.send(options.body && formatPostData(options.body));
};
browserAjax.get = function (url, options, callback) {
const _options = assign(options || {}, {
method: 'GET'
});
browserAjax.request(url, _options, callback);
};
browserAjax.post = function (url, options, callback) {
const _options = assign(options || {}, {
method: 'POST'
});
browserAjax.request(url, _options, callback);
};
browserAjax.getJSON = function (url, options, callback) {
options = options || {};
const done = function done(error, event) {
if (error) {
callback(error, event && event.target && event.target.responseText);
} else {
let response;
try {
response = JSON.parse(event.target.responseText);
} catch (e) {
// Badly formed JSON
callback(e, event && event.target && event.target.responseText);
return;
}
callback(null, response, event);
}
};
const extendedHeaders = assign({
Accept: 'application/json'
}, options.headers || {});
browserAjax.get(url, assign(options || {}, {
headers: extendedHeaders
}), done);
};
makeEverythingAttachToOTHelpers(browserAjax);
}
/***/ }),
/* 237 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-underscore-dangle, no-param-reassign */
module.exports = function (mod) {
const attachments = {};
Object.keys(mod).forEach(key => {
attachments[key] = mod[key];
});
mod._attachToOTHelpers = attachments;
};
/***/ }),
/* 238 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const assign = __webpack_require__(6);
const logging = __webpack_require__(1)('analytics');
const QueueRunner = __webpack_require__(514);
const uuid = __webpack_require__(15);
const defaultAjax = __webpack_require__(236);
const clientInstanceId = uuid();
const defaultQueue = new QueueRunner();
module.exports = function Analytics(_ref) {
var _this = this;
let loggingUrl = _ref.loggingUrl,
_ref$queue = _ref.queue,
queue = _ref$queue === void 0 ? defaultQueue : _ref$queue,
_ref$ajax = _ref.ajax,
ajax = _ref$ajax === void 0 ? defaultAjax : _ref$ajax;
this.loggingUrl = loggingUrl;
const endPoint = this.loggingUrl + "/logging/ClientEvent";
const endPointQos = this.loggingUrl + "/logging/ClientQos";
const reportedErrors = {};
const post = function post(data, onComplete, isQos) {
if (onComplete === void 0) {
onComplete = () => {};
}
queue.add(callback => {
logging.debug('sending analytics:', _this.loggingUrl, data);
ajax.post(isQos ? endPointQos : endPoint, {
body: data,
overrideMimeType: 'text/plain',
headers: {
Accept: 'text/plain',
'Content-Type': 'application/json'
}
}, err => {
if (err) {
console.log('Failed to send ClientEvent, moving on to the next item.');
}
onComplete(err || undefined);
callback();
});
});
};
const shouldThrottleError = (code, type, partnerId) => {
if (!partnerId) {
return false;
}
const errKey = [partnerId, type, code].join('_');
const msgLimit = 100;
return (reportedErrors[errKey] || 0) >= msgLimit;
}; // Log an error via ClientEvents.
//
// @param [String] code
// @param [String] type
// @param [String] message
// @param [Hash] details additional error details
//
// @param [Hash] options the options to log the client event with.
// @option options [String] action The name of the Event that we are logging. E.g.
// 'TokShowLoaded'. Required.
// @option options [String] variation Usually used for Split A/B testing, when you
// have multiple variations of the +_action+.
// @option options [String] payload The payload. Required.
// @option options [String] sessionId The active OpenTok session, if there is one
// @option options [String] connectionId The active OpenTok connectionId, if there is one
// @option options [String] partnerId
// @option options [String] guid ...
// @option options [String] streamId ...
// @option options [String] section ...
// @option options [String] clientVersion ...
//
// Reports will be throttled to X reports (see exceptionLogging.messageLimitPerPartner
// from the dynamic config for X) of each error type for each partner. Reports can be
// disabled/enabled globally or on a per partner basis (per partner settings
// take precedence) using exceptionLogging.enabled.
//
this.logError = function (code, type, message, details, options) {
if (details === void 0) {
details = null;
}
if (options === void 0) {
options = {};
}
const _options = options,
partnerId = _options.partnerId;
if (shouldThrottleError(code, type, partnerId)) {
// OT.log('ClientEvents.error has throttled an error of type ' + type + '.' +
// code + ' for partner ' + (partnerId || 'No Partner Id'));
return;
}
const errKey = [partnerId, type, code].join('_');
const payload = details;
reportedErrors[errKey] = typeof reportedErrors[errKey] !== 'undefined' ? reportedErrors[errKey] + 1 : 1;
_this.logEvent(assign(options, {
action: type + "." + code,
payload
}), false);
}; // Log a client event to the analytics backend.
//
// @example Logs a client event called 'foo'
// this.logEvent({
// action: 'foo',
// payload: 'bar',
// sessionId: sessionId,
// connectionId: connectionId
// }, false)
//
// @param [Hash] data the data to log the client event with.
// @param [Boolean] qos Whether this is a QoS event.
// @param [Boolean] throttle A number specifying the ratio of events to be sent
// out of the total number of events (other events are not ignored). If not
// set to a number, all events are sent.
// @param [Number] completionHandler A completion handler function to call when the
// client event POST request succeeds or fails. If it fails, an error
// object is passed into the function. (See throttledPost().)
//
this.logEvent = function (data, qos, throttle, completionHandler) {
if (qos === void 0) {
qos = false;
}
if (completionHandler === void 0) {
completionHandler = () => {};
}
data.clientInstanceId = clientInstanceId; // eslint-disable-line no-param-reassign
if (throttle && !isNaN(throttle)) {
if (Math.random() > throttle) {
logging.debug('skipping sending analytics due to throttle:', data);
return;
}
}
logging.debug('queueing analytics:', _this.loggingUrl, data);
let serializedData;
try {
serializedData = JSON.stringify(data);
} catch (err) {
completionHandler(err);
return;
}
post(serializedData, completionHandler, qos);
}; // Log a client QOS to the analytics backend.
// @option options [String] action The name of the Event that we are logging.
// E.g. 'TokShowLoaded'. Required.
// @option options [String] variation Usually used for Split A/B testing, when
// you have multiple variations of the +_action+.
// @option options [String] payload The payload. Required.
// @option options [String] sessionId The active OpenTok session, if there is one
// @option options [String] connectionId The active OpenTok connectionId, if there is one
// @option options [String] partnerId
// @option options [String] guid ...
// @option options [String] streamId ...
// @option options [String] section ...
// @option options [String] clientVersion ...
//
this.logQOS = options => this.logEvent(options, true);
};
/***/ }),
/* 239 */
/***/ (function(module, exports) {
// Unique ID creation requires a high quality random # generator. In the
// browser this is a little complicated due to unknown quality of Math.random()
// and inconsistent support for the `crypto` API. We do the best we can via
// feature-detection
// getRandomValues needs to be invoked in a context where "this" is a Crypto
// implementation. Also, find the complete implementation of crypto on IE11.
var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
(typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));
if (getRandomValues) {
// WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef
module.exports = function whatwgRNG() {
getRandomValues(rnds8);
return rnds8;
};
} else {
// Math.random()-based (RNG)
//
// If all else fails, use Math.random(). It's fast, but is of unspecified
// quality.
var rnds = new Array(16);
module.exports = function mathRNG() {
for (var i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
}
return rnds;
};
}
/***/ }),
/* 240 */
/***/ (function(module, exports) {
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex[i] = (i + 0x100).toString(16).substr(1);
}
function bytesToUuid(buf, offset) {
var i = offset || 0;
var bth = byteToHex;
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
return ([
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]], '-',
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]],
bth[buf[i++]], bth[buf[i++]]
]).join('');
}
module.exports = bytesToUuid;
/***/ }),
/* 241 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-underscore-dangle, no-void, no-restricted-syntax, no-prototype-builtins */
/* eslint-disable no-continue, prefer-spread, prefer-rest-params */
const isFunction = __webpack_require__(13);
const eventing = __webpack_require__(5);
const logging = __webpack_require__(1)('Collection');
module.exports = function Collection(idField) {
let _models = [];
let _byId = {};
const _idField = idField || 'id';
eventing(this);
const modelProperty = function modelProperty(model, property) {
if (isFunction(model[property])) {
return model[property]();
}
return model[property];
};
const onModelUpdate = function onModelUpdate(event) {
this.trigger('update', event);
this.trigger("update:" + event.target.id, event);
}.bind(this);
const onModelDestroy = function onModelDestroyed(event) {
this.remove(event.target, event.reason);
}.bind(this);
this.reset = function () {
// Stop listening on the models, they are no longer our problem
_models.forEach(function (model) {
model.off('updated', onModelUpdate, this);
model.off('destroyed', onModelDestroy, this);
}, this);
_models = [];
_byId = {};
};
this.destroy = function (reason) {
_models.forEach(model => {
if (model && typeof model.destroy === 'function') {
model.destroy(reason, true);
}
});
this.reset();
this.off();
};
this.get = function (id) {
return id && _byId[id] !== void 0 ? _models[_byId[id]] : void 0;
};
this.has = function (id) {
return id && _byId[id] !== void 0;
};
this.toString = function () {
return _models.toString();
}; // Return only models filtered by either a dict of properties
// or a filter function.
//
// @example Return all publishers with a streamId of 1
// OT.publishers.where({streamId: 1})
//
// @example The same thing but filtering using a filter function
// OT.publishers.where(function(publisher) {
// return publisher.stream.id === 4;
// });
//
// @example The same thing but filtering using a filter function
// executed with a specific this
// OT.publishers.where(function(publisher) {
// return publisher.stream.id === 4;
// }, self);
//
this.where = function (attrsOrFilterFn, context) {
if (isFunction(attrsOrFilterFn)) {
return _models.filter(attrsOrFilterFn, context);
}
return _models.filter(model => {
for (const key in attrsOrFilterFn) {
if (!attrsOrFilterFn.hasOwnProperty(key)) {
continue;
}
if (modelProperty(model, key) !== attrsOrFilterFn[key]) {
return false;
}
}
return true;
});
}; // Similar to where in behaviour, except that it only returns
// the first match.
this.find = function (attrsOrFilterFn, context) {
let filterFn;
if (isFunction(attrsOrFilterFn)) {
filterFn = attrsOrFilterFn;
} else {
filterFn = function filterFn(model) {
for (const key in attrsOrFilterFn) {
if (!attrsOrFilterFn.hasOwnProperty(key)) {
continue;
}
if (modelProperty(model, key) !== attrsOrFilterFn[key]) {
return false;
}
}
return true;
};
}
filterFn = filterFn.bind(context);
for (let i = 0; i < _models.length; ++i) {
if (filterFn(_models[i]) === true) {
return _models[i];
}
}
return null;
};
this.forEach = function (fn, context) {
_models.forEach(fn, context);
return this;
};
this.map = function (fn) {
return _models.map(fn);
};
this.add = function (model) {
const id = modelProperty(model, _idField);
if (this.has(id)) {
logging.warn("Model " + id + " is already in the collection", _models);
return this;
}
_byId[id] = _models.push(model) - 1;
model.on('updated', onModelUpdate, this);
model.on('destroyed', onModelDestroy, this);
this.trigger('add', model);
this.trigger("add:" + id, model);
return this;
};
this.remove = function (model, reason) {
const id = modelProperty(model, _idField);
_models.splice(_byId[id], 1); // Shuffle everyone down one
for (let i = _byId[id]; i < _models.length; ++i) {
_byId[modelProperty(_models[i], _idField)] = i;
}
delete _byId[id];
model.off('updated', onModelUpdate, this);
model.off('destroyed', onModelDestroy, this);
this.trigger('remove', model, reason);
this.trigger("remove:" + id, model, reason);
return this;
}; // Retrigger the add event behaviour for each model. You can also
// select a subset of models to trigger using the same arguments
// as the #where method.
this._triggerAddEvents = function () {
this.where.apply(this, arguments).forEach(function (model) {
this.trigger('add', model);
this.trigger("add:" + modelProperty(model, _idField), model);
}, this);
};
this.length = function () {
return _models.length;
};
};
/***/ }),
/* 242 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-underscore-dangle, no-param-reassign, no-void */
/* eslint-disable no-restricted-syntax, no-prototype-builtins, no-shadow, one-var, vars-on-top */
/* eslint-disable no-var */
const env = __webpack_require__(3);
const isObject = __webpack_require__(7);
let getErrorLocation; // Properties that we'll acknowledge from the JS Error object
const safeErrorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; // OTHelpers.Error
//
// A construct to contain error information that also helps with extracting error
// context, such as stack trace.
//
// @constructor
// @memberof OTHelpers
// @method Error
//
// @param {String} message
// Optional. The error message
//
// @param {Object} props
// Optional. A dictionary of properties containing extra Error info.
//
//
// @example Create a simple error with juts a custom message
// var error = new OTHelpers.Error('Something Broke!');
// error.message === 'Something Broke!';
//
// @example Create an Error with a message and a name
// var error = new OTHelpers.Error('Something Broke!', 'FooError');
// error.message === 'Something Broke!';
// error.name === 'FooError';
//
// @example Create an Error with a message, name, and custom properties
// var error = new OTHelpers.Error('Something Broke!', 'FooError', {
// foo: 'bar',
// listOfImportantThings: [1,2,3,4]
// });
// error.message === 'Something Broke!';
// error.name === 'FooError';
// error.foo === 'bar';
// error.listOfImportantThings == [1,2,3,4];
//
// @example Create an Error from a Javascript Error
// var error = new OTHelpers.Error(domSyntaxError);
// error.message === domSyntaxError.message;
// error.name === domSyntaxError.name === 'SyntaxError';
// // ...continues for each properties of domSyntaxError
//
// @references
// * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
// * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack
// * http://www.w3.org/TR/dom/#interface-domerror
//
//
// @todo
// * update usage in OTMedia
// * replace error handling in OT.js
// * normalise stack behaviour under Chrome/Node/Safari with other browsers
// * unit test for stack parsing
//
const Error_ = function Error_(message, name, props) {
switch (arguments.length) {
case 1:
if (isObject(message)) {
props = message;
name = void 0;
message = void 0;
} // Otherwise it's the message
break;
case 2:
if (isObject(name)) {
props = name;
name = void 0;
} // Otherwise name is actually the name
break;
default: // FIXME: Should something go here?
}
if (props instanceof Error) {
// Special handling of this due to Chrome weirdness. It seems that
// properties of the Error object, and it's children, are not
// enumerable in Chrome?
for (let i = 0, num = safeErrorProps.length; i < num; ++i) {
this[safeErrorProps[i]] = props[safeErrorProps[i]];
}
} else if (isObject(props)) {
// Use an custom properties that are provided
for (const key in props) {
if (props.hasOwnProperty(key)) {
this[key] = props[key];
}
}
} // If any of the fundamental properties are missing then try and
// extract them.
if (!(this.fileName && this.lineNumber && this.columnNumber && this.stack)) {
const err = getErrorLocation();
if (!this.fileName && err.fileName) {
this.fileName = err.fileName;
}
if (!this.lineNumber && err.lineNumber) {
this.lineNumber = err.lineNumber;
}
if (!this.columnNumber && err.columnNumber) {
this.columnNumber = err.columnNumber;
}
if (!this.stack && err.stack) {
this.stack = err.stack;
}
}
if (!this.message && message) {
this.message = message;
}
if ((!this.name || this.name === 'Error') && name) {
this.name = name;
}
};
module.exports = Error_;
Error_.prototype = Object.create(Error.prototype);
Error_.prototype.toString = function () {
let locationDetails = '';
if (this.fileName) {
locationDetails += " " + this.fileName;
}
if (this.lineNumber) {
locationDetails += " " + this.lineNumber;
if (this.columnNumber) {
locationDetails += ":" + this.columnNumber;
}
}
return "<" + (this.name ? this.name + " " : '') + this.message + locationDetails + ">";
};
Error_.prototype.valueOf = Error_.prototype.toString; // Normalise err.stack so that it is the same format as the other browsers
// We skip the first two frames so that we don't capture getErrorLocation() and
// the callee.
//
// Used by Environments that support the StackTrace API. (Chrome, Node, Opera)
//
const prepareStackTrace = function prepareStackTrace(_, stack) {
return stack.slice(2).map(frame => {
const _f = {
fileName: frame.getFileName(),
linenumber: frame.getLineNumber(),
columnNumber: frame.getColumnNumber()
};
if (frame.getFunctionName()) {
_f.functionName = frame.getFunctionName();
}
if (frame.getMethodName()) {
_f.methodName = frame.getMethodName();
}
if (frame.getThis()) {
_f.self = frame.getThis();
}
return _f;
});
}; // Black magic to retrieve error location info for various environments
getErrorLocation = function getErrorLocation() {
const info = {};
let callstack, errLocation, err;
switch (env.name) {
case 'Firefox':
case 'Safari':
try {
(typeof window !== undefined ? window : global).call.js.is.explody();
} catch (e) {
err = e;
}
callstack = (err.stack || '').split('\n'); // Remove call to getErrorLocation() and the callee
callstack.shift();
callstack.shift();
info.stack = callstack;
errLocation = /@(.+?):([0-9]+)(:([0-9]+))?$/.exec(callstack[0]);
if (errLocation) {
info.fileName = errLocation[1];
info.lineNumber = parseInt(errLocation[2], 10);
if (errLocation.length > 3) {
info.columnNumber = parseInt(errLocation[4], 10);
}
}
break;
case 'Chrome':
case 'Node':
case 'Opera':
var currentPST = Error.prepareStackTrace;
Error.prepareStackTrace = prepareStackTrace;
err = new Error();
info.stack = err.stack;
Error.prepareStackTrace = currentPST;
var topFrame = info.stack[0];
info.lineNumber = topFrame.lineNumber;
info.columnNumber = topFrame.columnNumber;
info.fileName = topFrame.fileName;
if (topFrame.functionName) {
info.functionName = topFrame.functionName;
}
if (topFrame.methodName) {
info.methodName = topFrame.methodName;
}
if (topFrame.self) {
info.self = topFrame.self;
}
break;
default:
err = new Error();
if (err.stack) {
info.stack = err.stack.split('\n');
}
break;
}
if (err.message) {
info.message = err.message;
}
return info;
};
/***/ }),
/* 243 */
/***/ (function(module, exports) {
/*
json-stringify-safe
Like JSON.stringify, but doesn't throw on circular references.
Originally forked from https://github.com/isaacs/json-stringify-safe
version 5.0.1 on 3/8/2017 and modified to handle Errors serialization
and IE8 compatibility. Tests for this are in test/vendor.
ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE
*/
exports = module.exports = stringify;
exports.getSerialize = serializer;
function indexOf(haystack, needle) {
for (var i = 0; i < haystack.length; ++i) {
if (haystack[i] === needle) return i;
}
return -1;
}
function stringify(obj, replacer, spaces, cycleReplacer) {
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);
}
// https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106
function stringifyError(value) {
var err = {
// These properties are implemented as magical getters and don't show up in for in
stack: value.stack,
message: value.message,
name: value.name
};
for (var i in value) {
if (Object.prototype.hasOwnProperty.call(value, i)) {
err[i] = value[i];
}
}
return err;
}
function serializer(replacer, cycleReplacer) {
var stack = [];
var keys = [];
if (cycleReplacer == null) {
cycleReplacer = function(key, value) {
if (stack[0] === value) {
return '[Circular ~]';
}
return '[Circular ~.' + keys.slice(0, indexOf(stack, value)).join('.') + ']';
};
}
return function(key, value) {
if (stack.length > 0) {
var thisPos = indexOf(stack, this);
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
if (~indexOf(stack, value)) {
value = cycleReplacer.call(this, key, value);
}
} else {
stack.push(value);
}
return replacer == null
? value instanceof Error ? stringifyError(value) : value
: replacer.call(this, key, value);
};
}
/***/ }),
/* 244 */
/***/ (function(module, exports, __webpack_require__) {
var arrayMap = __webpack_require__(30),
baseIntersection = __webpack_require__(524),
baseRest = __webpack_require__(94),
castArrayLikeObject = __webpack_require__(525);
/**
* Creates an array of unique values that are included in all given arrays
* using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
* for equality comparisons. The order and references of result values are
* determined by the first array.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Array
* @param {...Array} [arrays] The arrays to inspect.
* @returns {Array} Returns the new array of intersecting values.
* @example
*
* _.intersection([2, 1], [2, 3]);
* // => [2]
*/
var intersection = baseRest(function(arrays) {
var mapped = arrayMap(arrays, castArrayLikeObject);
return (mapped.length && mapped[0] === arrays[0])
? baseIntersection(mapped)
: [];
});
module.exports = intersection;
/***/ }),
/* 245 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-param-reassign, no-shadow */
const isFunction = __webpack_require__(13);
const uuid = __webpack_require__(15);
const logging = __webpack_require__(1)('guidStorage');
const guidStorage = {};
module.exports = guidStorage;
let currentGuidStorage;
let currentGuid;
const isInvalidStorage = function isInvalidStorage(storageInterface) {
return !(isFunction(storageInterface.get) && isFunction(storageInterface.set));
};
const getClientGuid = function getClientGuid(completion) {
if (currentGuid) {
completion(null, currentGuid);
return;
} // It's the first time that getClientGuid has been called
// in this page lifetime. Attempt to load any existing Guid
// from the storage
currentGuidStorage.get(completion);
};
/*
* Sets the methods for storing and retrieving client GUIDs persistently
* across sessions. By default, OpenTok.js attempts to use browser cookies to
* store GUIDs.
*
* Pass in an object that has a get()
method and
* a set()
method.
*
* The get()
method must take one parameter: the callback
* method to invoke. The callback method is passed two parameters —
* the first parameter is an error object or null if the call is successful;
* and the second parameter is the GUID (a string) if successful.
*
* The set()
method must include two parameters: the GUID to set
* (a string) and the callback method to invoke. The callback method is
* passed an error object on error, or it is passed no parameter if the call is
* successful.
*
* Here is an example: *
*
* var ComplexStorage = function() { * this.set = function(guid, completion) { * AwesomeBackendService.set(guid, function(response) { * completion(response.error || null); * }); * }; * this.get = function(completion) { * AwesomeBackendService.get(function(response, guid) { * completion(response.error || null, guid); * }); * }; * }; * * OT.overrideGuidStorage(new ComplexStorage()); **/ guidStorage.override = storageInterface => { if (isInvalidStorage(storageInterface)) { throw new Error('The storageInterface argument does not seem to be valid, ' + 'it must implement get and set methods'); } if (currentGuidStorage === storageInterface) { return; } currentGuidStorage = storageInterface; // If a client Guid has already been assigned to this client then // let the new storage know about it so that it's in sync. if (currentGuid) { currentGuidStorage.set(currentGuid, error => { if (error) { logging.error("Failed to send initial Guid value (" + currentGuid + ") to the newly assigned Guid Storage. The error was: " + error); // @todo error } }); } }; guidStorage.get = completion => { getClientGuid((error, guid) => { if (error) { completion(error); return; } if (!guid) { // Nothing came back, this client is entirely new. // generate a new Guid and persist it guid = uuid(); currentGuidStorage.set(guid, error => { if (error) { completion(error); return; } currentGuid = guid; }); } else if (!currentGuid) { currentGuid = guid; } completion(null, currentGuid); }); }; // Implement our default storage mechanism, which sets/gets a cookie // called 'opentok_client_id' guidStorage.override({ get(completion) { let clientId; try { clientId = (typeof window !== undefined ? window : global).localStorage.getItem('opentok_client_id'); } catch (err) { // will get Uncaught DOMException: Failed to read the 'localStorage' property from 'Window': // The document is sandboxed and lacks the 'allow-same-origin' flag. if (!clientId) { clientId = uuid(); } } completion(null, clientId); }, set(guid, completion) { try { (typeof window !== undefined ? window : global).localStorage.setItem('opentok_client_id', guid); } catch (err) {// will get Uncaught DOMException: Failed to read the 'localStorage' property from 'Window': // The document is sandboxed and lacks the 'allow-same-origin' flag. } completion(null); } }); // Test only guidStorage.set = guid => { currentGuid = guid; }; /***/ }), /* 246 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var _interopRequireDefault = __webpack_require__(2); var _createClass2 = _interopRequireDefault(__webpack_require__(40)); /* eslint-disable no-underscore-dangle */ const _get = __webpack_require__(51); const cloneDeep = __webpack_require__(50); /** * Provides an interface around the raw session info from Anvil * * @class SessionInfo * @param {Object} rawSessionInfo The raw session info from Anvil * */ let SessionInfo = /*#__PURE__*/function () { function SessionInfo(rawSessionInfo) { if (rawSessionInfo === void 0) { rawSessionInfo = {}; } Object.defineProperty(this, '_rawSessionInfo', { value: cloneDeep(rawSessionInfo) }); } /** * @type {String} * @readonly */ (0, _createClass2.default)(SessionInfo, [{ key: "sessionId", get: function get() { return this._rawSessionInfo.session_id; } /** * @type {String} * @readonly */ }, { key: "partnerId", get: function get() { return this._rawSessionInfo.partner_id; } /** * @type {String} * @readonly */ }, { key: "messagingServer", get: function get() { return this._rawSessionInfo.messaging_server_url; } /** * @type {String} * @readonly */ }, { key: "mediaServerName", get: function get() { return this._rawSessionInfo.media_server_hostname; } /** * @type {String} * @readonly */ }, { key: "messagingURL", get: function get() { return this._rawSessionInfo.messaging_url; } /** * @type {String} * @readonly */ }, { key: "symphonyAddress", get: function get() { return this._rawSessionInfo.symphony_address; } /** * @type {Array} * @readonly */ }, { key: "iceServers", get: function get() { return this._rawSessionInfo.ice_servers; } /** * Simulcast is tri-state: * * true - simulcast is on for this session * * false - simulcast is off for this session * * undefined - the developer can choose * @type {Boolean|Undefined} * @readonly */ }, { key: "simulcast", get: function get() { return _get(this._rawSessionInfo, 'properties.simulcast'); } /** * @type {Boolean} * @readonly */ }, { key: "reconnection", get: function get() { return _get(this._rawSessionInfo, 'properties.reconnection', false); } /** * @type {Boolean} * @readonly */ }, { key: "renegotiation", get: function get() { return _get(this._rawSessionInfo, 'properties.renegotiation', false); } /** * @type {Boolean} * @readonly */ }, { key: "p2pEnabled", get: function get() { return _get(this._rawSessionInfo, 'properties.p2p.preference.value') === 'enabled'; } /** * If this contains a valid codec, it will be prioritized * * @type {String} * @readonly */ }, { key: "priorityVideoCodec", get: function get() { return _get(this._rawSessionInfo, 'properties.priorityVideoCodec', ''); } /** * Is true if H264 codec is enabled * * @type {Boolean} * @readonly */ }, { key: "h264", get: function get() { return _get(this._rawSessionInfo, 'properties.h264', true); } /** * Is true if VP8 codec is enabled * * @type {Boolean} * @readonly */ }, { key: "vp8", get: function get() { return _get(this._rawSessionInfo, 'properties.vp8', true); } /** * Is true if VP9 codec is enabled * * @type {Boolean} * @readonly */ }, { key: "vp9", get: function get() { return _get(this._rawSessionInfo, 'properties.vp9', true); } /** * clientCandidates can be "relay". Other values are unknown. * * @type {String|Undefined} * @readonly */ }, { key: "clientCandidates", get: function get() { const rawValue = _get(this._rawSessionInfo, 'properties.clientCandidates'); return rawValue === 'relayed' ? 'relay' : rawValue; } }, { key: "peerRegeneration", get: function get() { return _get(this._rawSessionInfo, 'properties.peerRegeneration', true); } }]); return SessionInfo; }(); module.exports = SessionInfo; /***/ }), /* 247 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* eslint-env node */ // SDP helpers. var SDPUtils = {}; // Generate an alphanumeric identifier for cname or mids. // TODO: use UUIDs instead? https://gist.github.com/jed/982883 SDPUtils.generateIdentifier = function() { return Math.random().toString(36).substr(2, 10); }; // The RTCP CNAME used by all peerconnections from the same JS. SDPUtils.localCName = SDPUtils.generateIdentifier(); // Splits SDP into lines, dealing with both CRLF and LF. SDPUtils.splitLines = function(blob) { return blob.trim().split('\n').map(function(line) { return line.trim(); }); }; // Splits SDP into sessionpart and mediasections. Ensures CRLF. SDPUtils.splitSections = function(blob) { var parts = blob.split('\nm='); return parts.map(function(part, index) { return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; }); }; // returns the session description. SDPUtils.getDescription = function(blob) { var sections = SDPUtils.splitSections(blob); return sections && sections[0]; }; // returns the individual media sections. SDPUtils.getMediaSections = function(blob) { var sections = SDPUtils.splitSections(blob); sections.shift(); return sections; }; // Returns lines that start with a certain prefix. SDPUtils.matchPrefix = function(blob, prefix) { return SDPUtils.splitLines(blob).filter(function(line) { return line.indexOf(prefix) === 0; }); }; // Parses an ICE candidate line. Sample input: // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 // rport 55996" SDPUtils.parseCandidate = function(line) { var parts; // Parse both variants. if (line.indexOf('a=candidate:') === 0) { parts = line.substring(12).split(' '); } else { parts = line.substring(10).split(' '); } var candidate = { foundation: parts[0], component: parseInt(parts[1], 10), protocol: parts[2].toLowerCase(), priority: parseInt(parts[3], 10), ip: parts[4], address: parts[4], // address is an alias for ip. port: parseInt(parts[5], 10), // skip parts[6] == 'typ' type: parts[7] }; for (var i = 8; i < parts.length; i += 2) { switch (parts[i]) { case 'raddr': candidate.relatedAddress = parts[i + 1]; break; case 'rport': candidate.relatedPort = parseInt(parts[i + 1], 10); break; case 'tcptype': candidate.tcpType = parts[i + 1]; break; case 'ufrag': candidate.ufrag = parts[i + 1]; // for backward compability. candidate.usernameFragment = parts[i + 1]; break; default: // extension handling, in particular ufrag candidate[parts[i]] = parts[i + 1]; break; } } return candidate; }; // Translates a candidate object into SDP candidate attribute. SDPUtils.writeCandidate = function(candidate) { var sdp = []; sdp.push(candidate.foundation); sdp.push(candidate.component); sdp.push(candidate.protocol.toUpperCase()); sdp.push(candidate.priority); sdp.push(candidate.address || candidate.ip); sdp.push(candidate.port); var type = candidate.type; sdp.push('typ'); sdp.push(type); if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) { sdp.push('raddr'); sdp.push(candidate.relatedAddress); sdp.push('rport'); sdp.push(candidate.relatedPort); } if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') { sdp.push('tcptype'); sdp.push(candidate.tcpType); } if (candidate.usernameFragment || candidate.ufrag) { sdp.push('ufrag'); sdp.push(candidate.usernameFragment || candidate.ufrag); } return 'candidate:' + sdp.join(' '); }; // Parses an ice-options line, returns an array of option tags. // a=ice-options:foo bar SDPUtils.parseIceOptions = function(line) { return line.substr(14).split(' '); }; // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input: // a=rtpmap:111 opus/48000/2 SDPUtils.parseRtpMap = function(line) { var parts = line.substr(9).split(' '); var parsed = { payloadType: parseInt(parts.shift(), 10) // was: id }; parts = parts[0].split('/'); parsed.name = parts[0]; parsed.clockRate = parseInt(parts[1], 10); // was: clockrate parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; // legacy alias, got renamed back to channels in ORTC. parsed.numChannels = parsed.channels; return parsed; }; // Generate an a=rtpmap line from RTCRtpCodecCapability or // RTCRtpCodecParameters. SDPUtils.writeRtpMap = function(codec) { var pt = codec.payloadType; if (codec.preferredPayloadType !== undefined) { pt = codec.preferredPayloadType; } var channels = codec.channels || codec.numChannels || 1; return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n'; }; // Parses an a=extmap line (headerextension from RFC 5285). Sample input: // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset SDPUtils.parseExtmap = function(line) { var parts = line.substr(9).split(' '); return { id: parseInt(parts[0], 10), direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', uri: parts[1] }; }; // Generates a=extmap line from RTCRtpHeaderExtensionParameters or // RTCRtpHeaderExtension. SDPUtils.writeExtmap = function(headerExtension) { return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + ' ' + headerExtension.uri + '\r\n'; }; // Parses an ftmp line, returns dictionary. Sample input: // a=fmtp:96 vbr=on;cng=on // Also deals with vbr=on; cng=on SDPUtils.parseFmtp = function(line) { var parsed = {}; var kv; var parts = line.substr(line.indexOf(' ') + 1).split(';'); for (var j = 0; j < parts.length; j++) { kv = parts[j].trim().split('='); parsed[kv[0].trim()] = kv[1]; } return parsed; }; // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters. SDPUtils.writeFmtp = function(codec) { var line = ''; var pt = codec.payloadType; if (codec.preferredPayloadType !== undefined) { pt = codec.preferredPayloadType; } if (codec.parameters && Object.keys(codec.parameters).length) { var params = []; Object.keys(codec.parameters).forEach(function(param) { if (codec.parameters[param]) { params.push(param + '=' + codec.parameters[param]); } else { params.push(param); } }); line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n'; } return line; }; // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: // a=rtcp-fb:98 nack rpsi SDPUtils.parseRtcpFb = function(line) { var parts = line.substr(line.indexOf(' ') + 1).split(' '); return { type: parts.shift(), parameter: parts.join(' ') }; }; // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. SDPUtils.writeRtcpFb = function(codec) { var lines = ''; var pt = codec.payloadType; if (codec.preferredPayloadType !== undefined) { pt = codec.preferredPayloadType; } if (codec.rtcpFeedback && codec.rtcpFeedback.length) { // FIXME: special handling for trr-int? codec.rtcpFeedback.forEach(function(fb) { lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n'; }); } return lines; }; // Parses an RFC 5576 ssrc media attribute. Sample input: // a=ssrc:3735928559 cname:something SDPUtils.parseSsrcMedia = function(line) { var sp = line.indexOf(' '); var parts = { ssrc: parseInt(line.substr(7, sp - 7), 10) }; var colon = line.indexOf(':', sp); if (colon > -1) { parts.attribute = line.substr(sp + 1, colon - sp - 1); parts.value = line.substr(colon + 1); } else { parts.attribute = line.substr(sp + 1); } return parts; }; SDPUtils.parseSsrcGroup = function(line) { var parts = line.substr(13).split(' '); return { semantics: parts.shift(), ssrcs: parts.map(function(ssrc) { return parseInt(ssrc, 10); }) }; }; // Extracts the MID (RFC 5888) from a media section. // returns the MID or undefined if no mid line was found. SDPUtils.getMid = function(mediaSection) { var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; if (mid) { return mid.substr(6); } }; SDPUtils.parseFingerprint = function(line) { var parts = line.substr(14).split(' '); return { algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. value: parts[1] }; }; // Extracts DTLS parameters from SDP media section or sessionpart. // FIXME: for consistency with other functions this should only // get the fingerprint line as input. See also getIceParameters. SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) { var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:'); // Note: a=setup line is ignored since we use the 'auto' role. // Note2: 'algorithm' is not case sensitive except in Edge. return { role: 'auto', fingerprints: lines.map(SDPUtils.parseFingerprint) }; }; // Serializes DTLS parameters to SDP. SDPUtils.writeDtlsParameters = function(params, setupType) { var sdp = 'a=setup:' + setupType + '\r\n'; params.fingerprints.forEach(function(fp) { sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; }); return sdp; }; // Parses a=crypto lines into // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members SDPUtils.parseCryptoLine = function(line) { var parts = line.substr(9).split(' '); return { tag: parseInt(parts[0], 10), cryptoSuite: parts[1], keyParams: parts[2], sessionParams: parts.slice(3), }; }; SDPUtils.writeCryptoLine = function(parameters) { return 'a=crypto:' + parameters.tag + ' ' + parameters.cryptoSuite + ' ' + (typeof parameters.keyParams === 'object' ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) : parameters.keyParams) + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + '\r\n'; }; // Parses the crypto key parameters into // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam* SDPUtils.parseCryptoKeyParams = function(keyParams) { if (keyParams.indexOf('inline:') !== 0) { return null; } var parts = keyParams.substr(7).split('|'); return { keyMethod: 'inline', keySalt: parts[0], lifeTime: parts[1], mkiValue: parts[2] ? parts[2].split(':')[0] : undefined, mkiLength: parts[2] ? parts[2].split(':')[1] : undefined, }; }; SDPUtils.writeCryptoKeyParams = function(keyParams) { return keyParams.keyMethod + ':' + keyParams.keySalt + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + (keyParams.mkiValue && keyParams.mkiLength ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength : ''); }; // Extracts all SDES paramters. SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) { var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:'); return lines.map(SDPUtils.parseCryptoLine); }; // Parses ICE information from SDP media section or sessionpart. // FIXME: for consistency with other functions this should only // get the ice-ufrag and ice-pwd lines as input. SDPUtils.getIceParameters = function(mediaSection, sessionpart) { var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0]; var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0]; if (!(ufrag && pwd)) { return null; } return { usernameFragment: ufrag.substr(12), password: pwd.substr(10), }; }; // Serializes ICE parameters to SDP. SDPUtils.writeIceParameters = function(params) { return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n'; }; // Parses the SDP media section and returns RTCRtpParameters. SDPUtils.parseRtpParameters = function(mediaSection) { var description = { codecs: [], headerExtensions: [], fecMechanisms: [], rtcp: [] }; var lines = SDPUtils.splitLines(mediaSection); var mline = lines[0].split(' '); for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..] var pt = mline[i]; var rtpmapline = SDPUtils.matchPrefix( mediaSection, 'a=rtpmap:' + pt + ' ')[0]; if (rtpmapline) { var codec = SDPUtils.parseRtpMap(rtpmapline); var fmtps = SDPUtils.matchPrefix( mediaSection, 'a=fmtp:' + pt + ' '); // Only the first a=fmtp:
RTCPeerConnection.getStats
based audio level sampler.
*
* It uses the getStats
method to get the audioOutputLevel
.
* This implementation expects the single parameter version of the getStats
method.
*
* Currently the audioOutputLevel
stats is only supported in Chrome.
*
* @param {function} getStatsFn the "getStats" function
* @constructor
*/
module.exports = (_temp = /*#__PURE__*/function () {
function GetStatsAudioLevelSampler(getStats, _temp2) {
var _context;
let _ref = _temp2 === void 0 ? {} : _temp2,
_ref$requestAnimation = _ref.requestAnimationFrame,
requestAnimationFrame = _ref$requestAnimation === void 0 ? (_context = window).requestAnimationFrame.bind(_context) : _ref$requestAnimation;
(0, _defineProperty2.default)(this, "_lastCalled", now());
(0, _defineProperty2.default)(this, "_audioLevel", undefined);
(0, _defineProperty2.default)(this, "_running", false);
this._requestAnimationFrame = requestAnimationFrame;
this._getStats = getStats;
this._startLoop();
}
var _proto = GetStatsAudioLevelSampler.prototype;
_proto._startLoop = function _startLoop() {
if (this._running) {
return;
}
this._running = true;
this._requestAnimationFrame(this._loop.bind(this));
};
_proto._loop = function _loop() {
this._getStats((error, stats) => {
// @todo we should measure how long the getStats so that we can determine
// our impact on the render loop, and skip frames if need to reduce our
// CPU utilization
// Stat entries without audio can have audioLevel 0 so we just look for the max level
let maxLevel = null;
if (!error) {
for (let idx = 0; idx < stats.length; idx += 1) {
const stat = stats[idx];
let level = null;
if (stat.audioOutputLevel !== undefined) {
// the max value delivered by getStats via audioOutputLevel is 2^15
const audioOutputLevel = parseFloat(stat.audioOutputLevel) / 32768;
if (!isNaN(audioOutputLevel)) {
level = audioOutputLevel;
}
} else if (stat.audioLevel !== undefined) {
level = stat.audioLevel;
}
if (level !== null && (maxLevel === null || level > maxLevel)) {
maxLevel = level;
}
}
}
this._audioLevel = maxLevel;
if (this._running && now() - this._lastCalled < 10000) {
this._requestAnimationFrame(this._loop.bind(this));
} else {
this._running = false;
}
});
};
_proto.destroy = function destroy() {
this._running = false;
};
_proto.sample = function sample(done) {
if (typeof done === 'function') {
throw new Error('sample no longer takes a callback');
}
this._startLoop();
this._lastCalled = now();
return this._audioLevel;
};
return GetStatsAudioLevelSampler;
}(), _temp);
/***/ }),
/* 250 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const merge = __webpack_require__(143);
const normalizeConstraintInput = __webpack_require__(548);
const isScreenSharingSource = __webpack_require__(258);
function createSourceConstraint(media, enableRenegotiation, usingOptionalMandatoryStyle) {
if (!media.publish && enableRenegotiation) {
// FIXME: I'm still not quite happy with how this works. With renegotiation, when publish
// changes to true we shouldn't have to recalculate constraints. I think Publisher should handle
// this override instead and this component shouldn't know anything about renegotiation.
// Related: OPENTOK-31082
return false;
}
if (typeof media.source === 'boolean') {
return media.source;
}
if (usingOptionalMandatoryStyle) {
return {
mandatory: {
sourceId: media.source
}
};
} // getDisplayMedia doesn't support exact constraints
if (isScreenSharingSource(media.source)) {
return {
deviceId: media.source
};
}
return {
deviceId: {
exact: media.source
}
};
}
function generateAudioConstraints(opt) {
let constraints = createSourceConstraint(opt.audio, opt.enableRenegotiation, opt.usingOptionalMandatoryStyle);
if (constraints === false) {
return false;
}
const usingOptionalMandatoryStyle = opt.usingOptionalMandatoryStyle,
enableStereo = opt.enableStereo,
disableAudioProcessing = opt.disableAudioProcessing,
prefixedAudioConstraints = opt.prefixedAudioConstraints;
if ((enableStereo || disableAudioProcessing || prefixedAudioConstraints) && constraints === true) {
constraints = {};
}
if (enableStereo) {
// Chrome<61 uses optional/mandatory style because a boolean is equivalent to an ideal value
// See https://bugs.chromium.org/p/chromium/issues/detail?id=700223#c3
if (usingOptionalMandatoryStyle) {
merge(constraints, {
optional: []
});
constraints.optional.push({
channelCount: 2
});
} else {
constraints.channelCount = 2;
}
}
if (disableAudioProcessing) {
// Chrome<61 uses optional/mandatory style because a boolean is equivalent to an ideal value
// See https://bugs.chromium.org/p/chromium/issues/detail?id=700223#c3
if (usingOptionalMandatoryStyle) {
merge(constraints, {
optional: []
});
constraints.optional.push({
echoCancellation: false
});
} else {
constraints.echoCancellation = false;
}
}
if (prefixedAudioConstraints) {
if (usingOptionalMandatoryStyle) {
merge(constraints, {
optional: []
});
Object.keys(prefixedAudioConstraints).forEach(key => {
constraints.optional.push({
[key]: prefixedAudioConstraints[key]
});
});
} else {
merge(constraints, prefixedAudioConstraints);
}
}
return constraints;
}
function generateVideoConstraints(opt) {
let constraints = createSourceConstraint(opt.video, opt.enableRenegotiation, opt.usingOptionalMandatoryStyle);
if (constraints === false) {
return false;
}
const videoDimensions = opt.videoDimensions,
frameRate = opt.frameRate,
maxResolution = opt.maxResolution,
facingMode = opt.facingMode,
usingOptionalMandatoryStyle = opt.usingOptionalMandatoryStyle;
if ((videoDimensions || frameRate || maxResolution || facingMode) && constraints === true) {
constraints = {};
}
if (videoDimensions) {
const width = videoDimensions.width;
const height = videoDimensions.height;
if (usingOptionalMandatoryStyle) {
merge(constraints, {
optional: []
});
constraints.optional.push({
minWidth: width
}, {
maxWidth: width
}, {
minHeight: height
}, {
maxHeight: height
});
} else {
merge(constraints, {
width: {
ideal: width
},
height: {
ideal: height
}
});
}
}
if (frameRate) {
if (usingOptionalMandatoryStyle) {
merge(constraints, {
optional: []
});
constraints.optional.push({
minFrameRate: frameRate
}, {
maxFrameRate: frameRate
});
} else {
merge(constraints, {
frameRate: {
ideal: frameRate
}
});
}
}
if (maxResolution) {
if (usingOptionalMandatoryStyle) {
merge(constraints, {
mandatory: {
maxWidth: maxResolution.width,
maxHeight: maxResolution.height
}
});
} else {
merge(constraints, {
width: {
max: maxResolution.width
},
height: {
max: maxResolution.height
}
});
}
}
if (facingMode) {
if (usingOptionalMandatoryStyle) {
merge(constraints, {
optional: []
});
constraints.optional.push({
facingMode
});
} else {
merge(constraints, {
facingMode: {
ideal: facingMode
}
});
}
}
return constraints;
}
function generateConstraints(opt) {
return {
audio: generateAudioConstraints(opt),
video: generateVideoConstraints(opt)
};
}
module.exports = function generateConstraintInfo(opt) {
const normOpt = normalizeConstraintInput(opt);
const constraints = generateConstraints(normOpt);
return {
constraints,
publishAudio: normOpt.audio.publish,
publishVideo: normOpt.video.publish,
frameRate: normOpt.frameRate,
videoDimensions: normOpt.videoDimensions,
audioDeviceId: typeof normOpt.audio.source === 'string' ? normOpt.audio.source : undefined,
videoDeviceId: typeof normOpt.video.source === 'string' ? normOpt.video.source : undefined
};
};
/***/ }),
/* 251 */
/***/ (function(module, exports, __webpack_require__) {
var baseAssignValue = __webpack_require__(60),
eq = __webpack_require__(48);
/**
* This function is like `assignValue` except that it doesn't assign
* `undefined` values.
*
* @private
* @param {Object} object The object to modify.
* @param {string} key The key of the property to assign.
* @param {*} value The value to assign.
*/
function assignMergeValue(object, key, value) {
if ((value !== undefined && !eq(object[key], value)) ||
(value === undefined && !(key in object))) {
baseAssignValue(object, key, value);
}
}
module.exports = assignMergeValue;
/***/ }),
/* 252 */
/***/ (function(module, exports) {
/**
* Gets the value at `key`, unless `key` is "__proto__" or "constructor".
*
* @private
* @param {Object} object The object to query.
* @param {string} key The key of the property to get.
* @returns {*} Returns the property value.
*/
function safeGet(object, key) {
if (key === 'constructor' && typeof object[key] === 'function') {
return;
}
if (key == '__proto__') {
return;
}
return object[key];
}
module.exports = safeGet;
/***/ }),
/* 253 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const _require = __webpack_require__(3),
name = _require.name,
version = _require.version,
isChromiumEdge = _require.isChromiumEdge;
module.exports = isChromiumEdge || name === 'Chrome' && version >= 72 || name === 'Firefox' && version >= 66 || name === 'Opera' && version >= 60;
/***/ }),
/* 254 */
/***/ (function(module, exports, __webpack_require__) {
var toString = __webpack_require__(92),
upperFirst = __webpack_require__(553);
/**
* Converts the first character of `string` to upper case and the remaining
* to lower case.
*
* @static
* @memberOf _
* @since 3.0.0
* @category String
* @param {string} [string=''] The string to capitalize.
* @returns {string} Returns the capitalized string.
* @example
*
* _.capitalize('FRED');
* // => 'Fred'
*/
function capitalize(string) {
return upperFirst(toString(string).toLowerCase());
}
module.exports = capitalize;
/***/ }),
/* 255 */
/***/ (function(module, exports) {
/**
* The base implementation of `_.slice` without an iteratee call guard.
*
* @private
* @param {Array} array The array to slice.
* @param {number} [start=0] The start position.
* @param {number} [end=array.length] The end position.
* @returns {Array} Returns the slice of `array`.
*/
function baseSlice(array, start, end) {
var index = -1,
length = array.length;
if (start < 0) {
start = -start > length ? 0 : (length + start);
}
end = end > length ? length : end;
if (end < 0) {
end += length;
}
length = start > end ? 0 : ((end - start) >>> 0);
start >>>= 0;
var result = Array(length);
while (++index < length) {
result[index] = array[index + start];
}
return result;
}
module.exports = baseSlice;
/***/ }),
/* 256 */
/***/ (function(module, exports) {
/** Used to compose unicode character classes. */
var rsAstralRange = '\\ud800-\\udfff',
rsComboMarksRange = '\\u0300-\\u036f',
reComboHalfMarksRange = '\\ufe20-\\ufe2f',
rsComboSymbolsRange = '\\u20d0-\\u20ff',
rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
rsVarRange = '\\ufe0e\\ufe0f';
/** Used to compose unicode capture groups. */
var rsZWJ = '\\u200d';
/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
/**
* Checks if `string` contains Unicode symbols.
*
* @private
* @param {string} string The string to inspect.
* @returns {boolean} Returns `true` if a symbol is found, else `false`.
*/
function hasUnicode(string) {
return reHasUnicode.test(string);
}
module.exports = hasUnicode;
/***/ }),
/* 257 */
/***/ (function(module, exports, __webpack_require__) {
var asciiToArray = __webpack_require__(556),
hasUnicode = __webpack_require__(256),
unicodeToArray = __webpack_require__(557);
/**
* Converts `string` to an array.
*
* @private
* @param {string} string The string to convert.
* @returns {Array} Returns the converted array.
*/
function stringToArray(string) {
return hasUnicode(string)
? unicodeToArray(string)
: asciiToArray(string);
}
module.exports = stringToArray;
/***/ }),
/* 258 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const screenSharingSources = __webpack_require__(558);
module.exports = videoSource => screenSharingSources.indexOf(videoSource) > -1;
/***/ }),
/* 259 */
/***/ (function(module, exports, __webpack_require__) {
var baseForOwn = __webpack_require__(93),
createBaseEach = __webpack_require__(563);
/**
* The base implementation of `_.forEach` without support for iteratee shorthands.
*
* @private
* @param {Array|Object} collection The collection to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array|Object} Returns `collection`.
*/
var baseEach = createBaseEach(baseForOwn);
module.exports = baseEach;
/***/ }),
/* 260 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = 15000;
/***/ }),
/* 261 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/**
* castToBoolean
*
* @param {any} The value to cast
* @returns {Boolean} True if value === 'true' or value === true, otherwise false.
*/
module.exports = function (value, defaultValue) {
if (defaultValue === void 0) {
defaultValue = false;
}
return value === undefined ? defaultValue : value === 'true' || value === true;
};
/***/ }),
/* 262 */
/***/ (function(module, exports) {
function _setPrototypeOf(o, p) {
module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
module.exports = _setPrototypeOf;
/***/ }),
/* 263 */
/***/ (function(module, exports, __webpack_require__) {
var arrayMap = __webpack_require__(30),
baseClone = __webpack_require__(74),
baseUnset = __webpack_require__(578),
castPath = __webpack_require__(52),
copyObject = __webpack_require__(38),
customOmitClone = __webpack_require__(580),
flatRest = __webpack_require__(126),
getAllKeysIn = __webpack_require__(179);
/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
CLONE_FLAT_FLAG = 2,
CLONE_SYMBOLS_FLAG = 4;
/**
* The opposite of `_.pick`; this method creates an object composed of the
* own and inherited enumerable property paths of `object` that are not omitted.
*
* **Note:** This method is considerably slower than `_.pick`.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param {Object} object The source object.
* @param {...(string|string[])} [paths] The property paths to omit.
* @returns {Object} Returns the new object.
* @example
*
* var object = { 'a': 1, 'b': '2', 'c': 3 };
*
* _.omit(object, ['a', 'c']);
* // => { 'b': '2' }
*/
var omit = flatRest(function(object, paths) {
var result = {};
if (object == null) {
return result;
}
var isDeep = false;
paths = arrayMap(paths, function(path) {
path = castPath(path, object);
isDeep || (isDeep = path.length > 1);
return path;
});
copyObject(object, getAllKeysIn(object), result);
if (isDeep) {
result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);
}
var length = paths.length;
while (length--) {
baseUnset(result, paths[length]);
}
return result;
});
module.exports = omit;
/***/ }),
/* 264 */
/***/ (function(module, exports) {
/**
* Gets the last element of `array`.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Array
* @param {Array} array The array to query.
* @returns {*} Returns the last element of `array`.
* @example
*
* _.last([1, 2, 3]);
* // => 3
*/
function last(array) {
var length = array == null ? 0 : array.length;
return length ? array[length - 1] : undefined;
}
module.exports = last;
/***/ }),
/* 265 */
/***/ (function(module, exports, __webpack_require__) {
var baseFindIndex = __webpack_require__(205),
baseIteratee = __webpack_require__(29),
toInteger = __webpack_require__(87);
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
/**
* This method is like `_.find` except that it returns the index of the first
* element `predicate` returns truthy for instead of the element itself.
*
* @static
* @memberOf _
* @since 1.1.0
* @category Array
* @param {Array} array The array to inspect.
* @param {Function} [predicate=_.identity] The function invoked per iteration.
* @param {number} [fromIndex=0] The index to search from.
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
* var users = [
* { 'user': 'barney', 'active': false },
* { 'user': 'fred', 'active': false },
* { 'user': 'pebbles', 'active': true }
* ];
*
* _.findIndex(users, function(o) { return o.user == 'barney'; });
* // => 0
*
* // The `_.matches` iteratee shorthand.
* _.findIndex(users, { 'user': 'fred', 'active': false });
* // => 1
*
* // The `_.matchesProperty` iteratee shorthand.
* _.findIndex(users, ['active', false]);
* // => 0
*
* // The `_.property` iteratee shorthand.
* _.findIndex(users, 'active');
* // => 2
*/
function findIndex(array, predicate, fromIndex) {
var length = array == null ? 0 : array.length;
if (!length) {
return -1;
}
var index = fromIndex == null ? 0 : toInteger(fromIndex);
if (index < 0) {
index = nativeMax(length + index, 0);
}
return baseFindIndex(array, baseIteratee(predicate, 3), index);
}
module.exports = findIndex;
/***/ }),
/* 266 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const env = __webpack_require__(3);
const toArray = __webpack_require__(588);
/**
* @returns {function(RTCPeerConnection, function(DOMError, Arraysubscribe()
method of the
* {@link Session} object returns a Subscriber object.
*
* @property {Element} element The HTML DOM element containing the Subscriber.
* @property {String} id The DOM ID of the Subscriber.
* @property {Stream} stream The stream to which you are subscribing.
*
* @class Subscriber
* @augments EventDispatcher
*/
const Subscriber = function Subscriber(targetElement, options, completionHandler) {
var _this = this;
if (options === void 0) {
options = {};
}
if (completionHandler === void 0) {
completionHandler = () => {};
}
if (options.analytics === undefined) {
// @todo does anyone instantiate a Subscriber outside of `session.subscribe`?
// it might be best to instantiate an analytics object and replace it on subscribe like
// we do in the publisher.
throw new Error('Subscriber requires an instance of analytics');
}
/** @type AnalyticsHelperDefault */
const analytics = options.analytics;
const _widgetId = uuid();
const _audioLevelCapable = Subscriber.hasAudioOutputLevelStatCapability() || hasRemoteStreamsWithWebAudio();
const _subscriber = this;
const _pcConnected = {};
const peerConnections = {};
/** @type {defaultWidgetView|null} */
let _widgetView;
let _chrome;
let _muteDisplayMode;
let _audioLevelMeter;
let _subscribeStartTime;
let _state;
let _audioLevelSampler;
let destroyAudioLevelBehaviour;
let _webRTCStream;
let _lastSubscribeToVideoReason;
let _attemptStartTime;
/** @type IntervalRunnerDefault | undefined */
let _streamEventHandlers;
let _isSubscribingToAudio;
let _loaded = false;
let _domId = targetElement || _widgetId;
let _session = options.session;
let _stream = options.stream;
let _audioVolume;
let _latestPositiveVolume;
let _frameRateRestricted = false;
let _frameRateWatcher;
let _lastIceConnectionState = eventNames.SUBSCRIBER_DISCONNECTED;
let _preDisconnectStats = {};
let _congestionLevel = null;
let _hasLoadedAtLeastOnce = false;
let _isVideoSupported = true;
let fallbackIceServers = [];
let _properties = defaults({}, options, {
showControls: true,
testNetwork: false,
fitMode: _stream.defaultFitMode || 'cover',
insertDefaultUI: true
});
let _currentPeerConnectionEvents;
const socket = _session._.getSocket();
const getAllPeerConnections = () => values(peerConnections);
const getPeerConnectionByPriority = priority => peerConnections[priority];
const getMaxPriority = () => last(Object.keys(peerConnections).map(Number).sort());
const getPriorityPeerConnection = () => getPeerConnectionByPriority(getMaxPriority());
const removePeerConnectionByPromise = futurePc => {
Object.keys(peerConnections).forEach(key => {
if (peerConnections[key] === futurePc) {
delete peerConnections[key];
}
});
};
const logAnalyticsEvent = (action, variation, payload, options, throttle) => {
let stats = assign({
action,
variation,
payload,
streamId: _stream ? _stream.id : null,
sessionId: _session ? _session.sessionId : null,
connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null,
partnerId: _session && _session.sessionInfo ? _session.sessionInfo.partnerId : null,
subscriberId: _widgetId,
widgetType: 'Subscriber'
}, options);
if (variation === 'Failure' || variation === 'iceconnectionstatechange' && payload === 'closed') {
stats = assign(stats, _preDisconnectStats);
}
const args = [stats];
if (throttle) {
args.push(throttle);
}
analytics.logEvent(...args);
};
const connectivityState = createConnectivityState({
onInvalidTransition(transition, from) {
const err = "Invalid state transition: Event '" + transition + "' not possible in state '" + from + "'";
logging.error(err);
logAnalyticsEvent('Subscriber:InvalidStateTransition', 'Event', {
details: err
});
}
});
{
const errMessage = 'Unable to subscribe to stream in a reasonable amount of time'; // make sure we trigger an error if we are not getting any "data" after a reasonable
// amount of time
overallTimeout({
connectivityState,
onWarning() {
logConnectivityEvent('Warning', {});
},
onTimeout: () => {
if (_widgetView) {
_widgetView.addError(errMessage);
}
if (_state.isAttemptingToSubscribe()) {
_state.set('Failed');
this._disconnect({
noStateTransition: true
});
const error = otError(Errors.TIMEOUT, new Error(errMessage), ExceptionCodes.UNABLE_TO_SUBSCRIBE);
connectivityState.fail({
options: {
failureReason: 'Subscribe',
failureMessage: errMessage,
failureCode: ExceptionCodes.UNABLE_TO_SUBSCRIBE
},
error
});
}
},
warningMs: BIND_VIDEO_DELAY_WARNING,
timeoutMs: BIND_VIDEO_DELAY_MAX
});
}
_pcConnected.promise = new Promise((resolve, reject) => {
_pcConnected.resolve = resolve;
_pcConnected.reject = reject;
});
if (_properties.testNetwork && _session.sessionInfo.p2pEnabled) {
logging.warn('You cannot test your network with a relayed session. Use a routed session.');
}
this.id = _domId;
this.widgetId = _widgetId;
this.session = _session;
this.stream = _properties.stream;
_stream = _properties.stream;
this.streamId = _stream.id;
if (!_session) {
OTErrorClass.handleJsException({
errorMsg: 'OT.Subscriber must be passed a session option',
code: 2000,
target: this,
analytics
});
return null;
}
eventing(this);
_subscriber.once('subscribeComplete', function () {
try {
completionHandler(...arguments);
} catch (err) {
console.error('Completion handler threw an exception', err);
}
});
const getVariationFromState = (_ref3) => {
let transition = _ref3.transition,
from = _ref3.from;
if (transition === 'fail') {
if (from === 'connected') {
return 'Disconnect';
}
return 'Failure';
}
if (transition === 'disconnect' && from === 'connected') {
return 'Disconnect';
}
return 'Cancel';
};
connectivityState.observe({
onEnterConnecting() {
logConnectivityEvent('Attempt', null, {});
},
onEnterConnected() {
logConnectivityEvent('Success', null, {});
},
onEnterDisconnected(state, _temp2) {
let _ref4 = _temp2 === void 0 ? {} : _temp2,
_ref4$options = _ref4.options,
options = _ref4$options === void 0 ? {} : _ref4$options,
_ref4$payload = _ref4.payload,
payload = _ref4$payload === void 0 ? null : _ref4$payload;
logConnectivityEvent(getVariationFromState(state), payload, options);
}
});
connectivityState.observe({
onEnterConnected: () => {
this.trigger('subscribeComplete', undefined, this);
},
onEnterDisconnected: function onEnterDisconnected(_ref5, _temp3) {
let from = _ref5.from;
let _ref6 = _temp3 === void 0 ? {} : _temp3,
error = _ref6.error;
if (from === 'connecting') {
_this.trigger('subscribeComplete', error || new Error('An unknown error occurred'), _this);
}
}
});
createConnectivityAttemptPinger({
connectivityState,
logAttempt() {
logAnalyticsEvent('Subscribe', 'Attempting', null, {
connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null
});
}
});
const logConnectivityEvent = (variation, payload, options) => {
if (variation === 'Attempt') {
_attemptStartTime = new Date().getTime();
}
if (variation === 'Failure' || variation === 'Success' || variation === 'Cancel') {
logAnalyticsEvent('Subscribe', variation, payload, (0, _extends2.default)({}, options, {
attemptDuration: new Date().getTime() - _attemptStartTime
}));
} else {
logAnalyticsEvent('Subscribe', variation, payload, options);
}
};
const logResubscribe = (variation, payload) => {
logAnalyticsEvent('ICERestart', variation, payload);
};
const recordQOS = (_ref7) => {
let parsedStats = _ref7.parsedStats,
remoteConnectionId = _ref7.remoteConnectionId,
peerPriority = _ref7.peerPriority,
peerId = _ref7.peerId;
const QoSBlob = {
widgetType: 'Subscriber',
width: _widgetView.width,
height: _widgetView.height,
audioTrack: _webRTCStream && _webRTCStream.getAudioTracks().length > 0,
hasAudio: _stream && _stream.hasAudio,
subscribeToAudio: _isSubscribingToAudio,
audioVolume: this.getAudioVolume(),
videoTrack: _webRTCStream && _webRTCStream.getVideoTracks().length > 0,
connectionId: _session ? _session.connection.connectionId : null,
hasVideo: _stream && _stream.hasVideo,
subscribeToVideo: _properties.subscribeToVideo,
congestionLevel: _congestionLevel,
streamId: _stream.id,
subscriberId: _widgetId,
duration: Math.round((now() - _subscribeStartTime) / 1000),
remoteConnectionId,
peerPriority,
peerId
};
const combinedStats = assign(QoSBlob, parsedStats);
analytics.logQOS(combinedStats);
this.trigger('qos', analytics.combineWithCommon(combinedStats));
};
const stateChangeFailed = changeFailed => {
logging.error('OT.Subscriber State Change Failed: ', changeFailed.message);
logging.debug(changeFailed);
};
const onLoaded = () => {
if (_state.isSubscribing() || !_widgetView || !_widgetView.video()) {
return;
}
_loaded = true;
if (!_hasLoadedAtLeastOnce) {
connectivityState.connect();
_hasLoadedAtLeastOnce = true;
}
logging.debug('OT.Subscriber.onLoaded');
_state.set('Subscribing');
_subscribeStartTime = now();
_widgetView.loading(false);
if (_chrome) {
_chrome.showAfterLoading();
}
if (_frameRateRestricted) {
_stream.setRestrictFrameRate(true);
}
if (_audioLevelMeter) {
_audioLevelMeter.audioOnly(_widgetView.audioOnly());
}
this.setAudioVolume(_audioVolume);
this.trigger('loaded', this);
};
const onPeerDisconnected = () => {
logging.debug('OT.Subscriber has been disconnected from the Publisher\'s PeerConnection');
connectivityState.fail({
options: {
failureReason: 'PeerConnectionError',
failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,
failureMessage: 'PeerConnection disconnected'
},
error: otError(errors.DISCONNECTED, new Error('ClientDisconnected'))
});
this._disconnect({
noStateTransition: true
});
if (_state.isAttemptingToSubscribe() || _state.isSubscribing()) {
_state.set('Failed');
}
};
const onVideoError = plainErr => {
const err = otError(errors.MEDIA_ERR_DECODE, plainErr, plainErr.code || ExceptionCodes.P2P_CONNECTION_FAILED);
err.message = "OT.Subscriber while playing stream: " + err.message;
logging.error('OT.Subscriber.onVideoError');
connectivityState.fail({
options: {
failureReason: 'VideoElement',
failureMessage: err.message,
failureCode: err.code || ExceptionCodes.P2P_CONNECTION_FAILED
},
error: err
});
const isAttemptingToSubscribe = _state.isAttemptingToSubscribe();
_state.set('Failed');
if (!isAttemptingToSubscribe) {
// FIXME: This emits a string instead of an error here for backwards compatibility despite
// being undocumented. When possible we should remove access to this and other undocumented
// events, and restore emitting actual errors here.
_subscriber.trigger('error', err.message);
}
OTErrorClass.handleJsException({
error: err,
code: ExceptionCodes.UNABLE_TO_SUBSCRIBE,
// @todo why do we override the code?
target: _subscriber,
analytics
});
};
const onSubscriberCreateError = rawError => {
// @todo v3 These errors used to come from the peer connection hence the mention
// of peer connection in the logs/ errors. However they actually have nothing to do
// with the peer connection. I have chosen to keep the messages / errors the same for
// now, but we should consider changing them
const err = interpretSubscriberCreateError(rawError);
OTErrorClass.handleJsException({
error: err,
code: err.code,
target: _subscriber,
analytics
});
this._disconnect({
noStateTransition: true
});
const options = {
failureReason: 'Subscribe',
failureMessage: "OT.Subscriber PeerConnection Error: " + err.message,
failureCode: ExceptionCodes.P2P_CONNECTION_FAILED
};
logAnalyticsEvent('createPeerConnection', 'Failure', {}, options);
_showError(err.code);
connectivityState.fail({
options,
error: otError(err.name, new Error('Subscribe: Subscriber PeerConnection with connection (not found) ' + ("failed: " + err.message)), err.code)
});
if (err.name === Errors.STREAM_NOT_FOUND) {
// Usually, the subscriber should be kept in an error state when the peer connection fails
// because it still exists in the session. But when we get a 404 that means it doesn't
// exist, even if the session still thinks it does.
this._destroy({
reason: 'streamNotFound',
noStateTransition: true
});
}
};
const onPeerConnectionFailure = (code, reason, peerConnection, prefix) => {
if (prefix === 'SetRemoteDescription' && !_isVideoSupported && reason.match(/Unsupported video without audio for fallback/)) {
if (_widgetView) {
_widgetView.addError('The stream is unable to be played.', 'Your browser does not support the video format.');
}
let err;
if (OTHelpers.env.name === 'Safari') {
err = new Error('VP8 is not supported in this version of Safari. You might want to consider switching to ' + 'an H264 project. See https://tokbox.com/safari for more information.');
} else {
err = new Error('Video format not supported in this browser.');
}
err.code = ExceptionCodes.UNSUPPORTED_VIDEO_CODEC;
onVideoError(err);
return;
}
if (prefix === 'ICEWorkflow' && _session.sessionInfo.reconnection && _loaded) {
logging.debug('Ignoring peer connection failure due to possibility of reconnection.');
return;
}
let errorCode;
if (prefix === 'ICEWorkflow') {
errorCode = ExceptionCodes.SUBSCRIBER_ICE_WORKFLOW_FAILED;
} else if (code === ExceptionCodes.STREAM_LIMIT_EXCEEDED) {
errorCode = code;
} else {
errorCode = ExceptionCodes.P2P_CONNECTION_FAILED;
}
const options = {
failureReason: prefix || 'PeerConnectionError',
failureMessage: "OT.Subscriber PeerConnection Error: " + reason,
failureCode: errorCode
};
const error = interpretPeerConnectionError(code, reason, prefix, '(not found)', 'Subscriber');
connectivityState.fail({
options,
error
});
if (_state.isAttemptingToSubscribe()) {
// We weren't subscribing yet so this was a failure in setting
// up the PeerConnection or receiving the initial stream.
const payload = {
hasRelayCandidates: peerConnection && peerConnection.hasRelayCandidates()
};
logAnalyticsEvent('createPeerConnection', 'Failure', payload, options);
_state.set('Failed');
} else if (_state.isSubscribing()) {
// we were disconnected after we were already subscribing
_state.set('Failed');
this.trigger('error', reason);
}
this._disconnect({
noStateTransition: true
});
if (Number(code) === 404) {
// Usually, the subscriber should be kept in an error state when the peer connection fails
// because it still exists in the session. But when we get a 404 that means it doesn't
// exist, even if the session still thinks it does.
this._destroy({
noStateTransition: true
});
}
OTErrorClass.handleJsException({
errorMsg: "OT.Subscriber PeerConnection Error: " + reason,
errorCode,
target: this,
analytics
});
_showError(code);
};
const onRemoteStreamAdded = /*#__PURE__*/function () {
var _ref8 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(webRTCStream) {
var videoContainerOptions, currentPc, video, videoIndicator, videoElementCreated, peerConnection;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_webRTCStream = webRTCStream;
logging.debug('OT.Subscriber.onRemoteStreamAdded');
_context2.prev = 2;
_context2.next = 5;
return waitUntil(() => hasExpectedTracks(_webRTCStream, _stream));
case 5:
_context2.next = 14;
break;
case 7:
_context2.prev = 7;
_context2.t0 = _context2["catch"](2);
if (!(_context2.t0.message === 'TIMEOUT')) {
_context2.next = 13;
break;
}
logging.error('The expected tracks never arrived');
_context2.next = 14;
break;
case 13:
throw _context2.t0;
case 14:
_state.set('BindingRemoteStream'); // Disable the audio/video, if needed
_this.subscribeToAudio(_isSubscribingToAudio, false);
_lastSubscribeToVideoReason = 'loading';
_this.subscribeToVideo(_properties.subscribeToVideo, 'loading'); // setting resolution and frame rate doesn't work in P2P
if (!_session.sessionInfo.p2pEnabled) {
_this.setPreferredResolution(_properties.preferredResolution);
_this.setPreferredFrameRate(_properties.preferredFrameRate);
}
videoContainerOptions = {
error: onVideoError,
audioVolume: _audioVolume
}; // This is a workaround for a bug in Chrome where a track disabled on
// the remote end doesn't fire loadedmetadata causing the subscriber to timeout
// https://jira.tokbox.com/browse/OPENTOK-15605
// Also https://jira.tokbox.com/browse/OPENTOK-16425
// Also https://tokbox.atlassian.net/browse/OPENTOK-27112
webRTCStream.getVideoTracks().forEach(track => {
if ((typeof window !== undefined ? window : global).webkitMediaStream) {
track.enabled = false;
} else {
track.enabled = _stream.hasVideo && _properties.subscribeToVideo;
}
});
_context2.prev = 21;
_context2.next = 24;
return _widgetView.bindVideo(webRTCStream, videoContainerOptions);
case 24:
_context2.next = 32;
break;
case 26:
_context2.prev = 26;
_context2.t1 = _context2["catch"](21);
if (!(_context2.t1 instanceof CancellationError)) {
_context2.next = 30;
break;
}
return _context2.abrupt("return");
case 30:
onVideoError(_context2.t1);
throw _context2.t1;
case 32:
_context2.prev = 32;
_widgetView.loading(false);
return _context2.finish(32);
case 35:
currentPc = getPriorityPeerConnection();
getAllPeerConnections().filter(x => x !== currentPc).forEach( /*#__PURE__*/function () {
var _ref9 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(futurePc) {
var pc;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
removePeerConnectionByPromise(futurePc);
_context.next = 3;
return futurePc;
case 3:
pc = _context.sent;
logging.debug('closing old peer connection');
pc.close();
case 6:
case "end":
return _context.stop();
}
}, _callee);
}));
return function (_x2) {
return _ref9.apply(this, arguments);
};
}());
if (!_state.isDestroyed()) {
_context2.next = 39;
break;
}
throw new Error('Subscriber destroyed');
case 39:
if ((typeof window !== undefined ? window : global).webkitMediaStream) {
// Enable any video streams that we previously disabled for OPENTOK-27112
webRTCStream.getVideoTracks().forEach(track => {
track.enabled = _stream.hasVideo && _properties.subscribeToVideo;
});
}
video = _widgetView && _widgetView.video();
if (video) {
video.orientation({
width: _stream.videoDimensions.width,
height: _stream.videoDimensions.height,
videoOrientation: _stream.videoDimensions.orientation
});
}
videoIndicator = _chrome && _chrome.videoUnsupportedIndicator;
if (videoIndicator) {
videoIndicator.setVideoUnsupported(!_isVideoSupported);
}
videoElementCreated = new Promise((resolve, reject) => {
const video = _widgetView && _widgetView.video();
if (video && video.domElement()) {
resolve();
return;
}
_widgetView.once('videoElementCreated', resolve);
_subscriber.once('destroyed', reject);
});
_context2.next = 47;
return videoElementCreated;
case 47:
_context2.next = 49;
return _pcConnected.promise;
case 49:
onLoaded(); // if the audioLevelSampler implementation requires a stream we need to set it now
if (_audioLevelSampler && 'webRTCStream' in _audioLevelSampler && webRTCStream.getAudioTracks().length > 0) {
_audioLevelSampler.webRTCStream(webRTCStream);
} // We only need frame rate watcher in Safari because framesPerSecond is
// always zero so we need to rely on calculating the difference of
// framesDecoded across multiple getStats invocations.
// See https://bugs.webkit.org/show_bug.cgi?id=172682
if (!(OTHelpers.env.name === 'Safari' || OTHelpers.env.isChromiumEdge)) {
_context2.next = 57;
break;
}
if (_frameRateWatcher) {
_frameRateWatcher.destroy();
_frameRateWatcher = null;
}
_context2.next = 55;
return getPriorityPeerConnection();
case 55:
peerConnection = _context2.sent;
_frameRateWatcher = watchFrameRate(peerConnection.getStats.bind(peerConnection));
case 57:
_this.trigger('streamAdded', _this);
case 58:
case "end":
return _context2.stop();
}
}, _callee2, null, [[2, 7], [21, 26, 32, 35]]);
}));
return function onRemoteStreamAdded(_x) {
return _ref8.apply(this, arguments);
};
}();
const onRemoteStreamRemoved = webRTCStream => {
_webRTCStream = null;
logging.debug('OT.Subscriber.onStreamRemoved');
const video = _widgetView && _widgetView.video();
if (video && video.stream === webRTCStream) {
_widgetView.destroyVideo();
}
this.trigger('streamRemoved', this);
};
const onRemoteVideoSupported = supported => {
// as _isVideoSupported is true by default, this will only ever be hit if we receive
// a onRemoteVideoSupported false, and then a onRemoteVideoSupported true. In other words
// it will not trigger an event if video is enabled and is never disabled due to codec issues.
if (_isVideoSupported !== supported) {
this.dispatchEvent(new Events.VideoEnabledChangedEvent(supported ? 'videoEnabled' : 'videoDisabled', {
reason: supported ? 'codecChanged' : 'codecNotSupported'
}));
}
_isVideoSupported = supported;
};
const audioBlockedStateChange = value => {
_widgetView.setAudioBlockedUi(value);
const indicator = _chrome && _chrome.audioBlockedIndicator;
if (indicator) {
indicator.setAudioBlocked(value);
}
const videoIndicator = _chrome && _chrome.videoUnsupportedIndicator;
if (videoIndicator) {
videoIndicator.setVideoUnsupported(!value && !_isVideoSupported);
}
};
this.on('audioBlocked', () => {
audioBlockedStateChange(true);
});
this.on('audioUnblocked', () => {
audioBlockedStateChange(false);
});
const connectionStateMap = {
new: eventNames.SUBSCRIBER_DISCONNECTED,
checking: eventNames.SUBSCRIBER_DISCONNECTED,
connected: eventNames.SUBSCRIBER_CONNECTED,
completed: eventNames.SUBSCRIBER_CONNECTED,
disconnected: eventNames.SUBSCRIBER_DISCONNECTED
};
const onIceConnectionStateChange = state => {
const mappedState = connectionStateMap[state];
if (mappedState && mappedState !== _lastIceConnectionState) {
_lastIceConnectionState = mappedState;
if (_widgetView) {
_widgetView.loading(mappedState !== eventNames.SUBSCRIBER_CONNECTED);
}
logging.debug("OT.Subscriber.connectionStateChanged to " + state);
this.dispatchEvent(new Events.ConnectionStateChangedEvent(mappedState, this));
}
};
const onIceRestartSuccess = () => {
logResubscribe('Success');
};
const onIceRestartFailure = () => {
logResubscribe('Failure', {
reason: 'ICEWorkflow',
message: 'OT.Subscriber PeerConnection Error: ' + 'The stream was unable to connect due to a network error.' + ' Make sure your connection isn\'t blocked by a firewall.'
});
};
const streamUpdated = event => {
const video = _widgetView && _widgetView.video();
switch (event.changedProperty) {
case 'videoDimensions':
if (!video) {
// Ignore videoDimensions updates before video is created OPENTOK-17253
break;
}
video.orientation({
width: event.newValue.width,
height: event.newValue.height,
videoOrientation: event.newValue.orientation
});
this.dispatchEvent(new Events.VideoDimensionsChangedEvent(this, event.oldValue, event.newValue));
break;
case 'videoDisableWarning':
if (_chrome) {
_chrome.videoDisabledIndicator.setWarning(event.newValue);
}
this.dispatchEvent(new Events.VideoDisableWarningEvent(event.newValue ? 'videoDisableWarning' : 'videoDisableWarningLifted'));
_congestionLevel = event.newValue === 'videoDisableWarning' ? 1 : null;
break;
case 'hasVideo':
// @todo setAudioOnly affects peer connections, what happens with new ones?
setAudioOnly(!_stream.hasVideo || !_properties.subscribeToVideo);
this.dispatchEvent(new Events.VideoEnabledChangedEvent(_stream.hasVideo ? 'videoEnabled' : 'videoDisabled', {
reason: 'publishVideo'
}));
break;
case 'hasAudio':
_muteDisplayMode.update();
break;
default:
}
}; // Use _stream.getChannelsOfType instead of _webRTCStream.getAudioTracks
// because its available as soon as Subscriber is instantiated.
const _hasAudioTracks = () => _stream.getChannelsOfType('audio').length > 0; // / Chrome
_muteDisplayMode = {
get() {
// Use buttonDisplayMode if we have an audio track, even if its muted
return _hasAudioTracks() ? _subscriber.getStyle('buttonDisplayMode') : 'off';
},
update() {
const mode = _muteDisplayMode.get();
if (_chrome) {
_chrome.muteButton.setDisplayMode(mode);
_chrome.backingBar.setMuteMode(mode);
}
}
};
const updateChromeForStyleChange = (key, value
/* , oldValue */
) => {
if (!_chrome) {
return;
}
switch (key) {
case 'nameDisplayMode':
_chrome.name.setDisplayMode(value);
_chrome.backingBar.setNameMode(value);
break;
case 'videoUnsupportedDisplayMode':
_chrome.videoUnsupportedIndicator.setDisplayMode(value);
break;
case 'videoDisabledDisplayMode':
_chrome.videoDisabledIndicator.setDisplayMode(value);
break;
case 'audioBlockedDisplayMode':
_chrome.audioBlockedIndicator.setDisplayMode(value);
break;
case 'showArchiveStatus':
_chrome.archive.setShowArchiveStatus(value);
break;
case 'buttonDisplayMode':
_muteDisplayMode.update();
break;
case 'audioLevelDisplayMode':
_chrome.audioLevel.setDisplayMode(value);
break;
case 'bugDisplayMode':
// bugDisplayMode can't be updated but is used by some partners
break;
case 'backgroundImageURI':
_widgetView.setBackgroundImageURI(value);
break;
default:
}
};
const _createChrome = () => {
const widgets = {
backingBar: new BackingBar({
nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'),
muteMode: _muteDisplayMode.get()
}),
name: new NamePanel({
name: _properties.name,
mode: this.getStyle('nameDisplayMode')
}),
muteButton: new MuteButton({
muted: _audioVolume === 0,
mode: _muteDisplayMode.get()
})
};
if (_audioLevelCapable) {
const audioLevelTransformer = new AudioLevelTransformer();
_audioLevelMeter = new AudioLevelMeter({
mode: _subscriber.getStyle('audioLevelDisplayMode')
});
const updateAudioLevel = new RafRunner(() => {
_audioLevelMeter.setValue(audioLevelTransformer.transform(this.loudness));
});
_audioLevelMeter.watchVisibilityChanged(visible => {
if (visible) {
updateAudioLevel.start();
} else {
updateAudioLevel.stop();
}
});
_audioLevelMeter.setDisplayMode(this.getStyle('audioLevelDisplayMode'));
_audioLevelMeter.audioOnly(false);
widgets.audioLevel = _audioLevelMeter;
}
widgets.videoDisabledIndicator = new VideoDisabledIndicator({
mode: this.getStyle('videoDisabledDisplayMode')
});
widgets.audioBlockedIndicator = new AudioBlockedIndicator({
mode: this.getStyle('audioBlockedDisplayMode')
});
widgets.videoUnsupportedIndicator = new VideoUnsupportedIndicator({
mode: this.getStyle('videoUnsupportedDisplayMode')
});
if (_widgetView && _widgetView.domElement) {
_chrome = new Chrome({
parent: _widgetView.domElement
}).set(widgets).on({
muted() {
_subscriber.setAudioVolume(0);
},
unmuted() {
_subscriber.setAudioVolume(_latestPositiveVolume);
}
}, this); // Hide the chrome until we explicitly show it
_chrome.hideWhileLoading();
}
};
const _showError = code => {
let errorMessage;
let helpMessage; // Display the error message inside the container, assuming it's
// been created by now.
if (_widgetView) {
if (code === ExceptionCodes.STREAM_LIMIT_EXCEEDED) {
errorMessage = 'The stream was unable to connect.';
helpMessage = 'The limit for the number of media streams has been reached.';
} else {
errorMessage = 'The stream was unable to connect due to a network error.';
if (_hasLoadedAtLeastOnce) {
helpMessage = 'Ensure you have a stable connection and try again.';
} else {
helpMessage = 'Make sure you have a stable network connection and that it isn\'t ' + 'blocked by a firewall.';
}
}
_widgetView.addError(errorMessage, helpMessage);
}
};
StylableComponent(this, {
nameDisplayMode: 'auto',
buttonDisplayMode: 'auto',
audioLevelDisplayMode: 'auto',
videoDisabledDisplayMode: 'auto',
audioBlockedDisplayMode: 'auto',
backgroundImageURI: null,
showArchiveStatus: true,
showMicButton: true
}, _properties.showControls, payload => {
logAnalyticsEvent('SetStyle', 'Subscriber', payload, null, 0.1);
});
function setAudioOnly(audioOnly) {
getAllPeerConnections().forEach( /*#__PURE__*/function () {
var _ref10 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(peerConnection) {
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
_context3.next = 2;
return peerConnection;
case 2:
_context3.sent.subscribeToVideo(!audioOnly);
case 3:
case "end":
return _context3.stop();
}
}, _callee3);
}));
return function (_x3) {
return _ref10.apply(this, arguments);
};
}());
if (_widgetView) {
_widgetView.audioOnly(audioOnly);
_widgetView.showPoster(audioOnly);
}
if (_audioLevelMeter) {
_audioLevelMeter.audioOnly(audioOnly);
}
} // logs an analytics event for getStats on the first call
const notifyGetStatsCalled = once(() => {
logAnalyticsEvent('GetStats', 'Called');
});
this._destroy = (_ref11) => {
let _ref11$reason = _ref11.reason,
reason = _ref11$reason === void 0 ? 'Unknown' : _ref11$reason,
quiet = _ref11.quiet,
_ref11$noStateTransit = _ref11.noStateTransition,
noStateTransition = _ref11$noStateTransit === void 0 ? false : _ref11$noStateTransit;
if (_state.isDestroyed()) {
return this;
}
_state.set('Destroyed');
if (_frameRateWatcher) {
_frameRateWatcher.destroy();
_frameRateWatcher = null;
}
_preDisconnectStats = {
sessionId: _session.sessionId,
connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null,
partnerId: _session && _session.sessionInfo ? _session.sessionInfo.partnerId : null,
streamId: _stream && !_stream.destroyed ? _stream.id : null
};
this._disconnect({
reason,
noStateTransition
});
if (_chrome) {
_chrome.destroy();
_chrome = null;
}
if (_widgetView) {
_widgetView.destroy();
_widgetView.off();
_widgetView = null;
this.element = null;
}
if (_stream && !_stream.destroyed) {
logAnalyticsEvent('unsubscribe', null, {
streamId: _stream.id
});
}
_stream.off(_streamEventHandlers, this);
this.id = null;
_domId = null;
this.stream = null;
_stream = null;
this.streamId = null;
this.session = null;
_session = null;
_properties = null;
if (quiet !== true) {
this.dispatchEvent(new Events.DestroyedEvent(eventNames.SUBSCRIBER_DESTROYED, this, reason));
this.off();
}
return this;
};
this.destroy = function (reason, quiet) {
if (reason === void 0) {
reason = 'Unsubscribe';
}
logging.warn('Subscriber#destroy is deprecated and will be removed. Please use Session#unsubscribe instead');
_this._destroy({
reason,
quiet
});
};
this._disconnect = function (_temp4) {
let _ref12 = _temp4 === void 0 ? {} : _temp4,
_ref12$reason = _ref12.reason,
reason = _ref12$reason === void 0 ? 'Unknown' : _ref12$reason,
noStateTransition = _ref12.noStateTransition;
// known reasons:
// forceUnpublished (publisher stream was destroyed by forceUnpublish)
// clientDisconnected (publisher unpublished)
// Unsubscribe (when calling session.unsubscribe)
// Unknown (when reason is not determined)
if (!noStateTransition) {
const error = reason === 'Unsubscribe' ? undefined : otError(errors.STREAM_DESTROYED, new Error('Stream was destroyed before it could be subscribed to'));
connectivityState.disconnect({
payload: {
reason
},
error
});
}
if (!_state.isDestroyed() && !_state.isFailed()) {
// If we are already in the destroyed state then disconnect
// has been called after (or from within) destroy.
_state.set('NotSubscribing');
}
if (_widgetView) {
_widgetView.destroyVideo();
} // @todo check peer connection destroy triggers disconnect, and then gets logged...
getAllPeerConnections().forEach( /*#__PURE__*/function () {
var _ref13 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(peerConnection) {
return _regenerator.default.wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
_context4.next = 2;
return peerConnection;
case 2:
_context4.sent.destroy();
case 3:
case "end":
return _context4.stop();
}
}, _callee4);
}));
return function (_x4) {
return _ref13.apply(this, arguments);
};
}());
Object.keys(peerConnections).forEach(key => {
delete peerConnections[key];
}); // Unsubscribe us from the stream, if it hasn't already been destroyed
if (socket.is('connected') && _stream && !_stream.destroyed) {
// Notify the server components
// @todo I assume we don't want to send this message, but is there anything
// we need to do in p2p->mantis for when a peer conn is destroyed?
socket.subscriberDestroy(_stream.id, _this.widgetId);
}
};
this.disconnect = () => {
logging.warn('Subscriber#disconnect is deprecated and will be removed. Please use Session#unsubscribe instead');
this._disconnect({
reason: 'Unsubscribe'
});
};
const processOffer = /*#__PURE__*/function () {
var _ref15 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(_ref14) {
var peerPriority, peerId, fromConnectionId, uri, send, log, logQoS;
return _regenerator.default.wrap(function _callee5$(_context5) {
while (1) switch (_context5.prev = _context5.next) {
case 0:
peerPriority = _ref14.peerPriority, peerId = _ref14.peerId, fromConnectionId = _ref14.fromConnectionId;
if (getPeerConnectionByPriority(peerPriority)) {
_context5.next = 9;
break;
}
logging.info("PeerConnection escalation to " + peerPriority);
uri = constructSubscriberUri({
apiKey: _session.apiKey,
sessionId: _session.sessionId,
streamId: _stream.id,
subscriberId: _this.widgetId
});
send = createSendMethod({
socket: _this.session._.getSocket(),
uri,
content: {
peerPriority,
peerId
}
});
log = function log(action, variation, payload, options, throttle) {
if (options === void 0) {
options = {};
}
const transformedOptions = (0, _extends2.default)({
peerId,
peerPriority
}, options);
return logAnalyticsEvent(action, variation, payload, transformedOptions, throttle);
};
logQoS = qos => recordQOS((0, _extends2.default)({}, qos, {
peerId,
peerPriority,
remoteConnectionId: fromConnectionId
}));
_context5.next = 9;
return _this._setupPeerConnection({
peerPriority,
send,
log,
logQoS
});
case 9:
case "end":
return _context5.stop();
}
}, _callee5);
}));
return function processOffer(_x5) {
return _ref15.apply(this, arguments);
};
}();
this.processMessage = /*#__PURE__*/function () {
var _ref16 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(type, fromConnectionId, message) {
var peerPriority, peerId, peerConnection;
return _regenerator.default.wrap(function _callee6$(_context6) {
while (1) switch (_context6.prev = _context6.next) {
case 0:
logging.debug("OT.Subscriber.processMessage: Received " + type + " message from " + fromConnectionId);
logging.debug(message);
peerPriority = Number(get(message, 'content.peerPriority', 0));
peerId = get(message, 'content.peerId');
if (!(peerPriority < getMaxPriority())) {
_context6.next = 7;
break;
}
logging.debug("Ignoring " + type + " message from " + fromConnectionId + " with " + peerPriority + ". PeerConnection is marked as obsolete");
return _context6.abrupt("return");
case 7:
if (!(type === 'offer')) {
_context6.next = 10;
break;
}
_context6.next = 10;
return processOffer({
peerPriority,
peerId,
fromConnectionId
});
case 10:
_context6.next = 12;
return getPeerConnectionByPriority(peerPriority);
case 12:
peerConnection = _context6.sent;
peerConnection.processMessage(type, message);
case 14:
case "end":
return _context6.stop();
}
}, _callee6);
}));
return function (_x6, _x7, _x8) {
return _ref16.apply(this, arguments);
};
}();
this.disableVideo = active => {
if (!active) {
logging.warn('Due to high packet loss and low bandwidth, video has been disabled');
} else if (_lastSubscribeToVideoReason === 'auto') {
logging.info('Video has been re-enabled');
} else {
logging.info('Video was not re-enabled because it was manually disabled');
return;
}
this.subscribeToVideo(active, 'auto');
const payload = active ? {
videoEnabled: true
} : {
videoDisabled: true
};
logAnalyticsEvent('updateQuality', 'video', payload);
};
/**
* Returns the base-64-encoded string of PNG data representing the Subscriber video.
*
* You can use the string as the value for a data URL scheme passed to the src parameter of * an image file, as in the following:
* ** var imgData = subscriber.getImgData(); * * var img = document.createElement("img"); * img.setAttribute("src", "data:image/png;base64," + imgData); * var imgWin = window.open("about:blank", "Screenshot"); * imgWin.document.write("<body></body>"); * imgWin.document.body.appendChild(img); ** @method #getImgData * @memberOf Subscriber * @return {String} The base-64 encoded string. Returns an empty string if there is no video. */ this.getImgData = () => { if (!this.isSubscribing()) { logging.error('OT.Subscriber.getImgData: Cannot getImgData before the Subscriber ' + 'is subscribing.'); return null; } const video = _widgetView && _widgetView.video(); return video ? video.imgData() : null; }; /** * Returns the details on the subscriber stream quality, including the following: * *
testNetwork
property to true
in the options you
* pass into the Session.subscribe() method. For an example,
* see the opentok-network-test
* project on GitHub.
*
* You may also use these statistics to have a Subscriber subscribe to audio-only if the audio
* packet loss reaches a certain threshold. If you choose to do this, you should set the
* audioFallbackEnabled
setting to false
when you initialize Publisher
* objects for the session. This prevents the OpenTok Media Router from using its own audio-only
* toggling implementation. (See the documentation for the
* OT.initPublisher() method.)
*
* @param {Function} completionHandler A function that takes the following
* parameters:
*
*
error
(Error) — The error property is
* set if the client is not connected. Upon completion of a successful call to the method,
* this property is undefined.stats
(Object) — An object with the following properties:
* *
audio.bytesReceived
(Number) — The total number of audio bytes
* received by the subscriberaudio.packetsLost
(Number) — Total audio packets that did not reach
* the subscriberaudio.packetsReceived
(Number) — The total number of audio packets
* received by the subscribertimestamp
(Number) — The timestamp, in milliseconds since the Unix
* epoch, for when these stats were gatheredvideo.bytesReceived
(Number) — The total video bytes received by
* the subscribervideo.packetsLost
(Number) — The total number of video packets that
* did not reach the subscribervideo.packetsReceived
(Number) — The total number of video packets
* received by the subscribervideo.frameRate
(Number) — The current video frame rateYou can set the initial volume when you call the Session.subscribe()
* method. Pass a audioVolume
property of the properties
parameter
* of the method.
mySubscriber.setAudioVolume(50).setStyle(newStyle);* * @see getAudioVolume() * @see Session.subscribe() * @method #setAudioVolume * @memberOf Subscriber */ this.setAudioVolume = requestedVolume => { const volume = normalizeAudioVolume(requestedVolume); logAnalyticsEvent('setAudioVolume', 'Attempt', { audioVolume: volume }); if (isNaN(volume)) { logging.error('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100'); logAnalyticsEvent('setAudioVolume', 'Failure', { message: 'value should be an integer between 0 and 100' }); return this; } if (volume !== requestedVolume) { logging.warn('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100'); } if (volume === _audioVolume) { setAudioVolume(_audioVolume); logAnalyticsEvent('setAudioVolume', 'Success', { audioVolume: _audioVolume, message: 'Requested volume is same as already set audioVolume' }); return this; } if (_audioVolume > 0) { _latestPositiveVolume = _audioVolume; } _audioVolume = volume; setAudioVolume(_audioVolume); if (_audioVolume > 0 && !_isSubscribingToAudio) { // in Firefox (and others) we don't stop subscribing to audio when muted, however if we are 'unmuting' and in // the subscribeToAudio: false state we should subscribe to audio again // subscribeToAudio is going to call us with _latestPositiveVolume so we'll update it here _latestPositiveVolume = _audioVolume; this.subscribeToAudio(true); } logAnalyticsEvent('setAudioVolume', 'Success', { audioVolume: _audioVolume }); return this; }; /** * Returns the audio volume, between 0 and 100, of the Subscriber. * *
Generally you use this method in conjunction with the setAudioVolume()
* method.
value
is true
; stops
* subscribing to audio (if it is currently being subscribed to) when the value
* is false
.
* * Note: This method only affects the local playback of audio. It has no impact on the * audio for other connections subscribing to the same stream. If the Publisher is not * publishing audio, enabling the Subscriber audio will have no practical effect. *
* * @param {Boolean} value Whether to start subscribing to audio (true
) or not
* (false
).
*
* @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the
* following:
*
* mySubscriber.subscribeToAudio(true).subscribeToVideo(false);* * @see subscribeToVideo() * @see Session.subscribe() * @see StreamPropertyChangedEvent * * @method #subscribeToAudio * @memberOf Subscriber */ this.subscribeToAudio = pValue => { const value = castToBoolean(pValue, true); getAllPeerConnections().forEach( /*#__PURE__*/function () { var _ref18 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(peerConnection) { return _regenerator.default.wrap(function _callee8$(_context8) { while (1) switch (_context8.prev = _context8.next) { case 0: _context8.next = 2; return peerConnection; case 2: _context8.sent.subscribeToAudio(value); case 3: case "end": return _context8.stop(); } }, _callee8); })); return function (_x10) { return _ref18.apply(this, arguments); }; }()); if (_stream && getAllPeerConnections().length !== 0) { _stream.setChannelActiveState('audio', value); } const changed = _isSubscribingToAudio !== value; _isSubscribingToAudio = value; if (changed) { this.setAudioVolume(value ? _latestPositiveVolume : 0); } logAnalyticsEvent('subscribeToAudio', 'Event', { subscribeToAudio: value }); return this; }; const reasonMap = { auto: 'quality', publishVideo: 'publishVideo', subscribeToVideo: 'subscribeToVideo' }; if (env.name === 'Safari') { const onVisibilityChange = () => { if (!document.hidden && _properties.subscribeToVideo) { logging.debug('document visibility restored in Safari - resubscribing to video'); this.subscribeToVideo(false); this.subscribeToVideo(true); } }; document.addEventListener('visibilitychange', onVisibilityChange); this.once('destroyed', () => { document.removeEventListener('visibilitychange', onVisibilityChange); }); } /** * Toggles video on and off. Starts subscribing to video (if it is available and * currently not being subscribed to) when the
value
is true
;
* stops subscribing to video (if it is currently being subscribed to) when the
* value
is false
.
* * Note: This method only affects the local playback of video. It has no impact on * the video for other connections subscribing to the same stream. If the Publisher is not * publishing video, enabling the Subscriber video will have no practical effect. *
* * @param {Boolean} value Whether to start subscribing to video (true
) or not
* (false
).
*
* @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the
* following:
*
* mySubscriber.subscribeToVideo(true).subscribeToAudio(false);* * @see subscribeToAudio() * @see Session.subscribe() * @see StreamPropertyChangedEvent * * @method #subscribeToVideo * @memberOf Subscriber */ this.subscribeToVideo = (pValue, reason) => { const value = castToBoolean(pValue, true); logAnalyticsEvent('subscribeToVideo', 'Attempt', { subscribeToVideo: value, reason }); setAudioOnly(!value || !_stream.hasVideo || !(_webRTCStream && _webRTCStream.getVideoTracks().length > 0)); if (_stream.hasVideo && _webRTCStream && _webRTCStream.getVideoTracks().length === 0) { if (value) { logging.info('Subscriber is audio-only due to incompatibility, can\'t subscribeToVideo.'); } _properties.subscribeToVideo = false; logAnalyticsEvent('subscribeToVideo', 'Failure', { message: 'No video tracks available' }); return this; } if (_chrome && _chrome.videoDisabledIndicator) { // If this is an auto disableVideo then we want to show the indicator, otherwise hide it again _chrome.videoDisabledIndicator.disableVideo(reason === 'auto' && !value); } if (getAllPeerConnections().length > 0) { if (_session && _stream && (value !== _properties.subscribeToVideo || reason !== _lastSubscribeToVideoReason)) { _stream.setChannelActiveState('video', value, reason); } } _properties.subscribeToVideo = value; _lastSubscribeToVideoReason = reason; logAnalyticsEvent('subscribeToVideo', 'Success', { subscribeToVideo: value, reason }); if (reason !== 'loading') { this.dispatchEvent(new Events.VideoEnabledChangedEvent(value ? 'videoEnabled' : 'videoDisabled', { reason: reasonMap[reason] || 'subscribeToVideo' })); if (value === 'videoDisabled' && reason === 'auto') { _congestionLevel = 2; } } return this; }; /** * Sets the preferred resolution of the subscriber's video. *
* Lowering the preferred resolution * lowers video quality on the subscribing client, but it also reduces network and CPU usage. * You may want to use a lower resolution based on the dimensions of subscriber's video on * the web page. You may want to use a resolution rate for a subscriber to a stream that is less * important (and smaller) than other streams. *
*
* This method only applies when subscribing to a stream that uses the * * scalable video feature. Scalable video is available: *
* In streams that do not use scalable video, calling this method has no effect. *
* Note: The resolution for scalable video streams automatically adjusts for each * subscriber, based on network conditions and CPU usage, even if you do not call this method. * Call this method if you want to set a maximum resolution for this subscriber. *
* In streams that do not use scalable video, calling this method has no effect. *
* Not every frame rate is available to a subscriber. When you set the preferred resolution for
* the subscriber, OpenTok.js picks the best resolution available that matches your setting.
* The resolutions available are based on the value of the Subscriber object's
* stream.resolution
property, which represents the maximum resolution available for
* the stream. The actual resolutions available depend, dynamically, on network and CPU resources
* available to the publisher and subscriber.
*
* You can set the initial preferred resolution used by setting the
* preferredResolution
property of the options
object you pass into the
* Session.subscribe()
method.
*
* @param {Object} resolution Set this to an object with two properties: width
and
* height
(both numbers), such as {width: 320, height: 240}
. Set this to
* null
to remove the preferred resolution, and the client will use the highest
* resolution available.
*
* @see Subscriber.setPreferredFrameRate()
* @see Session.subscribe()
*
* @method #setPreferredResolution
* @memberOf Subscriber
*/
this.setPreferredResolution = preferredResolution => {
if (_state.isDestroyed() || getAllPeerConnections().length === 0 && !_state.current === 'Connecting') {
logging.warn('Cannot set the max Resolution when not subscribing to a publisher');
return;
}
_properties.preferredResolution = preferredResolution;
if (_session.sessionInfo.p2pEnabled) {
logging.warn('OT.Subscriber.setPreferredResolution will not work in a P2P Session');
return;
}
const curMaxResolution = _stream.getPreferredResolution();
const isUnchanged = curMaxResolution && preferredResolution && curMaxResolution.width === preferredResolution.width && curMaxResolution.height === preferredResolution.height || !curMaxResolution && !preferredResolution;
if (isUnchanged) {
return;
}
_stream.setPreferredResolution(preferredResolution);
};
/**
* Sets the preferred frame rate of the subscriber's video.
*
* Lowering the preferred frame rate * lowers video quality on the subscribing client, but it also reduces network and CPU usage. * You may want to use a lower frame rate for a subscriber to a stream that is less important * than other streams. *
*
* This method only applies when subscribing to a stream that uses the * * scalable video feature. Scalable video is available: *
* In streams that do not use scalable video, calling this method has no effect. *
* Note: The frame rate for scalable video streams automatically adjusts for each * subscriber, based on network conditions and CPU usage, even if you do not call this method. * Call this method if you want to set a maximum frame rate for this subscriber. *
* Not every frame rate is available to a subscriber. When you set the preferred frame rate for
* the subscriber, OpenTok.js picks the best frame rate available that matches your setting.
* The frame rates available are based on the value of the Subscriber object's
* stream.frameRate
property, which represents the maximum value available for the
* stream. The actual frame rates available depend, dynamically, on network and CPU resources
* available to the publisher and subscriber.
*
* You can set the initial preferred frame rate used by setting the preferredFrameRate
* property of the options
object you pass into the Session.subscribe()
* method.
*
* @param {Number} frameRate Set this to the desired frame rate (in frames per second). Set this to
* null
to remove the preferred frame rate, and the client will use the highest
* frame rate available.
*
* @see Subscriber.setPreferredResolution()
* @see Session.subscribe()
*
* @method #setPreferredFrameRate
* @memberOf Subscriber
*/
this.setPreferredFrameRate = preferredFrameRate => {
if (_state.isDestroyed() || getAllPeerConnections().length === 0 && !_state.current === 'Connecting') {
logging.warn('Cannot set the max frameRate when not subscribing to a publisher');
return;
}
_properties.preferredFrameRate = preferredFrameRate;
if (_session.sessionInfo.p2pEnabled) {
logging.warn('OT.Subscriber.setPreferredFrameRate will not work in a P2P Session');
return;
}
const currentPreferredFrameRate = _stream.getPreferredFrameRate();
const isUnchangedFrameRate = preferredFrameRate && currentPreferredFrameRate && currentPreferredFrameRate === preferredFrameRate || !currentPreferredFrameRate && !preferredFrameRate;
if (isUnchangedFrameRate) {
return;
}
_stream.setPreferredFrameRate(preferredFrameRate);
};
this.isSubscribing = () => _state.isSubscribing();
this.isWebRTC = true;
this.isLoading = () => _widgetView && _widgetView.loading();
/**
* Indicates whether the subscriber's audio is blocked because of
* the browser's audio autoplay policy.
*
* @see OT.unblockAudio()
* @see The audioBlocked
* and audioUnblocked
* Subscriber events
* @see The style.audioBlockedDisplayMode
property of the
* options
parameter of the
* Session.subscribe() method
*
* @method #isAudioBlocked
* @memberof Subscriber
*/
this.isAudioBlocked = () => Boolean(_widgetView && _widgetView.isAudioBlocked());
this.videoElement = () => {
const video = _widgetView && _widgetView.video();
return video ? video.domElement() : null;
};
/**
* Returns the width, in pixels, of the Subscriber video.
*
* @method #videoWidth
* @memberOf Subscriber
* @return {Number} the width, in pixels, of the Subscriber video.
*/
this.videoWidth = () => {
const video = _widgetView && _widgetView.video();
return video ? video.videoWidth() : undefined;
};
/**
* Returns the height, in pixels, of the Subscriber video.
*
* @method #videoHeight
* @memberOf Subscriber
* @return {Number} the width, in pixels, of the Subscriber video.
*/
this.videoHeight = () => {
const video = _widgetView && _widgetView.video();
return video ? video.videoHeight() : undefined;
};
this._subscribeToSelf = /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9() {
var publisher;
return _regenerator.default.wrap(function _callee9$(_context9) {
while (1) switch (_context9.prev = _context9.next) {
case 0:
publisher = _session.getPublisherForStream(_stream);
if (publisher && publisher._.webRtcStream()) {
_context9.next = 5;
break;
}
connectivityState.fail({
payload: {
reason: 'streamNotFound'
},
error: otError(errors.STREAM_DESTROYED, new Error('Tried to subscribe to a local publisher, but its stream no longer exists'))
});
_context9.next = 14;
break;
case 5:
_pcConnected.resolve();
_context9.prev = 6;
_context9.next = 9;
return onRemoteStreamAdded(publisher._.webRtcStream());
case 9:
_context9.next = 14;
break;
case 11:
_context9.prev = 11;
_context9.t0 = _context9["catch"](6);
logging.error(_context9.t0);
case 14:
case "end":
return _context9.stop();
}
}, _callee9, null, [[6, 11]]);
}));
this._setupPeerConnection = /*#__PURE__*/function () {
var _ref21 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12(_ref20) {
var peerPriority, send, log, logQoS;
return _regenerator.default.wrap(function _callee12$(_context12) {
while (1) switch (_context12.prev = _context12.next) {
case 0:
peerPriority = _ref20.peerPriority, send = _ref20.send, log = _ref20.log, logQoS = _ref20.logQoS;
if (_properties.testNetwork) {
_this.setAudioVolume(0);
}
if (getAllPeerConnections().length === 0) {
// @todo The subscribers states should be something like:
// disconnected || connecting || reconnecting || connected || destroyed
// the state of peer connections belong to the peer connection itself
_state.set('Connecting');
}
peerConnections[peerPriority] = new Promise((resolve, reject) => {
_session._.getIceConfig().then(iceConfig => {
if (iceConfig.needRumorIceServersFallback) {
iceConfig.servers = [...(fallbackIceServers || []), ...(iceConfig.servers || [])];
}
const props = {
iceConfig,
subscriberId: _this.widgetId,
send,
logAnalyticsEvent: log,
p2p: _session.sessionInfo.p2pEnabled
};
if (Object.prototype.hasOwnProperty.call(_properties, 'codecFlags')) {
props.codecFlags = _properties.codecFlags;
}
const peerConnection = new Subscriber.SubscriberPeerConnection(props);
peerConnection.once('iceConnected', _pcConnected.resolve);
peerConnection.once('error', _pcConnected.reject);
if (_currentPeerConnectionEvents) {
_currentPeerConnectionEvents.removeAll();
const onDisconnected = () => {
peerConnection.off('error', onDisconnected);
peerConnection.off('disconnected', onDisconnected);
delete peerConnections[peerPriority];
};
peerConnection.on('error', onDisconnected);
peerConnection.on('disconnected', onDisconnected);
}
_currentPeerConnectionEvents = eventHelper(peerConnection);
_currentPeerConnectionEvents.on('disconnected', onPeerDisconnected);
_currentPeerConnectionEvents.on('error', onPeerConnectionFailure);
_currentPeerConnectionEvents.on('remoteStreamAdded', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10() {
var _args10 = arguments;
return _regenerator.default.wrap(function _callee10$(_context10) {
while (1) switch (_context10.prev = _context10.next) {
case 0:
_context10.prev = 0;
_context10.next = 3;
return onRemoteStreamAdded(..._args10);
case 3:
_context10.next = 8;
break;
case 5:
_context10.prev = 5;
_context10.t0 = _context10["catch"](0);
logging.error(_context10.t0);
case 8:
case "end":
return _context10.stop();
}
}, _callee10, null, [[0, 5]]);
})));
_currentPeerConnectionEvents.on('remoteStreamRemoved', onRemoteStreamRemoved);
_currentPeerConnectionEvents.on('signalingStateStable', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11() {
var video;
return _regenerator.default.wrap(function _callee11$(_context11) {
while (1) switch (_context11.prev = _context11.next) {
case 0:
_subscriber.trigger('signalingStateStable');
video = _widgetView && _widgetView.video();
if (!(video && _webRTCStream)) {
_context11.next = 12;
break;
}
_context11.prev = 3;
_context11.next = 6;
return video.rebind();
case 6:
_context11.next = 11;
break;
case 8:
_context11.prev = 8;
_context11.t0 = _context11["catch"](3);
logging.error('Could not bind to stream', _context11.t0);
case 11:
if (_audioLevelSampler && 'webRTCStream' in _audioLevelSampler && _webRTCStream.getAudioTracks().length > 0) {
_audioLevelSampler.webRTCStream(_webRTCStream);
}
case 12:
case "end":
return _context11.stop();
}
}, _callee11, null, [[3, 8]]);
})));
peerConnection.on('qos', logQoS);
_currentPeerConnectionEvents.on('iceConnectionStateChange', onIceConnectionStateChange);
_currentPeerConnectionEvents.on('iceRestartSuccess', onIceRestartSuccess);
_currentPeerConnectionEvents.on('iceRestartFailure', onIceRestartFailure);
_currentPeerConnectionEvents.on('remoteVideoSupported', onRemoteVideoSupported); // initialize the peer connection AFTER we've added the event listeners
peerConnection.init(err => {
if (err) {
reject(err);
} else {
resolve(peerConnection);
}
});
_currentPeerConnectionEvents.once('remoteStreamAdded', () => {
if (destroyAudioLevelBehaviour) {
destroyAudioLevelBehaviour();
destroyAudioLevelBehaviour = undefined;
} // prefer the audioLevelSampler (more efficient and better responsiveness)
if (hasRemoteStreamsWithWebAudio()) {
try {
_audioLevelSampler = new WebAudioAudioLevelSampler(audioContext());
} catch (e) {
logging.warn('Failed to get AudioContext()', e);
}
}
if (!_audioLevelSampler && Subscriber.hasAudioOutputLevelStatCapability()) {
_audioLevelSampler = new GetStatsAudioOutputLevelSampler(peerConnection.getStats);
}
if (_audioLevelSampler) {
var _audioLevelBehaviour = audioLevelBehaviour({
subscriber: _this,
audioLevelSampler: _audioLevelSampler
});
destroyAudioLevelBehaviour = _audioLevelBehaviour.destroyAudioLevelBehaviour;
} else {
Object.defineProperty(_this, 'loudness', {
value: undefined,
configurable: true,
writable: false
});
logging.error('No suitable audio level samplers found, audio level visualisation will not work');
}
});
}).catch(reject);
});
return _context12.abrupt("return", peerConnections[peerPriority]);
case 5:
case "end":
return _context12.stop();
}
}, _callee12);
}));
return function (_x11) {
return _ref21.apply(this, arguments);
};
}();
/**
* Restricts the frame rate of the Subscriber's video stream, when you pass in
* true
. When you pass in false
, the frame rate of the video stream
* is not restricted.
*
* When the frame rate is restricted, the Subscriber video frame will update once or less per * second. *
* This feature is only available in sessions that use the OpenTok Media Router (sessions with * the media mode * set to routed), not in sessions with the media mode set to relayed. In relayed sessions, * calling this method has no effect. *
* Restricting the subscriber frame rate has the following benefits: *
* Reducing a subscriber's frame rate has no effect on the frame rate of the video in
* other clients.
*
* @param {Boolean} value Whether to restrict the Subscriber's video frame rate
* (true
) or not (false
).
*
* @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the
* following:
*
*
mySubscriber.restrictFrameRate(false).subscribeToAudio(true);* * @method #restrictFrameRate * @memberOf Subscriber */ this.restrictFrameRate = val => { logging.debug("OT.Subscriber.restrictFrameRate(" + val + ")"); logAnalyticsEvent('restrictFrameRate', val.toString(), { streamId: _stream.id }); if (_session.sessionInfo.p2pEnabled) { logging.warn('OT.Subscriber.restrictFrameRate: Cannot restrictFrameRate on a P2P session'); } if (typeof val !== 'boolean') { logging.error("OT.Subscriber.restrictFrameRate: expected a boolean value got a " + typeof val); } else { _frameRateRestricted = val; _stream.setRestrictFrameRate(val); } return this; }; this.on('styleValueChanged', updateChromeForStyleChange, this); this._ = { getDataChannel(label, options, completion) { return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee13() { return _regenerator.default.wrap(function _callee13$(_context13) { while (1) switch (_context13.prev = _context13.next) { case 0: if (getPriorityPeerConnection()) { _context13.next = 3; break; } completion(new OTHelpers.Error('Cannot create a DataChannel before there is a publisher connection.')); return _context13.abrupt("return"); case 3: _context13.next = 5; return getPriorityPeerConnection(); case 5: _context13.sent.getDataChannel(label, options, completion); case 6: case "end": return _context13.stop(); } }, _callee13); }))(); }, iceRestart() { return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee14() { var peerConnection; return _regenerator.default.wrap(function _callee14$(_context14) { while (1) switch (_context14.prev = _context14.next) { case 0: _context14.next = 2; return getPriorityPeerConnection(); case 2: peerConnection = _context14.sent; if (!peerConnection) { logging.debug('Subscriber: Skipping ice restart, we have no peer connection'); } else { logResubscribe('Attempt'); logging.debug('Subscriber: iceRestart attempt'); peerConnection.iceRestart(); } case 4: case "end": return _context14.stop(); } }, _callee14); }))(); }, unblockAudio: () => _widgetView && _widgetView.unblockAudio(), webRtcStream: () => _webRTCStream, privateEvents: new EventEmitter() }; _state = new SubscribingState(stateChangeFailed); logging.debug("OT.Subscriber: subscribe to " + _stream.id); _state.set('Init'); if (!_stream) { // @todo error logging.error('OT.Subscriber: No stream parameter.'); return false; } _streamEventHandlers = { updated: streamUpdated }; _stream.on(_streamEventHandlers, this); _properties.name = _properties.name || _stream.name; _properties.classNames = 'OT_root OT_subscriber'; if (_properties.style) { this.setStyle(_properties.style, null, true); } _latestPositiveVolume = DEFAULT_AUDIO_VOLUME; _properties.subscribeToVideo = castToBoolean(_properties.subscribeToVideo, true); _properties.subscribeToAudio = castToBoolean(_properties.subscribeToAudio, true); this.subscribeToAudio(_properties.subscribeToAudio); this.setAudioVolume(determineAudioVolume(_properties)); _widgetView = new Subscriber.WidgetView(targetElement, (0, _extends2.default)({}, _properties, { widgetType: 'subscriber' })); _widgetView.on('error', onVideoError); _widgetView.on('audioBlocked', () => this.trigger('audioBlocked')); _widgetView.on('audioUnblocked', () => this.trigger('audioUnblocked')); this.id = _widgetView.domId(); _domId = _widgetView.domId(); this.element = _widgetView.domElement; _widgetView.on('videoElementCreated', element => { const event = new Events.VideoElementCreatedEvent(element); const self = this; if (!_loaded) { this.once('loaded', () => { self.dispatchEvent(event); }); } else { this.dispatchEvent(event); } }); if (this.element) { // Only create the chrome if there is an element to insert it in // for insertDefautlUI:false we don't create the chrome _createChrome.call(this); } let channelsToSubscribeTo; if (_properties.subscribeToVideo || _properties.subscribeToAudio) { const audio = _stream.getChannelsOfType('audio'); const video = _stream.getChannelsOfType('video'); channelsToSubscribeTo = audio.map(channel => ({ id: channel.id, type: channel.type, active: _properties.subscribeToAudio })).concat(video.map(channel => { const props = { id: channel.id, type: channel.type, active: _properties.subscribeToVideo, restrictFrameRate: _properties.restrictFrameRate !== undefined ? _properties.restrictFrameRate : false }; if (_properties.preferredFrameRate !== undefined) { props.preferredFrameRate = parseFloat(_properties.preferredFrameRate); } if (_properties.preferredHeight !== undefined) { props.preferredHeight = parseInt(_properties.preferredHeight, 10); } if (_properties.preferredWidth !== undefined) { props.preferredWidth = parseInt(_properties.preferredWidth, 10); } return props; })); } const shouldSubscribeToSelf = !_properties.testNetwork && isLocalStream(_stream, _session); connectivityState.beginConnect(); if (shouldSubscribeToSelf) { // bypass rumor etc and just subscribe locally this._subscribeToSelf(); return this; } socket.subscriberCreate(_stream.id, _widgetId, // subscriberId channelsToSubscribeTo, (err, message) => { // when the publisher is destroyed before we subscribe, chances are we have been told about // before we get the subscriberCreate error, so this can be ignored. if (err && !connectivityState.is('disconnected')) { onSubscriberCreateError(err); } fallbackIceServers = parseIceServers(message); }); /** * Dispatched when the subscriber's audio is blocked because of the * browser's autoplay policy. * * @see OT.unblockAudio() * @see Subscriber.isAudioBlocked() * @see The audioUnblocked event * @see The
style.audioBlockedDisplayMode
property of the
* options
parameter of the
* Session.subscribe() method)
*
* @name audioBlocked
* @event
* @memberof Subscriber
*/
/**
* Dispatched when the subscriber's audio is unblocked after
* being paused because of the browser's autoplay policy.
* * Subscriber audio is unblocked when any of the following occurs: *
click
event (if you have disabled the default
* audio playback icon)
* OT.initPublisher()
).
* style.audioBlockedDisplayMode
property of the
* options
parameter of the
* Session#subscribe() method)
*
* @name audioUnblocked
* @event
* @memberof Subscriber
*/
/**
* Dispatched periodically to indicate the subscriber's audio level. The event is dispatched
* up to 60 times per second, depending on the browser. The audioLevel
property
* of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more
* information.
* * The following example adjusts the value of a meter element that shows volume of the * subscriber. Note that the audio level is adjusted logarithmically and a moving average * is applied: *
* var movingAvg = null; * subscriber.on('audioLevelUpdated', function(event) { * if (movingAvg === null || movingAvg <= event.audioLevel) { * movingAvg = event.audioLevel; * } else { * movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel; * } * * // 1.5 scaling to map the -30 - 0 dBm range to [0,1] * var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1; * logLevel = Math.min(Math.max(logLevel, 0), 1); * document.getElementById('subscriberMeter').value = logLevel; * }); **
This example shows the algorithm used by the default audio level indicator displayed * in an audio-only Subscriber. * * @name audioLevelUpdated * @event * @memberof Subscriber * @see AudioLevelUpdatedEvent */ /** * Dispatched when the video for the subscriber is disabled. *
* The reason
property defines the reason the video was disabled. This can be set to
* one of the following values:
*
* *
"codecNotSupported"
— The client's browser does not support the
* video codec used by the stream. For example, in Safari if you connect to a
* routed session
* in a non-Safari OpenTok
* project, and you try to subscribe to a stream that includes video, the Subscriber
* will dispatch a videoDisabled
event with the reason
property
* set to "codecNotSupported"
. (In routed sessions in a non-Safari project,
* streams use the VP8 video codec, which is not supported in Safari.) The subscriber
* element will also display a "Video format not supported" message. (See
* OT.getSupportedCodecs().)"publishVideo"
— The publisher stopped publishing video by calling
* publishVideo(false)
."quality"
— The OpenTok Media Router stopped sending video
* to the subscriber based on stream quality changes. This feature of the OpenTok Media
* Router has a subscriber drop the video stream when connectivity degrades. (The subscriber
* continues to receive the audio stream, if there is one.)
*
* Before sending this event, when the Subscriber's stream quality deteriorates to a level
* that is low enough that the video stream is at risk of being disabled, the Subscriber
* dispatches a videoDisableWarning
event.
*
* If connectivity improves to support video again, the Subscriber object dispatches
* a videoEnabled
event, and the Subscriber resumes receiving video.
*
* By default, the Subscriber displays a video disabled indicator when a
* videoDisabled
event with this reason is dispatched and removes the indicator
* when the videoEnabled
event with this reason is dispatched. You can control
* the display of this icon by calling the setStyle()
method of the Subscriber,
* setting the videoDisabledDisplayMode
property(or you can set the style when
* calling the Session.subscribe()
method, setting the style
property
* of the properties
parameter).
*
* This feature is only available in sessions that use the OpenTok Media Router (sessions with * the media mode * set to routed), not in sessions with the media mode set to relayed. *
* You can disable this audio-only fallback feature, by setting the
* audioFallbackEnabled
property to false
in the options you pass
* into the OT.initPublisher()
method on the publishing client. (See
* OT.initPublisher().)
*
"subscribeToVideo"
— The subscriber started or stopped subscribing to
* video, by calling subscribeToVideo(false)
.
* videoDisabled
event.
*
* By default, the Subscriber displays a video disabled warning indicator when this event
* is dispatched (and the video is disabled). You can control the display of this icon by
* calling the setStyle()
method and setting the
* videoDisabledDisplayMode
property (or you can set the style when calling
* the Session.subscribe()
method and setting the style
property
* of the properties
parameter).
*
* This feature is only available in sessions that use the OpenTok Media Router (sessions with
* the media mode
* set to routed), not in sessions with the media mode set to relayed.
*
* @see Event
* @see videoDisabled event
* @see videoDisableWarningLifted event
* @name videoDisableWarning
* @event
* @memberof Subscriber
*/
/**
* Dispatched when the Subscriber's video element is created. Add a listener for this event when
* you set the insertDefaultUI
option to false
in the call to the
* Session.subscribe() method. The element
* property of the event object is a reference to the Subscriber's video
element
* (or in Internet Explorer the object
element containing the video). Add it to
* the HTML DOM to display the video. When you set the insertDefaultUI
option to
* false
, the video
(or object
) element is not automatically
* inserted into the DOM.
*
* Add a listener for this event only if you have set the insertDefaultUI
option to
* false
. If you have not set insertDefaultUI
option to
* false
, do not move the video
(or object
) element in
* in the HTML DOM. Doing so causes the Subscriber object to be destroyed.
*
* @name videoElementCreated
* @event
* @memberof Subscriber
* @see VideoElementCreatedEvent
*/
/**
* Dispatched when the OpenTok Media Router determines that the stream quality has improved
* to the point at which the video being disabled is not an immediate risk. This event is
* dispatched after the Subscriber object dispatches a videoDisableWarning
event.
*
* This feature is only available in sessions that use the OpenTok Media Router (sessions with * the media mode * set to routed), not in sessions with the media mode set to relayed. * * @see Event * @see videoDisableWarning event * @see videoDisabled event * @name videoDisableWarningLifted * @event * @memberof Subscriber */ /** * Dispatched when the OpenTok Media Router resumes sending video to the subscriber * after video was previously disabled. *
* The reason
property defines the reason the video was enabled. This can be set to
* one of the following values:
*
* *
"codecChanged"
— The subscriber video was enabled after
* a codec change from an incompatible codec."publishVideo"
— The publisher started publishing video by calling
* publishVideo(true)
."quality"
— The OpenTok Media Router resumed sending video
* to the subscriber based on stream quality changes. This feature of the OpenTok Media
* Router has a subscriber drop the video stream when connectivity degrades and then resume
* the video stream if the stream quality improves.
* * This feature is only available in sessions that use the OpenTok Media Router (sessions with * the media mode * set to routed), not in sessions with the media mode set to relayed. *
"subscribeToVideo"
— The subscriber started or stopped subscribing to
* video, by calling subscribeToVideo(false)
.
*
* To prevent video from resuming, in the videoEnabled
event listener,
* call subscribeToVideo(false)
on the Subscriber object.
*
* @see VideoEnabledChangedEvent
* @see videoDisabled event
* @name videoEnabled
* @event
* @memberof Subscriber
*/
/**
* Sent when the subscriber's stream has been interrupted.
*
* In response to this event, you may want to provide a user interface notification, to let the * user know that the audio-video stream is temporarily disconnected and that the app is trying * to reconnect to it. *
* If the client reconnects to the stream, the Subscriber object dispatches a
* connected
event. Otherwise, if the client cannot reconnect to the stream,
* the Subscriber object dispatches a destroyed
event.
*
* @name disconnected
* @event
* @memberof Subscriber
* @see connected event
* @see Event
*/
/**
* Sent when the subscriber's stream has resumed, after the Subscriber dispatches a
* disconnected
event.
*
* @name connected
* @event
* @memberof Subscriber
* @see disconnected event
* @see Event
*/
/**
* Dispatched when the Subscriber element is removed from the HTML DOM. When this event is
* dispatched, you may choose to adjust or remove HTML DOM elements related to the subscriber.
*
* To prevent the Subscriber from being removed from the DOM when the stream is destroyed,
* listen for the streamDestroyed event
* dispatched by the Session object. The streamDestroyed
event dispatched by the
* Session object is cancelable, and calling the preventDefault()
method of the
* event object prevents Subscribers of the stream from being removed from the HTML DOM.
*
* @see Event
* @name destroyed
* @event
* @memberof Subscriber
*/
/**
* Dispatched when the video dimensions of the video change. This can occur when the
* stream.videoType
property is set to "screen"
(for a screen-sharing
* video stream), when the user resizes the window being captured. It can also occur if the video
* is being published by a mobile device and the user rotates the device (causing the camera
* orientation to change). This event object has a newValue
property and an
* oldValue
property, representing the new and old dimensions of the video.
* Each of these has a height
property and a width
property,
* representing the height and width, in pixels.
* @name videoDimensionsChanged
* @event
* @memberof Subscriber
* @see VideoDimensionsChangedEvent
*/
return this;
};
Subscriber.hasAudioOutputLevelStatCapability = hasAudioOutputLevelStatCapability;
Subscriber.WidgetView = WidgetView;
Subscriber.SubscriberPeerConnection = SubscriberPeerConnection;
return Subscriber;
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 290 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const generateSimpleStateMachine = __webpack_require__(155)(); // Models a Subscriber's subscribing State
//
// Valid States:
// NotSubscribing (the initial state
// Init (basic setup of DOM
// Connecting (Failure Cases -> No Route, Bad Offer, Bad Answer
// BindingRemoteStream (Failure Cases -> Anything to do with the media being
// (invalid, the media never plays
// Subscribing (this is 'onLoad'
// Failed (terminal state, with a reason that maps to one of the
// (failure cases above
// Destroyed (The subscriber has been cleaned up, terminal state
//
//
// Valid Transitions:
// NotSubscribing ->
// Init
//
// Init ->
// Connecting
// | BindingRemoteStream (if we are subscribing to ourselves and we alreay
// (have a stream
// | NotSubscribing (destroy()
//
// Connecting ->
// BindingRemoteStream
// | NotSubscribing
// | Failed
// | NotSubscribing (destroy()
//
// BindingRemoteStream ->
// Subscribing
// | Failed
// | NotSubscribing (destroy()
//
// Subscribing ->
// NotSubscribing (unsubscribe
// | Failed (probably a peer connection failure after we began
// (subscribing
//
// Failed ->
// Destroyed
//
// Destroyed -> (terminal state)
//
//
// @example
// var state = new SubscribingState(function(change) {
// console.log(change.message);
// });
//
// state.set('Init');
// state.current; -> 'Init'
//
// state.set('Subscribing'); -> triggers stateChangeFailed and logs out the error message
//
//
const initialState = 'NotSubscribing';
const validStates = ['NotSubscribing', 'Init', 'Connecting', 'BindingRemoteStream', 'Subscribing', 'Failed', 'Destroyed'];
const validTransitions = {
NotSubscribing: ['NotSubscribing', 'Init', 'Destroyed'],
Init: ['NotSubscribing', 'Connecting', 'BindingRemoteStream', 'Destroyed'],
Connecting: ['NotSubscribing', 'BindingRemoteStream', 'Failed', 'Destroyed'],
BindingRemoteStream: ['NotSubscribing', 'Subscribing', 'Failed', 'Destroyed'],
Subscribing: ['NotSubscribing', 'Failed', 'Destroyed', 'BindingRemoteStream'],
Failed: ['Destroyed'],
Destroyed: []
};
const SubscribingState = generateSimpleStateMachine(initialState, validStates, validTransitions);
SubscribingState.prototype.isDestroyed = function () {
return this.current === 'Destroyed';
};
SubscribingState.prototype.isFailed = function () {
return this.current === 'Failed';
};
SubscribingState.prototype.isSubscribing = function () {
return this.current === 'Subscribing';
};
SubscribingState.prototype.isAttemptingToSubscribe = function () {
return ['Init', 'Connecting', 'BindingRemoteStream'].indexOf(this.current) !== -1;
};
module.exports = SubscribingState;
/***/ }),
/* 291 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__(2);
var _extends2 = _interopRequireDefault(__webpack_require__(23));
module.exports = (_ref) => {
let socket = _ref.socket,
uri = _ref.uri,
defaultContent = _ref.content;
return (method, content) => socket.send({
uri,
method,
content: (0, _extends2.default)({}, defaultContent, {}, content)
});
};
/***/ }),
/* 292 */
/***/ (function(module, exports, __webpack_require__) {
(function webpackUniversalModuleDefinition(root, factory) {
if(true)
module.exports = factory();
else {}
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 5);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function(target, sources) {
var n, source, key;
for(n = 1 ; n < arguments.length ; n++) {
source = arguments[n];
for(key in source) {
if (source.hasOwnProperty(key))
target[key] = source[key];
}
}
return target;
}
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
//-------------------------------------------------------------------------------------------------
var mixin = __webpack_require__(0);
//-------------------------------------------------------------------------------------------------
module.exports = {
build: function(target, config) {
var n, max, plugin, plugins = config.plugins;
for(n = 0, max = plugins.length ; n < max ; n++) {
plugin = plugins[n];
if (plugin.methods)
mixin(target, plugin.methods);
if (plugin.properties)
Object.defineProperties(target, plugin.properties);
}
},
hook: function(fsm, name, additional) {
var n, max, method, plugin,
plugins = fsm.config.plugins,
args = [fsm.context];
if (additional)
args = args.concat(additional)
for(n = 0, max = plugins.length ; n < max ; n++) {
plugin = plugins[n]
method = plugins[n][name]
if (method)
method.apply(plugin, args);
}
}
}
//-------------------------------------------------------------------------------------------------
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
//-------------------------------------------------------------------------------------------------
function camelize(label) {
if (label.length === 0)
return label;
var n, result, word, words = label.split(/[_-]/);
// single word with first character already lowercase, return untouched
if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))
return label;
result = words[0].toLowerCase();
for(n = 1 ; n < words.length ; n++) {
result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();
}
return result;
}
//-------------------------------------------------------------------------------------------------
camelize.prepended = function(prepend, label) {
label = camelize(label);
return prepend + label[0].toUpperCase() + label.substring(1);
}
//-------------------------------------------------------------------------------------------------
module.exports = camelize;
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
//-------------------------------------------------------------------------------------------------
var mixin = __webpack_require__(0),
camelize = __webpack_require__(2);
//-------------------------------------------------------------------------------------------------
function Config(options, StateMachine) {
options = options || {};
this.options = options; // preserving original options can be useful (e.g visualize plugin)
this.defaults = StateMachine.defaults;
this.states = [];
this.transitions = [];
this.map = {};
this.lifecycle = this.configureLifecycle();
this.init = this.configureInitTransition(options.init);
this.data = this.configureData(options.data);
this.methods = this.configureMethods(options.methods);
this.map[this.defaults.wildcard] = {};
this.configureTransitions(options.transitions || []);
this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin);
}
//-------------------------------------------------------------------------------------------------
mixin(Config.prototype, {
addState: function(name) {
if (!this.map[name]) {
this.states.push(name);
this.addStateLifecycleNames(name);
this.map[name] = {};
}
},
addStateLifecycleNames: function(name) {
this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name);
this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name);
this.lifecycle.on[name] = camelize.prepended('on', name);
},
addTransition: function(name) {
if (this.transitions.indexOf(name) < 0) {
this.transitions.push(name);
this.addTransitionLifecycleNames(name);
}
},
addTransitionLifecycleNames: function(name) {
this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name);
this.lifecycle.onAfter[name] = camelize.prepended('onAfter', name);
this.lifecycle.on[name] = camelize.prepended('on', name);
},
mapTransition: function(transition) {
var name = transition.name,
from = transition.from,
to = transition.to;
this.addState(from);
if (typeof to !== 'function')
this.addState(to);
this.addTransition(name);
this.map[from][name] = transition;
return transition;
},
configureLifecycle: function() {
return {
onBefore: { transition: 'onBeforeTransition' },
onAfter: { transition: 'onAfterTransition' },
onEnter: { state: 'onEnterState' },
onLeave: { state: 'onLeaveState' },
on: { transition: 'onTransition' }
};
},
configureInitTransition: function(init) {
if (typeof init === 'string') {
return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true }));
}
else if (typeof init === 'object') {
return this.mapTransition(mixin({}, this.defaults.init, init, { active: true }));
}
else {
this.addState(this.defaults.init.from);
return this.defaults.init;
}
},
configureData: function(data) {
if (typeof data === 'function')
return data;
else if (typeof data === 'object')
return function() { return data; }
else
return function() { return {}; }
},
configureMethods: function(methods) {
return methods || {};
},
configurePlugins: function(plugins, builtin) {
plugins = plugins || [];
var n, max, plugin;
for(n = 0, max = plugins.length ; n < max ; n++) {
plugin = plugins[n];
if (typeof plugin === 'function')
plugins[n] = plugin = plugin()
if (plugin.configure)
plugin.configure(this);
}
return plugins
},
configureTransitions: function(transitions) {
var i, n, transition, from, to, wildcard = this.defaults.wildcard;
for(n = 0 ; n < transitions.length ; n++) {
transition = transitions[n];
from = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard]
to = transition.to || wildcard;
for(i = 0 ; i < from.length ; i++) {
this.mapTransition({ name: transition.name, from: from[i], to: to });
}
}
},
transitionFor: function(state, transition) {
var wildcard = this.defaults.wildcard;
return this.map[state][transition] ||
this.map[wildcard][transition];
},
transitionsFor: function(state) {
var wildcard = this.defaults.wildcard;
return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard]));
},
allStates: function() {
return this.states;
},
allTransitions: function() {
return this.transitions;
}
});
//-------------------------------------------------------------------------------------------------
module.exports = Config;
//-------------------------------------------------------------------------------------------------
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
var mixin = __webpack_require__(0),
Exception = __webpack_require__(6),
plugin = __webpack_require__(1),
UNOBSERVED = [ null, [] ];
//-------------------------------------------------------------------------------------------------
function JSM(context, config) {
this.context = context;
this.config = config;
this.state = config.init.from;
this.observers = [context];
}
//-------------------------------------------------------------------------------------------------
mixin(JSM.prototype, {
init: function(args) {
mixin(this.context, this.config.data.apply(this.context, args));
plugin.hook(this, 'init');
if (this.config.init.active)
return this.fire(this.config.init.name, []);
},
is: function(state) {
return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state);
},
isPending: function() {
return this.pending;
},
can: function(transition) {
return !this.isPending() && !!this.seek(transition);
},
cannot: function(transition) {
return !this.can(transition);
},
allStates: function() {
return this.config.allStates();
},
allTransitions: function() {
return this.config.allTransitions();
},
transitions: function() {
return this.config.transitionsFor(this.state);
},
seek: function(transition, args) {
var wildcard = this.config.defaults.wildcard,
entry = this.config.transitionFor(this.state, transition),
to = entry && entry.to;
if (typeof to === 'function')
return to.apply(this.context, args);
else if (to === wildcard)
return this.state
else
return to
},
fire: function(transition, args) {
return this.transit(transition, this.state, this.seek(transition, args), args);
},
transit: function(transition, from, to, args) {
var lifecycle = this.config.lifecycle,
changed = this.config.options.observeUnchangedState || (from !== to);
if (!to)
return this.context.onInvalidTransition(transition, from, to);
if (this.isPending())
return this.context.onPendingTransition(transition, from, to);
this.config.addState(to); // might need to add this state if it's unknown (e.g. conditional transition or goto)
this.beginTransit();
args.unshift({ // this context will be passed to each lifecycle event observer
transition: transition,
from: from,
to: to,
fsm: this.context
});
return this.observeEvents([
this.observersForEvent(lifecycle.onBefore.transition),
this.observersForEvent(lifecycle.onBefore[transition]),
changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED,
changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED,
this.observersForEvent(lifecycle.on.transition),
changed ? [ 'doTransit', [ this ] ] : UNOBSERVED,
changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED,
changed ? this.observersForEvent(lifecycle.onEnter[to]) : UNOBSERVED,
changed ? this.observersForEvent(lifecycle.on[to]) : UNOBSERVED,
this.observersForEvent(lifecycle.onAfter.transition),
this.observersForEvent(lifecycle.onAfter[transition]),
this.observersForEvent(lifecycle.on[transition])
], args);
},
beginTransit: function() { this.pending = true; },
endTransit: function(result) { this.pending = false; return result; },
failTransit: function(result) { this.pending = false; throw result; },
doTransit: function(lifecycle) { this.state = lifecycle.to; },
observe: function(args) {
if (args.length === 2) {
var observer = {};
observer[args[0]] = args[1];
this.observers.push(observer);
}
else {
this.observers.push(args[0]);
}
},
observersForEvent: function(event) { // TODO: this could be cached
var n = 0, max = this.observers.length, observer, result = [];
for( ; n < max ; n++) {
observer = this.observers[n];
if (observer[event])
result.push(observer);
}
return [ event, result, true ]
},
observeEvents: function(events, args, previousEvent, previousResult) {
if (events.length === 0) {
return this.endTransit(previousResult === undefined ? true : previousResult);
}
var event = events[0][0],
observers = events[0][1],
pluggable = events[0][2];
args[0].event = event;
if (event && pluggable && event !== previousEvent)
plugin.hook(this, 'lifecycle', args);
if (observers.length === 0) {
events.shift();
return this.observeEvents(events, args, event, previousResult);
}
else {
var observer = observers.shift(),
result = observer[event].apply(observer, args);
if (result && typeof result.then === 'function') {
return result.then(this.observeEvents.bind(this, events, args, event))
.catch(this.failTransit.bind(this))
}
else if (result === false) {
return this.endTransit(false);
}
else {
return this.observeEvents(events, args, event, result);
}
}
},
onInvalidTransition: function(transition, from, to) {
throw new Exception("transition is invalid in current state", transition, from, to, this.state);
},
onPendingTransition: function(transition, from, to) {
throw new Exception("transition is invalid while previous transition is still in progress", transition, from, to, this.state);
}
});
//-------------------------------------------------------------------------------------------------
module.exports = JSM;
//-------------------------------------------------------------------------------------------------
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
//-----------------------------------------------------------------------------------------------
var mixin = __webpack_require__(0),
camelize = __webpack_require__(2),
plugin = __webpack_require__(1),
Config = __webpack_require__(3),
JSM = __webpack_require__(4);
//-----------------------------------------------------------------------------------------------
var PublicMethods = {
is: function(state) { return this._fsm.is(state) },
can: function(transition) { return this._fsm.can(transition) },
cannot: function(transition) { return this._fsm.cannot(transition) },
observe: function() { return this._fsm.observe(arguments) },
transitions: function() { return this._fsm.transitions() },
allTransitions: function() { return this._fsm.allTransitions() },
allStates: function() { return this._fsm.allStates() },
onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to) },
onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to) },
}
var PublicProperties = {
state: {
configurable: false,
enumerable: true,
get: function() {
return this._fsm.state;
},
set: function(state) {
throw Error('use transitions to change state')
}
}
}
//-----------------------------------------------------------------------------------------------
function StateMachine(options) {
return apply(this || {}, options);
}
function factory() {
var cstor, options;
if (typeof arguments[0] === 'function') {
cstor = arguments[0];
options = arguments[1] || {};
}
else {
cstor = function() { this._fsm.apply(this, arguments) };
options = arguments[0] || {};
}
var config = new Config(options, StateMachine);
build(cstor.prototype, config);
cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance
return cstor;
}
//-------------------------------------------------------------------------------------------------
function apply(instance, options) {
var config = new Config(options, StateMachine);
build(instance, config);
instance._fsm();
return instance;
}
function build(target, config) {
if ((typeof target !== 'object') || Array.isArray(target))
throw Error('StateMachine can only be applied to objects');
plugin.build(target, config);
Object.defineProperties(target, PublicProperties);
mixin(target, PublicMethods);
mixin(target, config.methods);
config.allTransitions().forEach(function(transition) {
target[camelize(transition)] = function() {
return this._fsm.fire(transition, [].slice.call(arguments))
}
});
target._fsm = function() {
this._fsm = new JSM(this, config);
this._fsm.init(arguments);
}
}
//-----------------------------------------------------------------------------------------------
StateMachine.version = '3.0.1';
StateMachine.factory = factory;
StateMachine.apply = apply;
StateMachine.defaults = {
wildcard: '*',
init: {
name: 'init',
from: 'none'
}
}
//===============================================================================================
module.exports = StateMachine;
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function(message, transition, from, to, current) {
this.message = message;
this.transition = transition;
this.from = from;
this.to = to;
this.current = current;
}
/***/ })
/******/ ]);
});
/***/ }),
/* 293 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(Promise) {
/**
* promisify - Promisify a node style callback function into a promise
*
* Returns a promisifed version of the function, if it's callback is called
* with a truthy error parameter the promise will be rejected. If the callback
* is called with one argument, it's value will be resolved. If the callback
* is called with multiple arguments, the promise will resolve to an array
* of those arguments.
*
* @param {function} fn Function to promisify
* @returns {function} Promise returning function
*/
module.exports = function promisify(fn) {
return function promisified() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return new Promise((resolve, reject) => {
fn(...args, function (err) {
if (err) {
reject(err);
return;
}
for (var _len2 = arguments.length, callbackArgs = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
callbackArgs[_key2 - 1] = arguments[_key2];
}
if (callbackArgs.length === 1) {
resolve(callbackArgs[0]);
return;
}
resolve(callbackArgs);
});
});
};
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 294 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = {
TEARDOWN_REQUIRED: 'The proxyUrl was set after a session or publisher were created. Please tear down the session and all publishers and try again',
ALREADY_SET: 'The proxyUrl has already been set. Setting it again will not have any effect.',
DEPRECATED_METHOD: 'Setting the proxyUrl via initSession has been deprecated because it can result in some HTTPS traffic going directly to OpenTok. Please tear down the session and use OT.setProxyUrl() instead.'
};
/***/ }),
/* 295 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-param-reassign, global-require, max-len, prefer-rest-params */
/* eslint-disable no-use-before-define */
const isFunction = __webpack_require__(13);
const isObject = __webpack_require__(7);
module.exports = function initPublisherFactory(deps) {
if (deps === void 0) {
deps = {};
}
const Errors = deps.Errors || __webpack_require__(8);
const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(11);
const logging = deps.logging || __webpack_require__(1)('publisherInit');
const otError = deps.otError || __webpack_require__(12)();
const OTHelpers = deps.OTHelpers || __webpack_require__(4);
const Publisher = deps.Publisher || __webpack_require__(163)();
const sessionObjects = deps.sessionObjects || __webpack_require__(22);
/**
*
* Initializes and returns a Publisher object. You can then pass this Publisher
* object to Session.publish()
to publish a stream to a session.
*
* Note: If you intend to reuse a Publisher object created using
* OT.initPublisher()
to publish to different sessions sequentially,
* call either Session.disconnect()
or Session.unpublish()
.
* Do not call both. Then call the preventDefault()
method of the
* streamDestroyed
or sessionDisconnected
event object to prevent the
* Publisher object from being removed from the page.
*
id
attribute of the
* existing DOM element used to determine the location of the Publisher video in the HTML DOM. See
* the insertMode
property of the properties
parameter. If you do not
* specify a targetElement
, the application appends a new DOM element to the HTML
* body
.
*
*
* The application throws an error if an element with an ID set to the
* targetElement
value does not exist in the HTML DOM.
*
* The following are recommended settings: *
* The default value is 40,000. *
** Currently, this setting is not supported in streams published in Firefox. *
*true
) or not (false
). The audio-fallback
* feature is available in sessions that use the OpenTok Media Router. With the audio-fallback
* feature enabled (the default), when the server determines that a stream's quality has degraded
* significantly for a specific subscriber, it disables the video in that subscriber in order to
* preserve audio quality. For streams that use a camera as a video source, the default setting is
* true
(the audio-fallback feature is enabled). The default setting is
* false
(the audio-fallback feature is disabled) for screen-sharing streams, which
* have the videoSource
property set to "application"
,
* "screen"
, or "window"
in the OT.initPublisher()
* options. For more information, see the Subscriber
* videoDisabled event and
* the OpenTok Media
* Router and media modes.
* OT.initPublisher()
fails with an
* error (error code 1500, "Unable to Publish") passed to the completion handler function.
*
* If you set this property to null
or false
, the browser does not
* request access to the microphone, and no audio is published.
*
* You can also set this property to an audio
*
* MediaStreamTrack object. This lets you use the object as the audio source for
* the published stream. For example, you can get an array of audio MediaStreamTrack objects
* from the audioTracks
property of an HTMLMediaElement object. Or you can call
* the getLocalStreams()
or getRemoteStreams()
method of an
* RTCPeerConnection object to get an array of MediaStream objects, and then call the
* getAudioTracks()
method of one of the MediaStream objects to get an audio
* MediaStreamTrack object.
*
true
when publishing high-quality audio (by setting the
* audioBitrate
property of the OT.initPublisher()
options). The
* default value is false
.
* false
.
* * Note: Some browsers (such as Chome 73+) do not support * echo cancellation for stereo audio (see this * Chrome issue report). *
*
"user"
— The front-facing camera.
* "environment"
— The back camera.
* "left"
— The camera facing the user but to the left
* (uncommon).
* "right"
— The camera facing the user but to the right
* (uncommon).
* *
"cover"
— The video is cropped if its dimensions do not match those of
* the DOM element. This is the default setting for videos publishing a camera feed.
* "contain"
— The video is letterboxed if its dimensions do not match
* those of the DOM element. This is the default setting for screen-sharing videos.
* If the publisher specifies a frame rate, the actual frame rate of the video stream
* is set as the frameRate
property of the Stream object, though the actual frame rate
* will vary based on changing network and system conditions. If the developer does not specify a
* frame rate, this property is undefined. Also, if the video source is a video MediaStreamTrack
* object (see the videoSource
), this value is ignored and the frame rate is
* defined by the MediaStreamTrack object, which you can get by calling the
* getConstraints()
method of the MediaStreamTrack object.
*
* For sessions that use the OpenTok Media Router (sessions with * the media mode * set to routed, lowering the frame rate proportionally reduces the maximum bandwidth the stream * uses. However, in sessions with the media mode set to relayed, lowering the frame rate does not * reduce the stream's bandwidth. *
*
* You can also restrict the frame rate of a Subscriber's video stream. To restrict the frame rate
* a Subscriber, call the restrictFrameRate()
method of the subscriber, passing in
* true
.
* (See Subscriber.restrictFrameRate().)
*
element
property of the Publisher object) or (if the height is specified as
* a percentage) its parent DOM element (see
* Resizing
* or repositioning a video).
* true
, the default) or not (false
). The default UI element contains
* user interface controls, a video loading indicator, and automatic video cropping or
* letterboxing, in addition to the video. (If you leave insertDefaultUI
set to
* true
, you can control individual UI settings using the fitMode
,
* showControls
, and style
options.)
*
* If you set this option to false
, OpenTok.js does not insert a default UI element
* in the HTML DOM, and the element
property of the Publisher object is undefined.
* Instead, the Publisher object dispatches a
* videoElementCreated event when
* the video
element (or in Internet Explorer the object
element
* containing the video) is created. The element
property of the event object is a
* reference to the Publisher's video
(or object
) element. Add it to
* the HTML DOM to display the video.
*
* Set this option to false
if you want to move the Publisher's video
* (or object
) element in the HTML DOM.
*
* If you set this to false
, do not set the targetElement
parameter.
* (This results in an error passed into to the OT.initPublisher()
callback
* function.) To add the video to the HTML DOM, add an event listener for the
* videoElementCreated
event, and then add the element
property of
* the event object into the HTML DOM.
*
targetElement
parameter. This string can
* have the following values:
* *
"replace"
The Publisher object replaces contents of the
* targetElement. This is the default."after"
The Publisher object is a new element inserted after
* the targetElement in the HTML DOM. (Both the Publisher and targetElement have the
* same parent element.)"before"
The Publisher object is a new element inserted before
* the targetElement in the HTML DOM. (Both the Publisher and targetElement have the same
* parent element.)"append"
The Publisher object is a new element added as a child
* of the targetElement. If there are other child elements, the Publisher is appended as
* the last child element of the targetElement.videoSource
property is set to
* "application"
, "screen"
, or "window"
* (when the publisher is screen-sharing). The resolution of the
* stream will match the captured screen region unless the region is greater than the
* maxResolution
setting. Set this to an object that has two properties:
* width
and height
(both numbers). The maximum value for each of
* the width
and height
properties is 1920, and the minimum value
* is 10.
* true
* (the video image is mirrored), except for a screen-sharing video (when the
* videoSource
property is set to "application"
,
* "screen"
, or "window"
* (in which case the default is false
). This property
* does not affect the display on subscribers' views of the video.
* true
). This setting applies when you pass
* the Publisher object in a call to the Session.publish()
method.
* true
). This setting applies when you pass
* the Publisher object in a call to the Session.publish()
method.
* "widthxheight"
, where the width and height are represented in
* pixels. Valid values are "1280x720"
, "640x480"
, and
* "320x240"
. The published video will only use the desired resolution if the
* client configuration supports it. Some browsers and clients do not support each of these
* resolution settings.
*
* The requested resolution of a video stream is set as the videoDimensions.width
and
* videoDimensions.height
properties of the Stream object.
*
* The default resolution for a stream (if you do not specify a resolution) is 640x480 pixels. * If the client system cannot support the resolution you requested, the stream will use the * next largest setting supported. *
*
* The actual resolution used by the Publisher is returned by the videoHeight()
and
* videoWidth()
methods of the Publisher object. The actual resolution of a
* Subscriber video stream is returned by the videoHeight()
and
* videoWidth()
properties of the Subscriber object. These may differ from the values
* of the resolution
property passed in as the properties
property of the
* OT.initPublisher()
method, if the browser does not support the requested
* resolution, this value is ignored. Also, if the video source is a video MediaStreamTrack
* object (see the videoSource
), this value is ignored and the resolution
* is defined by the MediaStreamTrack object, which you can get by calling the
* getConstraints()
method of the MediaStreamTrack object.
*
true
) for the Publisher. These controls include the name
* display, the audio level indicator, and the microphone control button. You can turn off all user
* interface controls by setting this property to false
. You can control the display
* of individual user interface controls by leaving this property set to true
(the
* default) and setting individual properties of the style
property.
* style
object includes
* the following properties:
* audioLevelDisplayMode
(String) — How to display the audio level
* indicator. Possible values are: "auto"
(the indicator is displayed when the
* video is disabled), "off"
(the indicator is not displayed), and
* "on"
(the indicator is always displayed).archiveStatusDisplayMode
(String) — How to display the archive status
* indicator. Possible values are: "auto"
(the indicator is displayed when the
* session is being recorded), "off"
(the indicator is not displayed). If you
* disable the archive status display indicator, you can display your own user interface
* notifications based on the archiveStarted
and archiveStopped
* events dispatched by the Session object.backgroundImageURI
(String) — A URI for an image to display as
* the background image when a video is not displayed. (A video may not be displayed if
* you call publishVideo(false)
on the Publisher object). You can pass an http
* or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the
* data
URI scheme (instead of http or https) and pass in base-64-encrypted
* PNG data, such as that obtained from the
* Publisher.getImgData() method. (For example,
* you could set the property to a value returned by calling getImgData()
on
* a previous Publisher object.) If the URL or the image data is invalid, the property
* is ignored (the attempt to set the image fails silently).buttonDisplayMode
(String) — How to display the microphone controls
* Possible values are: "auto"
(controls are displayed when the stream is first
* displayed and when the user mouses over the display), "off"
(controls are not
* displayed), and "on"
(controls are always displayed).nameDisplayMode
(String) Whether to display the stream name.
* Possible values are: "auto"
(the name is displayed when the stream is first
* displayed and when the user mouses over the display), "off"
(the name is not
* displayed), and "on"
(the name is always displayed).OT.initPublisher()
fails with an
* error (error code 1500, "Unable to Publish") passed to the completion handler function.
*
* If you set this property to null
or false
, the browser does not
* request access to the camera, and no video is published. In a voice-only call, set this
* property to null
or false
for each Publisher.
*
* To publish a screen-sharing stream, set this property to "application"
,
* "screen"
, or "window"
. In Firefox, the screen sharing source
* reflects the value set. For example, if you set the property to "window"
,
* Firefox will prompt the end user to select the window to share. In other browsers,
* setting the videoSource
property to any of these values results in a prompt
* that asks the user to determine the screen-sharing source. Call
* OT.checkScreenSharingCapability()
* to check if screen sharing is supported and which video sources ("application"
,
* "screen"
, or "window"
) are supported. For screen-sharing streams,
* the following are default values for other properties: audioFallbackEnabled == false
,
* maxResolution == {width: 1920, height: 1920}
, mirror == false
,
* scaleMode == "fit"
. Also, the default scaleMode
setting for
* subscribers to the stream is "fit"
.
*
* You can also set this property to a video
*
* MediaStreamTrack object. This lets you use the MediaStreamTrack object as the
* video source for the published stream. For example, you can get a CanvasCaptureMediaStream
* object by calling the captureStream()
method of a HTMLCanvasElement, and then
* you can call the getVideoTracks()
method of the CanvasCaptureMediaStream object
* to get a video MediaStreamTrack object. Or you can get an array of video MediaStreamTrack
* object from the videoTracks
property of an HTMLMediaElement object. Or you can
* call the getLocalStreams()
or getRemoteStreams()
method of an
* RTCPeerConnection object to get an array of MediaStream objects, and then call the
* getVideoTracks()
method of one of the MediaStream objects to get an array
* of video MediaStreamTrack objects. Note that by default the publisher uses a microphone
* as the audio source, so be sure to set the audioSource
option if you want
* to use a MediaStreamTrack as the audio source or if you do not want to publish an audio
* track. The default mirror
setting for a Publisher with a MediaStreamTrack video
* source is false
. You can also call
* OT.getUserMedia() to get a MediaStream object that uses
* the microphone and camera as the audio and video sources.
*
element
property of the Publisher object) or (if the width is specified as
* a percentage) its parent DOM element (see
* Resizing
* or repositioning a video).
* error
. On success, the error
object is set to null
. On
* failure, the error
object has two properties: code
(an integer) and
* message
(a string), which identify the cause of the failure. The method succeeds
* when the user grants access to the camera and microphone. The method fails if the user denies
* access to the camera and microphone. The completionHandler
function is called
* before the Publisher dispatches an accessAllowed
(success) event or an
* accessDenied
(failure) event.
*
* The following code adds a completionHandler
when calling the
* OT.initPublisher()
method:
*
* var publisher = OT.initPublisher('publisher', null, function (error) { * if (error) { * console.log(error); * } else { * console.log("Publisher initialized."); * } * }); ** * @returns {Publisher} The Publisher object. * @see Session.publish() * @method OT.initPublisher * @memberof OT */ return function initPublisher(targetElement, properties, completionHandler) { logging.debug("OT.initPublisher(" + targetElement + ")"); // To support legacy (apikey, targetElement, properties) users // we check to see if targetElement is actually an apikey. Which we ignore. if (typeof targetElement === 'string' && !document.getElementById(targetElement)) { targetElement = properties; properties = completionHandler; completionHandler = arguments[3]; } if (typeof targetElement === 'function') { completionHandler = targetElement; properties = undefined; targetElement = undefined; } else if (isObject(targetElement) && !OTHelpers.isElementNode(targetElement)) { completionHandler = properties; properties = targetElement; targetElement = undefined; } if (typeof properties === 'function') { completionHandler = properties; properties = undefined; } let errMsg; if (properties && !isObject(properties)) { errMsg = 'properties argument to Publisher constructor, if provided, should be an object'; properties = undefined; } if (properties && properties.insertDefaultUI === false && targetElement) { errMsg = 'You cannot specify a target element if insertDefaultUI is false'; } const publisher = new Publisher(properties || {}); sessionObjects.publishers.add(publisher); const triggerCallback = function triggerCallback() { if (completionHandler && isFunction(completionHandler)) { completionHandler(...arguments); completionHandler = undefined; } }; if (errMsg !== undefined) { logging.error(errMsg); triggerCallback(otError(Errors.INVALID_PARAMETER, new Error(errMsg), ExceptionCodes.INVALID_PARAMETER)); } const removeInitSuccessAndCallComplete = function removeInitSuccessAndCallComplete(err) { publisher.off('publishComplete', removeHandlersAndCallComplete); triggerCallback(err); }; let removeHandlersAndCallComplete = function removeHandlersAndCallComplete(err) { publisher.off('initSuccess', removeInitSuccessAndCallComplete); // We're only handling the error case here as we're just // initing the publisher, not actually attempting to publish. if (err) { triggerCallback(err); } }; publisher.once('initSuccess', removeInitSuccessAndCallComplete); publisher.once('publishComplete', removeHandlersAndCallComplete); publisher.publish(targetElement); return publisher; }; }; /***/ }), /* 296 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(Promise) { var _interopRequireDefault = __webpack_require__(2); var _extends2 = _interopRequireDefault(__webpack_require__(23)); /* eslint-disable no-param-reassign, global-require, no-underscore-dangle, func-names */ /* eslint-disable max-len */ const EventEmitter = __webpack_require__(43); const findKey = __webpack_require__(650); const isObject = __webpack_require__(7); const isFunction = __webpack_require__(13); const assign = __webpack_require__(6); const eventNames = __webpack_require__(24); const eventing = __webpack_require__(5); const AnalyticsHelperDefault = __webpack_require__(54); const StaticConfigDefault = __webpack_require__(28)(); const IntervalRunner = __webpack_require__(71); const promisify = __webpack_require__(293); const testRemovingVideoCodecs = __webpack_require__(652); const validateIceConfigFactory = __webpack_require__(653); const _require = __webpack_require__(128), prependProxyToUrlIfNeeded = _require.prependProxyToUrlIfNeeded; const _require2 = __webpack_require__(164), getProxyUrl = _require2.getProxyUrl; const ProxyUrlWarnings = __webpack_require__(294); module.exports = function SessionFactory(deps) { if (deps === void 0) { deps = {}; } const adaptIceServers = deps.adaptIceServers || __webpack_require__(98).adaptIceServers; /** @type {AnalyticsHelper} */ const AnalyticsHelper = deps.AnalyticsHelper || AnalyticsHelperDefault; const APIKEY = deps.APIKEY || __webpack_require__(69); const Capabilities = deps.Capabilities || __webpack_require__(165); const convertAnvilErrorCode = deps.convertAnvilErrorCode || __webpack_require__(654); const convertRumorError = deps.convertRumorError || __webpack_require__(297); const errors = deps.Errors || __webpack_require__(8); const Events = deps.Events || __webpack_require__(20)(); const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(11); const hasIceRestartsCapability = deps.hasIceRestartsCapability || __webpack_require__(298); const logging = deps.logging || __webpack_require__(1)('Session'); const otError = deps.otError || __webpack_require__(12)(); const OTErrorClass = deps.OTErrorClass || __webpack_require__(33); const OTHelpers = deps.OTHelpers || __webpack_require__(4); /** @type {typeof StaticConfigDefault} */ const StaticConfig = deps.StaticConfig || StaticConfigDefault; /** @type {StaticConfigDefault} */ const localStaticConfig = deps.staticConfig || StaticConfig.onlyLocal(); const Publisher = deps.Publisher || __webpack_require__(163)(); const RaptorSocket = deps.RaptorSocket || __webpack_require__(655).default(); const SessionDispatcher = deps.SessionDispatcher || __webpack_require__(305); const sessionObjects = deps.sessionObjects || __webpack_require__(22); const sessionTag = deps.sessionTag || __webpack_require__(304); const socketCloseCodes = deps.socketCloseCodes || __webpack_require__(73); const Subscriber = deps.Subscriber || __webpack_require__(289)(); const systemRequirements = deps.systemRequirements || __webpack_require__(148); const uuid = deps.uuid || __webpack_require__(15); const validateIceConfig = validateIceConfigFactory({ otError }); const windowMock = deps.global || (typeof window !== undefined ? window : global); const getSessionInfo = deps.getSessionInfo || __webpack_require__(679)(); const initPublisher = deps.initPublisher || __webpack_require__(295)({ Publisher }); /** * The Session object returned by the
OT.initSession()
method provides access to
* much of the OpenTok functionality.
*
* @class Session
* @augments EventDispatcher
*
* @property {Capabilities} capabilities A {@link Capabilities} object that includes information
* about the capabilities of the client. All properties of the capabilities
object
* are undefined until you have connected to a session and the completion handler for the
* Session.connect()
method has been called without error.
* @property {Connection} connection The {@link Connection} object for this session. The
* connection property is only available once the completion handler for the
* Session.connect()
method has been called successfully. See the
* Session.connect() method and the {@link Connection} class.
* @property {String} sessionId The session ID for this session. You pass this value into the
* OT.initSession()
method when you create the Session object. (Note: a Session
* object is not connected to the OpenTok server until you call the connect() method of the
* object and its completion handler is called without error. See the
* OT.initSession() and
* the Session.connect()
* methods.) For more information on sessions and session IDs, see
* Session creation.
*/
const Session = function Session(apiKey, sessionId, _temp) {
var _this = this;
let _ref = _temp === void 0 ? {} : _temp,
_ref$allowPeerRegener = _ref.allowPeerRegeneration,
allowPeerRegeneration = _ref$allowPeerRegener === void 0 ? true : _ref$allowPeerRegener,
iceConfig = _ref.iceConfig,
connectionEventsSuppressed = _ref.connectionEventsSuppressed,
_ref$ipWhitelist = _ref.ipWhitelist,
ipWhitelist = _ref$ipWhitelist === void 0 ? false : _ref$ipWhitelist,
proxyUrl = _ref.proxyUrl;
/** @type AnalyticsHelperDefault */
if (proxyUrl) {
// @TODO WILL NEED TO CONVERT TO EXCEPTION IN 2.18.0
console.warn(ProxyUrlWarnings.DEPRECATED_METHOD);
}
proxyUrl = getProxyUrl() || proxyUrl;
const analytics = new AnalyticsHelper({
proxyUrl
});
const getStream = stream => typeof stream === 'string' ? this.streams.get(stream) || {
id: stream
} : stream;
eventing(this);
this._tag = sessionTag; // Check that the client meets the minimum requirements, if they don't the upgrade
// flow will be triggered.
if (!systemRequirements.check()) {
systemRequirements.upgrade();
}
if (sessionId == null) {
sessionId = apiKey;
apiKey = null;
}
validateIceConfig(iceConfig);
this.id = sessionId;
this.sessionId = sessionId;
let _socket;
/** @type IntervalRunner | undefined */
let _connectivityAttemptPinger;
let _token;
let _p2p;
let _messagingServer;
let _attemptStartTime;
let _configurationAttemptStartTime;
let _iceServerDetails;
let _initialConnection = true;
let _apiKey = apiKey;
const _session = this;
let _sessionId = sessionId;
let _widgetId = uuid();
let _connectionId = uuid();
let _logging = logging;
let disconnectComponents;
let reset;
let destroyPublishers;
let destroySubscribers;
const setState = OTHelpers.statable(this, ['disconnected', 'connecting', 'connected', 'disconnecting'], 'disconnected');
this.connection = null;
this.connections = new OTHelpers.Collection();
this.streams = new OTHelpers.Collection();
this.archives = new OTHelpers.Collection(); //--------------------------------------
// MESSAGE HANDLERS
//--------------------------------------
/*
* The sessionConnectFailed event handler
* @param {Error}
*/
const sessionConnectFailed = function sessionConnectFailed(error) {
setState('disconnected');
if (!error.code) {
error.code = ExceptionCodes.CONNECT_FAILED;
}
_logging.error((error.name || 'Unknown Error') + ": " + error.message);
OTErrorClass.handleJsException({
error,
target: this,
analytics
});
this.trigger('sessionConnectFailed', error);
};
const sessionDisconnectedHandler = function sessionDisconnectedHandler(event) {
const reason = event.reason;
this.logEvent('Connect', 'Disconnected', {
reason: event.reason
});
const publicEvent = new Events.SessionDisconnectEvent('sessionDisconnected', reason.replace('networkTimedout', 'networkDisconnected'));
if (this.isConnected()) {
this.disconnect();
}
reset();
disconnectComponents.call(this, reason);
setTimeout(() => {
this.dispatchEvent(publicEvent); // Although part of the defaultAction for sessionDisconnected we have
// chosen to still destroy Publishers within the session as there is
// another mechanism to stop a Publisher from being destroyed.
// Publishers use preventDefault on the Publisher streamDestroyed event
destroyPublishers.call(this, publicEvent.reason);
if (!publicEvent.isDefaultPrevented()) {
destroySubscribers.call(this, publicEvent.reason);
}
});
};
const connectionCreatedHandler = function connectionCreatedHandler(connection) {
// With connectionEventsSuppressed, connections objects are still added internally, but they
// don't come from rumor messages, and can't have corresponding delete events, so we don't
// emit created events.
if (connectionEventsSuppressed) {
return;
} // We don't broadcast events for the symphony connection
if (connection.id.match(/^symphony\./)) {
return;
}
this.dispatchEvent(new Events.ConnectionEvent(eventNames.CONNECTION_CREATED, connection));
};
const connectionDestroyedHandler = function connectionDestroyedHandler(connection, reason) {
// We don't broadcast events for the symphony connection
if (connection.id.match(/^symphony\./)) {
return;
} // Don't delete the connection if it's ours. This only happens when
// we're about to receive a session disconnected and session disconnected
// will also clean up our connection.
if (_socket && connection.id === _socket.id()) {
return;
}
this.dispatchEvent(new Events.ConnectionEvent(eventNames.CONNECTION_DESTROYED, connection, reason));
};
const streamCreatedHandler = function streamCreatedHandler(stream) {
if (stream && stream.connection && (!this.connection || stream.connection.id !== this.connection.id)) {
this.dispatchEvent(new Events.StreamEvent(eventNames.STREAM_CREATED, stream, null, false));
}
};
const streamPropertyModifiedHandler = function streamPropertyModifiedHandler(event) {
const stream = event.target;
const propertyName = event.changedProperty;
let newValue = event.newValue;
if (propertyName === 'videoDisableWarning' || propertyName === 'audioDisableWarning') {
return; // These are not public properties, skip top level event for them.
}
if (propertyName === 'videoDimensions') {
newValue = {
width: newValue.width,
height: newValue.height
};
}
this.dispatchEvent(new Events.StreamPropertyChangedEvent(eventNames.STREAM_PROPERTY_CHANGED, stream, propertyName, event.oldValue, newValue));
};
const streamDestroyedHandler = function streamDestroyedHandler(stream, reason) {
if (reason === void 0) {
reason = 'clientDisconnected';
}
// if the stream is one of ours we delegate handling to the publisher itself.
if (stream.connection.id === this.connection.id) {
sessionObjects.publishers.where({
streamId: stream.id
}).forEach(function (publisher) {
publisher._.unpublishStreamFromSession(stream, this, reason);
}, this);
return;
}
const event = new Events.StreamEvent('streamDestroyed', stream, reason, true);
this.dispatchEvent(event); // If we are subscribed to any of the streams we should unsubscribe
sessionObjects.subscribers.where({
streamId: stream.id
}).filter(subscriber => subscriber.session.id === this.id && subscriber.stream).forEach(subscriber => {
subscriber._disconnect({
reason
});
if (!event.isDefaultPrevented()) {
subscriber._destroy({
reason,
noStateTransition: true
});
}
}); // @TODO Add a else with a one time warning that this no longer cleans up the publisher
};
const archiveCreatedHandler = function archiveCreatedHandler(archive) {
this.dispatchEvent(new Events.ArchiveEvent('archiveStarted', archive));
};
const archiveDestroyedHandler = function archiveDestroyedHandler(archive) {
this.dispatchEvent(new Events.ArchiveEvent('archiveDestroyed', archive));
};
const archiveUpdatedHandler = function archiveUpdatedHandler(event) {
const archive = event.target;
const propertyName = event.changedProperty;
const newValue = event.newValue;
if (propertyName === 'status' && newValue === 'stopped') {
this.dispatchEvent(new Events.ArchiveEvent('archiveStopped', archive));
} else {
this.dispatchEvent(new Events.ArchiveEvent('archiveUpdated', archive));
}
};
const init = function init() {
_session.token = null;
_token = null;
setState('disconnected');
_socket = null;
if (_connectivityAttemptPinger) {
_connectivityAttemptPinger.stop();
_connectivityAttemptPinger = null;
}
_session.connection = null;
_session.capabilities = new Capabilities([]);
_session.connections.destroy();
_session.streams.destroy();
_session.archives.destroy();
}; // Put ourselves into a pristine state
reset = function reset() {
// reset connection id now so that following calls to testNetwork and connect will share
// the same new session id. We need to reset here because testNetwork might be call after
// and it is always called before the session is connected
// on initial connection we don't reset
_connectionId = uuid();
init();
};
disconnectComponents = function disconnectComponents(reason) {
sessionObjects.publishers.where({
session: this
}).forEach(publisher => {
publisher.disconnect(reason);
});
sessionObjects.subscribers.where({
session: this
}).forEach(subscriber => {
subscriber._disconnect();
});
};
destroyPublishers = function destroyPublishers(reason) {
sessionObjects.publishers.where({
session: this
}).forEach(publisher => {
publisher._.streamDestroyed(reason);
});
};
destroySubscribers = function destroySubscribers(reason) {
sessionObjects.subscribers.where({
session: this
}).forEach(subscriber => {
subscriber._destroy({
reason
});
});
};
const connectMessenger = function connectMessenger() {
_logging.debug('OT.Session: connecting to Raptor');
const messagingUrl = prependProxyToUrlIfNeeded(this.sessionInfo.messagingURL, proxyUrl);
const capabilities = []; // basically we support it if:
if (allowPeerRegeneration && this.sessionInfo.peerRegeneration === true) {
capabilities.push('peerRegeneration');
}
_socket = new RaptorSocket(_connectionId, _widgetId, messagingUrl, this.sessionInfo.symphonyAddress, SessionDispatcher(this, {
connectionEventsSuppressed
}), analytics, capabilities);
/**
* Maps an error from RaptorSocket.connect to its corresponding name
* @param {string} reason - Failure reason
* @param {number} code - Error code
* @return {string|undefined} Error name
*/
function getErrorNameFromCode(reason, code) {
let name;
switch (reason) {
case 'WebSocketConnection':
name = findKey(socketCloseCodes.codes, x => x === code);
if (name) {
return errors["SOCKET_" + name];
}
break;
case 'ConnectToSession':
case 'GetSessionState':
switch (code) {
case ExceptionCodes.CONNECT_FAILED:
return errors.CONNECT_FAILED;
case ExceptionCodes.UNEXPECTED_SERVER_RESPONSE:
return errors.UNEXPECTED_SERVER_RESPONSE;
case ExceptionCodes.CONNECTION_LIMIT_EXCEEDED:
return errors.CONNECTION_LIMIT_EXCEEDED;
default:
break;
}
break;
default:
break;
}
return undefined;
}
_socket.connect(_token, this.sessionInfo, {
connectionEventsSuppressed
}, (error, sessionState) => {
if (error) {
const payload = {};
let options;
if (error.reason === 'ConnectToSession' || error.reason === 'GetSessionState') {
const converted = convertRumorError(error);
assign(payload, {
originalMessage: error.message,
originalCode: error.code
});
error.code = converted.code;
error.message = converted.message;
}
if (error.code || error.message || error.reason) {
options = {
failureCode: error.code,
failureMessage: error.message,
failureReason: error.reason,
socketId: _socket.socketId
};
}
_socket = null;
this.logConnectivityEvent('Failure', payload, options);
const errorName = getErrorNameFromCode(error.reason, error.code);
if (errorName) {
error = otError(errorName, new Error(error.message), error.code);
}
sessionConnectFailed.call(this, error);
return;
}
_logging.debug('OT.Session: Received session state from Raptor', sessionState);
this.connection = this.connections.get(_socket.id());
if (this.connection) {
this.capabilities = this.connection.permissions;
}
setState('connected');
this.logConnectivityEvent('Success', {
connectionId: this.connection.id
}); // Listen for our own connection's destroyed event so we know when we've been disconnected.
this.connection.on('destroyed', sessionDisconnectedHandler, this);
this.dispatchEvent(new Events.SessionConnectEvent(eventNames.SESSION_CONNECTED)); // Listen for connection updates
this.connections.on({
add: connectionCreatedHandler,
remove: connectionDestroyedHandler
}, this); // Listen for stream updates
this.streams.on({
add: streamCreatedHandler,
remove: streamDestroyedHandler,
update: streamPropertyModifiedHandler
}, this);
this.archives.on({
add: archiveCreatedHandler,
remove: archiveDestroyedHandler,
update: archiveUpdatedHandler
}, this);
this.connections._triggerAddEvents(); // { id: this.connection.id }
this.streams._triggerAddEvents(); // { id: this.stream.id }
this.archives._triggerAddEvents();
});
}; // Check whether we have permissions to perform the action.
const permittedTo = action => this.capabilities.permittedTo(action);
const dispatchOTError = (error, completionHandler) => {
logging.error(error.name + ": " + error.message);
completionHandler(error);
OTErrorClass.handleJsException({
error,
target: this,
analytics
});
};
this.reportIssue = (_ref2) => {
let id = _ref2.id;
return promisify(analytics.logEvent.bind(analytics))({
action: 'ReportIssue',
variation: 'Event',
connectionId: _connectionId,
payload: {
reportIssueId: id
}
}, null);
};
this.logEvent = function (action, variation, payload, options) {
let event = {
action,
variation,
payload,
sessionId: _sessionId,
messagingServer: _messagingServer,
p2p: _p2p,
partnerId: _apiKey,
connectionId: _connectionId
};
if (options) {
event = assign(options, event);
}
analytics.logEvent(event);
};
this.logConfigurationFileEvent = function (variation, payload, options) {
if (payload === void 0) {
payload = null;
}
if (options === void 0) {
options = {};
}
if (variation === 'Attempt') {
_configurationAttemptStartTime = new Date().getTime();
} else if (variation === 'Failure' || variation === 'Success') {
const attemptDuration = new Date().getTime() - _configurationAttemptStartTime;
assign({
attemptDuration
}, options);
}
if (proxyUrl) {
options.proxyUrl = proxyUrl;
}
this.logEvent('ConfigurationFile', variation, payload, (0, _extends2.default)({}, options));
};
this.logConnectivityEvent = function (variation, payload, options) {
if (payload === void 0) {
payload = null;
}
if (options === void 0) {
options = {};
}
if (proxyUrl) {
options.proxyUrl = proxyUrl;
}
if (variation === 'Attempt') {
if (_connectivityAttemptPinger) {
_connectivityAttemptPinger.stop();
logging.error('_connectivityAttemptPinger should have been cleaned up');
}
_attemptStartTime = new Date().getTime();
_connectivityAttemptPinger = new IntervalRunner(() => {
this.logEvent('Connect', 'Attempting', payload, (0, _extends2.default)({}, options));
}, 1 / 5, 6);
_connectivityAttemptPinger.start();
}
if (variation === 'Failure' || variation === 'Success' || variation === 'Cancel') {
const logConnect = _iceConfig => {
if (_connectivityAttemptPinger) {
_connectivityAttemptPinger.stop();
_connectivityAttemptPinger = undefined;
}
this.logEvent('Connect', variation, payload, (0, _extends2.default)({}, options, {
attemptDuration: new Date().getTime() - _attemptStartTime,
iceConfig: _iceConfig,
ipWhitelist
}));
};
if (variation === 'Success') {
this._.getIceConfig().then(config => {
const _iceConfig = {
includeServers: iceConfig && iceConfig.includeServers || 'all',
transportPolicy: config.transportPolicy
};
_iceConfig.servers = config.servers ? config.servers.map(server => ({
url: server.urls
})) : [];
logConnect(_iceConfig);
});
} else {
logConnect();
}
} else {
this.logEvent('Connect', variation, payload, options);
}
};
/**
* Connects to an OpenTok session.
* * Upon a successful connection, the completion handler (the second parameter of the method) is * invoked without an error object passed in. (If there is an error connecting, the completion * handler is invoked with an error object.) Make sure that you have successfully connected to the * session before calling other methods of the Session object. *
*
* The Session object dispatches a connectionCreated
event when any client
* (including your own) connects to the session.
*
* The following code initializes a session and sets up an event listener for when the session * connects: *
** var apiKey = ""; // Replace with your API key. See https://tokbox.com/account * var sessionID = ""; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/. * var token = ""; // Replace with a generated token. * // See https://tokbox.com/developer/guides/create-token/. * * var session = OT.initSession(apiKey, sessionID); * session.connect(token, function(error) { * if (error) { * console.log(error.message); * } else { * // You have connected to the session. You could publish a stream now. * } * }); **
* *
* exception
(ExceptionEvent) Dispatched
* by the OT class locally in the event of an error.
*
* connectionCreated
(ConnectionEvent)
* Dispatched by the Session object on all clients connected to the session.
*
* sessionConnected
(SessionConnectEvent)
* Dispatched locally by the Session object when the connection is established. However,
* you can pass a completion handler function in as the second parameter of the
* connect()
and use this function instead of a listener for the
* sessionConnected
event.
*
connect()
method succeeds or fails. This function takes one parameter —
* error
(see the Error object).
* On success, the completionHandler
function is not passed any
* arguments. On error, the function is passed an error
object parameter
* (see the Error object). The
* error
object has two properties: code
(an integer) and
* message
(a string), which identify the cause of the failure. The following
* code adds a completionHandler
when calling the connect()
method:
* * session.connect(token, function (error) { * if (error) { * console.log(error.message); * } else { * console.log("Connected to session."); * } * }); **
* Note that upon connecting to the session, the Session object dispatches a
* sessionConnected
event in addition to calling the completionHandler
.
* The SessionConnectEvent object, which defines the sessionConnected
event,
* includes connections
and streams
properties, which
* list the connections and streams in the session when you connect.
*
* Calling the disconnect()
method ends your connection with the session. In the
* course of terminating your connection, it also ceases publishing any stream(s) you were
* publishing.
*
* Session objects on remote clients dispatch streamDestroyed
events for any
* stream you were publishing. The Session object dispatches a sessionDisconnected
* event locally. The Session objects on remote clients dispatch connectionDestroyed
* events, letting other connections know you have left the session. The
* {@link SessionDisconnectEvent} and {@link StreamEvent} objects that define the
* sessionDisconnect
and connectionDestroyed
events each have a
* reason
property. The reason
property lets the developer determine
* whether the connection is being terminated voluntarily and whether any streams are being
* destroyed as a byproduct of the underlying connection's voluntary destruction.
*
* If the session is not currently connected, calling this method causes a warning to be logged. * See OT.setLogLevel(). *
* *
* Note: If you intend to reuse a Publisher object created using
* OT.initPublisher()
to publish to different sessions sequentially, call either
* Session.disconnect()
or Session.unpublish()
. Do not call both.
* Then call the preventDefault()
method of the Publisher's streamDestroyed
* event object to prevent the Publisher object from being removed from the page. Be sure to
* call preventDefault()
only if the connection.connectionId
property
* of the Stream object in the event matches the connection.connectionId
property of
* your Session object (to ensure that you are preventing the default behavior for your published
* streams, not for other streams that you subscribe to).
*
* sessionDisconnected
* (SessionDisconnectEvent)
* Dispatched locally when the connection is disconnected.
*
* connectionDestroyed
(ConnectionEvent)
* Dispatched on other clients, along with the streamDestroyed
event (as warranted).
*
* streamDestroyed
(StreamEvent)
* Dispatched on other clients if streams are lost as a result of the session disconnecting.
*
publish()
method starts publishing an audio-video stream to the session.
* The audio-video stream is captured from a local microphone and webcam. Upon successful
* publishing, the Session objects on all connected clients dispatch the
* streamCreated
event.
*
*
*
* You pass a Publisher object as the one parameter of the method. You can initialize a
* Publisher object by calling the OT.initPublisher()
* method. Before calling Session.publish()
.
*
This method takes an alternate form: publish([targetElement:String,
* properties:Object]):Publisher
In this form, you do not pass a Publisher
* object into the function. Instead, you pass in a targetElement
(the ID of the
* DOM element that the Publisher will replace) and a properties
object that
* defines options for the Publisher (see OT.initPublisher().)
* The method returns a new Publisher object, which starts sending an audio-video stream to the
* session. The remainder of this documentation describes the form that takes a single Publisher
* object as a parameter.
*
*
* A local display of the published stream is created on the web page by replacing * the specified element in the DOM with a streaming video display. The video stream * is automatically mirrored horizontally so that users see themselves and movement * in their stream in a natural way. If the width and height of the display do not match * the 4:3 aspect ratio of the video signal, the video stream is cropped to fit the * display. *
* ** If calling this method creates a new Publisher object and the OpenTok library does not * have access to the camera or microphone, the web page alerts the user to grant access * to the camera and microphone. *
* *
* The OT object dispatches an exception
event if the user's role does not
* include permissions required to publish. For example, if the user's role is set to subscriber,
* then they cannot publish. You define a user's role when you create the user token
* (see Token creation overview).
* You pass the token string as a parameter of the connect()
method of the Session
* object. See ExceptionEvent and
* OT.on().
*
* The application throws an error if the session is not connected. *
* *
* exception
(ExceptionEvent) Dispatched
* by the OT object. This can occur when user's role does not allow publishing (the
* code
property of event object is set to 1500); it can also occur if the
* connection fails to connect (the code
property of event object is set to 1013).
* WebRTC is a peer-to-peer protocol, and it is possible that connections will fail to connect.
* The most common cause for failure is a firewall that the protocol cannot traverse.
*
* streamCreated
(StreamEvent)
* The stream has been published. The Session object dispatches this on all clients
* subscribed to the stream, as well as on the publisher's client.
*
* The following example publishes a video once the session connects: *
** var apiKey = ""; // Replace with your API key. See https://tokbox.com/account * var sessionId = ""; // Replace with your own session ID. * // https://tokbox.com/developer/guides/create-session/. * var token = ""; // Replace with a generated token that has been assigned the publish role. * // See https://tokbox.com/developer/guides/create-token/. * var session = OT.initSession(apiKey, sessionID); * session.connect(token, function(error) { * if (error) { * console.log(error.message); * } else { * var publisherOptions = {width: 400, height:300, name:"Bob's stream"}; * // This assumes that there is a DOM element with the ID 'publisher': * publisher = OT.initPublisher('publisher', publisherOptions); * session.publish(publisher); * } * }); ** * @param {Publisher} publisher A Publisher object, which you initialize by calling the * OT.initPublisher() method. * * @param {Function} completionHandler (Optional) A function to be called when the call to the *
publish()
method succeeds or fails. This function takes one parameter —
* error
. On success, the completionHandler
function is not passed any
* arguments. On error, the function is passed an error
object parameter
* (see the Error object). The
* error
object has two properties: code
(an integer) and
* message
(a string), which identify the cause of the failure. Calling
* publish()
fails if the role assigned to your token is not "publisher" or
* "moderator"; in this case the error.name
property is set to
* "OT_PERMISSION_DENIED"
. Calling publish()
also fails if the
* client fails to connect; in this case the error.name
property is set to
* "OT_NOT_CONNECTED"
. The following code adds a completion handler when
* calling the publish()
method:
* * session.publish(publisher, null, function (error) { * if (error) { * console.log(error.message); * } else { * console.log("Publishing a stream."); * } * }); ** * @returns The Publisher object for this stream. * * @method #publish * @memberOf Session */ this.publish = function (publisher, properties, completionHandler) { if (typeof publisher === 'function') { completionHandler = publisher; publisher = undefined; } if (typeof properties === 'function') { completionHandler = properties; properties = undefined; } completionHandler = completionHandler || function () {}; if (this.isNot('connected')) { analytics.logError(1010, 'OT.exception', 'We need to be connected before you can publish', null, { action: 'Publish', variation: 'Failure', failureReason: 'unconnected', failureCode: ExceptionCodes.NOT_CONNECTED, failureMessage: 'We need to be connected before you can publish', sessionId: _sessionId, streamId: publisher && publisher.stream ? publisher.stream.id : null, p2p: this.sessionInfo ? this.sessionInfo.p2pEnabled : undefined, messagingServer: this.sessionInfo ? this.sessionInfo.messagingServer : null, partnerId: _apiKey }); dispatchOTError(otError(errors.NOT_CONNECTED, new Error('We need to be connected before you can publish'), ExceptionCodes.NOT_CONNECTED), completionHandler); return null; } if (!permittedTo('publish')) { const errorMessage = 'This token does not allow publishing. The role must be at least ' + '`publisher` to enable this functionality'; const options = { failureReason: 'Permission', failureCode: ExceptionCodes.UNABLE_TO_PUBLISH, failureMessage: errorMessage }; this.logEvent('Publish', 'Failure', null, options); dispatchOTError(otError(errors.PERMISSION_DENIED, new Error(errorMessage), ExceptionCodes.UNABLE_TO_PUBLISH), completionHandler); return null; } // If the user has passed in an ID of a element then we create a new publisher. if (!publisher || typeof publisher === 'string' || OTHelpers.isElementNode(publisher)) { // Initiate a new Publisher with the new session credentials publisher = initPublisher(publisher, properties); } else if (publisher instanceof Publisher) { // If the publisher already has a session attached to it we can if ('session' in publisher && publisher.session && 'sessionId' in publisher.session) { // send a warning message that we can't publish again. if (publisher.session.sessionId === this.sessionId) { _logging.warn("Cannot publish " + publisher.guid() + " again to " + this.sessionId + ". Please call session.unpublish(publisher) first."); } else { _logging.warn("Cannot publish " + publisher.guid() + " publisher already attached to " + publisher.session.sessionId + ". Please call session.unpublish(publisher) first."); } } } else { dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('Session.publish :: First parameter passed in is neither a ' + 'string nor an instance of the Publisher'), ExceptionCodes.UNABLE_TO_PUBLISH), completionHandler); return undefined; } // Add publisher reference to the session publisher._.publishToSession(this, analytics).catch(err => { err.message = "Session.publish :: " + err.message; _logging.error(err.code, err.message); throw err; }).then(() => publisher).then(function () { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return completionHandler(null, ...args); }, err => completionHandler(err)); // return the embed publisher return publisher; }; /** * Ceases publishing the specified publisher's audio-video stream * to the session. By default, the local representation of the audio-video stream is * removed from the web page. Upon successful termination, the Session object on every * connected web page dispatches * a
streamDestroyed
event.
*
*
*
* To prevent the Publisher from being removed from the DOM, add an event listener for the
* streamDestroyed
event dispatched by the Publisher object and call the
* preventDefault()
method of the event object.
*
* Note: If you intend to reuse a Publisher object created using
* OT.initPublisher()
to publish to different sessions sequentially, call
* either Session.disconnect()
or Session.unpublish()
. Do not call
* both. Then call the preventDefault()
method of the streamDestroyed
* or sessionDisconnected
event object to prevent the Publisher object from being
* removed from the page. Be sure to call preventDefault()
only if the
* connection.connectionId
property of the Stream object in the event matches the
* connection.connectionId
property of your Session object (to ensure that you are
* preventing the default behavior for your published streams, not for other streams that you
* subscribe to).
*
* streamDestroyed
(StreamEvent)
* The stream associated with the Publisher has been destroyed. Dispatched on by the
* Publisher on on the Publisher's browser. Dispatched by the Session object on
* all other connections subscribing to the publisher's stream.
*
* <script> * var apiKey = ""; // Replace with your API key. See https://tokbox.com/account * var sessionID = ""; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/. * var token = ""; // Replace with a generated token. * // See https://tokbox.com/developer/guides/create-token/. * var publisher; * var session = OT.initSession(apiKey, sessionID); * session.connect(token, function(error) { * if (error) { * console.log(error.message); * } else { * // This assumes that there is a DOM element with the ID 'publisher': * publisher = OT.initPublisher('publisher'); * session.publish(publisher); * } * }); * * function unpublish() { * session.unpublish(publisher); * } * </script> * * <body> * * <div id="publisherContainer/> * <br/> * * <a href="javascript:unpublish()">Stop Publishing</a> * * </body> * ** * @see publish() * * @see streamDestroyed event * * @param {Publisher} publisher The Publisher object to stop streaming. * * @method #unpublish * @memberOf Session */ this.unpublish = function (publisher) { if (!publisher) { _logging.error('OT.Session.unpublish: publisher parameter missing.'); return; } // Unpublish the localMedia publisher publisher._.unpublishFromSession(this, 'unpublished'); }; /** * Subscribes to a stream that is available to the session. You can get an array of * available streams from the
streams
property of the sessionConnected
* and streamCreated
events (see
* SessionConnectEvent and
* StreamEvent).
*
* * The subscribed stream is displayed on the local web page by replacing the specified element * in the DOM with a streaming video display. If the width and height of the display do not * match the 4:3 aspect ratio of the video signal, the video stream is cropped to fit * the display. If the stream lacks a video component, a blank screen with an audio indicator * is displayed in place of the video stream. *
* *
* The application throws an error if the session is not connected or if the
* targetElement
does not exist in the HTML DOM.
*
* var apiKey = ""; // Replace with your API key. See https://tokbox.com/account * var sessionID = ""; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/. * var token = ""; // Replace with a generated token. * // See https://tokbox.com/developer/guides/create-token/. * * var session = OT.initSession(apiKey, sessionID); * session.on("streamCreated", function(event) { * subscriber = session.subscribe(event.stream, targetElement); * }); * session.connect(token); ** * @param {Stream} stream The Stream object representing the stream to which we are trying to * subscribe. * * @param {Object} targetElement (Optional) The DOM element or the
id
attribute of
* the existing DOM element used to determine the location of the Subscriber video in the HTML
* DOM. See the insertMode
property of the properties
parameter. If
* you do not specify a targetElement
, the application appends a new DOM element
* to the HTML body
.
*
* @param {Object} properties This is an object that contains the following properties:
* audioVolume
(Number) The desired audio volume, between 0 and
* 100, when the Subscriber is first opened (default: 50). After you subscribe to the
* stream, you can adjust the volume by calling the
* setAudioVolume()
method of the
* Subscriber object. This volume setting affects local playback only; it does not affect
* the stream's volume on other clients.fitMode
(String) Determines how the video is displayed if the its
* dimensions do not match those of the DOM element. You can set this property to one of
* the following values:
* *
"cover"
— The video is cropped if its dimensions do not match
* those of the DOM element. This is the default setting for videos that have a
* camera as the source (for Stream objects with the videoType
property
* set to "camera"
).
* "contain"
— The video is letterboxed if its dimensions do not
* match those of the DOM element. This is the default setting for screen-sharing
* videos (for Stream objects with the videoType
property set to
* "screen"
).
* height
(Number or String) The desired initial height of the displayed
* video in the HTML page (default: 198 pixels). You can specify the number of pixels as
* either a number (such as 300) or a string ending in "px" (such as "300px"). Or you can
* specify a percentage of the size of the parent element, with a string ending in "%"
* (such as "100%"). Note: To resize the video, adjust the CSS of the subscriber's
* DOM element (the element
property of the Subscriber object) or (if the
* height is specified as a percentage) its parent DOM element (see
*
* Resizing or repositioning a video).
* true
, the default) or not (false
). The default UI element
* contains user interface controls, a video loading indicator, and automatic video cropping
* or letterboxing, in addition to the video. (If you leave insertDefaultUI
* set to true
, you can control individual UI settings using the
* fitMode
, showControls
, and style
options.)
*
* If you set this option to false
, OpenTok.js does not insert a default UI
* element in the HTML DOM, and the element
property of the Subscriber object is
* undefined. The Subscriber object dispatches a
* videoElementCreated event when
* the video
element (or in Internet Explorer the object
element
* containing the video) is created. The element
property of the event object
* is a reference to the Subscriber's video
(or object
) element.
* Add it to the HTML DOM to display the video.
*
* Set this option to false
if you want to move the Publisher's
* video
element (or its object
element in Internet Explorer) in
* the HTML DOM.
*
* If you set this to false
, do not set the targetElement
* parameter. (This results in an error passed into to the OT.initPublisher()
* callback function.) To add the video to the HTML DOM, add an event listener for the
* videoElementCreated
event, and then add the element
property of
* the event object into the HTML DOM.
*
insertMode
(String) Specifies how the Subscriber object will
* be inserted in the HTML DOM. See the targetElement
parameter. This
* string can have the following values:
* *
"replace"
The Subscriber object replaces contents of the
* targetElement. This is the default."after"
The Subscriber object is a new element inserted
* after the targetElement in the HTML DOM. (Both the Subscriber and targetElement
* have the same parent element.)"before"
The Subscriber object is a new element inserted
* before the targetElement in the HTML DOM. (Both the Subscriber and targetElement
* have the same parent element.)"append"
The Subscriber object is a new element added as a
* child of the targetElement. If there are other child elements, the Subscriber is
* appended as the last child element of the targetElement.preferredFrameRate
(Number) The preferred frame rate of the subscriber's
* video. Lowering the preferred frame rate lowers video quality on the subscribing client,
* but it also reduces network and CPU usage. You may want to use a lower frame rate for
* subscribers to a stream that is less important than other streams.
* * This property only applies when subscribing to a stream that uses the * * scalable video feature. Scalable video is available: *
* In streams that do not use scalable video, setting this property has no effect. *
* Note: The frame rate for scalable video streams automatically adjusts for each * subscriber, based on network conditions and CPU usage, even if you do not call this method. * Call this method if you want to set a maximum frame rate for this subscriber. *
*
* Not every frame rate is available to a subscriber. When you set the preferred frame rate for
* the subscriber, OpenTok.js picks the best frame rate available that matches your setting.
* The frame rates available are based on the value of the Subscriber object's
* stream.frameRate
property, which represents the maximum value available for the
* stream. The actual frame rates available depend, dynamically, on network and CPU resources
* available to the publisher and subscriber.
*
* You can dynamically change the preferred frame rate used by calling the
* setPreferredFrameRate()
method of the Subscriber object.
*
preferredResolution
(Object) The preferred resolution of the subscriber's
* video. Set this to an object with two properties: width
and height
* (both numbers), such as {width: 320, height: 240}
. Lowering the preferred video
* resolution lowers video quality on the subscribing client, but it also reduces network and CPU
* usage. You may want to use a lower resolution based on the dimensions of subscriber's video on
* the web page. You may want to use a resolution for subscribers to a stream that is less
* important (and smaller) than other streams.
* * This property only applies when subscribing to a stream that uses the * * scalable video feature. Scalable video is available: *
* In streams that do not use scalable video, setting this property has no effect. *
* Not every resolution is available to a subscriber. When you set the preferred resolution,
* OpenTok.js and the video encoder pick the best resolution available that matches your
* setting. The resolutions available depend on the resolution of the published stream.
* The Subscriber object's stream.resolution
property represents the highest
* resolution available for the stream. Each of the resolutions available for a stream will use
* the same aspect ratio. The actual resolutions available depend, dynamically, on network
* and CPU resources available to the publisher and subscriber.
*
* You can dynamically change the preferred video resolution used by calling the
* setPreferredResolution()
method of the Subscriber object.
*
showControls
(Boolean) Whether to display the built-in user interface
* controls for the Subscriber (default: true
). These controls include the name
* display, the audio level indicator, the speaker control button, the video disabled indicator,
* and the video disabled warning icon. You can turn off all user interface controls by setting
* this property to false
. You can control the display of individual user interface
* controls by leaving this property set to true
(the default) and setting
* individual properties of the style
property.
* style
(Object) An object containing properties that define the initial
* appearance of user interface controls of the Subscriber. The style
object
* includes the following properties:
* audioBlockedDisplayMode
(String) — Whether to display
* the default audio blocked icon in Subscribers (in browsers where audio
* autoplay is blocked). Possible values are: "auto"
(the default,
* icon is displayed when the audio is disabled) and "off"
(the icon
* is not displayed). Set this to "off"
if you want to display
* your own UI element showing that the audio is blocked. In response to an
* HTML element dispatching a click
event, you can call the
* OT.unblockAudio() method to start audio
* playback in this and all other blocked subscribers.audioLevelDisplayMode
(String) — How to display the audio level
* indicator. Possible values are: "auto"
(the indicator is displayed when the
* video is disabled), "off"
(the indicator is not displayed), and
* "on"
(the indicator is always displayed).backgroundImageURI
(String) — A URI for an image to display as
* the background image when a video is not displayed. (A video may not be displayed if
* you call subscribeToVideo(false)
on the Subscriber object). You can pass an
* http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the
* data
URI scheme (instead of http or https) and pass in base-64-encrypted
* PNG data, such as that obtained from the
* Subscriber.getImgData() method. (For example,
* you could set the property to a value returned by calling getImgData()
on
* a previous Subscriber object.) If the URL or the image data is invalid, the
* property is ignored (the attempt to set the image fails silently).buttonDisplayMode
(String) — How to display the speaker controls
* Possible values are: "auto"
(controls are displayed when the stream is first
* displayed and when the user mouses over the display), "off"
(controls are not
* displayed), and "on"
(controls are always displayed).nameDisplayMode
(String) Whether to display the stream name.
* Possible values are: "auto"
(the name is displayed when the stream is first
* displayed and when the user mouses over the display), "off"
(the name is not
* displayed), and "on"
(the name is always displayed).videoDisabledDisplayMode
(String) Whether to display the video
* disabled indicator and video disabled warning icons for a Subscriber. These icons
* indicate that the video has been disabled (or is in risk of being disabled for
* the warning icon) due to poor stream quality. This style only applies to the Subscriber
* object. Possible values are: "auto"
(the icons are automatically when the
* displayed video is disabled or in risk of being disabled due to poor stream quality),
* "off"
(do not display the icons), and "on"
(display the
* icons). The default setting is "auto"
subscribeToAudio
(Boolean) Whether to initially subscribe to audio
* (if available) for the stream (default: true
).subscribeToVideo
(Boolean) Whether to initially subscribe to video
* (if available) for the stream (default: true
).testNetwork
(Boolean) Whether, when subscribing to a stream
* published by the local client, you want to have the stream come from the OpenTok Media
* Router (true
) or if you want the DOM to simply to display the local camera's
* video (false
). Set this to true
when you want to use the
* Subscriber.getStats() method to check statistics
* for a stream you publish. This setting only applies to streams published by the local
* client in a session that uses the OpenTok Media Router (sessions with the
* media mode
* set to routed), not in sessions with the media mode set to relayed. The default value is
* false
.width
(Number or String) The desired initial width of the displayed
* video in the HTML page (default: 264 pixels). You can specify the number of pixels as
* either a number (such as 400) or a string ending in "px" (such as "400px"). Or you can
* specify a percentage of the size of the parent element, with a string ending in "%"
* (such as "100%"). Note: To resize the video, adjust the CSS of the subscriber's
* DOM element (the element
property of the Subscriber object) or (if the
* width is specified as a percentage) its parent DOM element (see
*
* Resizing or repositioning a video).
* subscribe()
method succeeds or fails. This function takes one parameter —
* error
. On success, the completionHandler
function is not passed any
* arguments. On error, the function is passed an error
object, defined by the
* Error class, has two properties: code
(an integer) and
* message
(a string), which identify the cause of the failure. The following
* code adds a completionHandler
when calling the subscribe()
method:
* * session.subscribe(stream, "subscriber", null, function (error) { * if (error) { * console.log(error.message); * } else { * console.log("Subscribed to stream: " + stream.id); * } * }); ** * @signature subscribe(stream, targetElement, properties, completionHandler) * @returns {Subscriber} The Subscriber object for this stream. Stream control functions * are exposed through the Subscriber object. * @method #subscribe * @memberOf Session */ this.subscribe = function (stream, targetElement, properties, completionHandler) { if (typeof targetElement === 'function') { completionHandler = targetElement; targetElement = undefined; properties = undefined; } if (typeof properties === 'function') { completionHandler = properties; properties = undefined; } completionHandler = completionHandler || function () {}; if (!this.connection || !this.connection.connectionId) { dispatchOTError(otError(errors.NOT_CONNECTED, new Error('Session.subscribe :: Connection required to subscribe'), ExceptionCodes.UNABLE_TO_SUBSCRIBE), completionHandler); return undefined; } if (!stream) { dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('Session.subscribe :: stream cannot be null'), ExceptionCodes.UNABLE_TO_SUBSCRIBE), completionHandler); return undefined; } if (!Object.prototype.hasOwnProperty.call(stream, 'streamId')) { dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('Session.subscribe :: invalid stream object'), ExceptionCodes.UNABLE_TO_SUBSCRIBE), completionHandler); return undefined; } if (properties && properties.insertDefaultUI === false && targetElement) { dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('You cannot specify a target element if insertDefaultUI is false'), ExceptionCodes.INVALID_PARAMETER), completionHandler); return undefined; } if (targetElement && targetElement.insertDefaultUI === false) { // You can omit the targetElement property if you set insertDefaultUI to false properties = targetElement; targetElement = undefined; } const subscriber = new Subscriber(targetElement, assign(properties || {}, { stream, session: this, // @todo this needs to go. analytics }), err => { if (err) { dispatchOTError(err, completionHandler); return; } completionHandler(null, subscriber); }); sessionObjects.subscribers.add(subscriber); return subscriber; }; /** * Stops subscribing to a stream in the session. the display of the audio-video stream is * removed from the local web page. * *
* The following code subscribes to other clients' streams. For each stream, the code also * adds an Unsubscribe link. *
** var apiKey = ""; // Replace with your API key. See See https://tokbox.com/account * var sessionID = ""; // Replace with your own session ID. * // See https://tokbox.com/developer/guides/create-session/. * var token = ""; // Replace with a generated token. * // See https://tokbox.com/developer/guides/create-token/. * var streams = []; * * var session = OT.initSession(apiKey, sessionID); * session.on("streamCreated", function(event) { * var stream = event.stream; * displayStream(stream); * }); * session.connect(token); * * function displayStream(stream) { * var div = document.createElement('div'); * div.setAttribute('id', 'stream' + stream.streamId); * * var subscriber = session.subscribe(stream, div); * subscribers.push(subscriber); * * var aLink = document.createElement('a'); * aLink.setAttribute('href', 'javascript: unsubscribe("' + subscriber.id + '")'); * aLink.innerHTML = "Unsubscribe"; * * var streamsContainer = document.getElementById('streamsContainer'); * streamsContainer.appendChild(div); * streamsContainer.appendChild(aLink); * * streams = event.streams; * } * * function unsubscribe(subscriberId) { * console.log("unsubscribe called"); * for (var i = 0; i < subscribers.length; i++) { * var subscriber = subscribers[i]; * if (subscriber.id == subscriberId) { * session.unsubscribe(subscriber); * } * } * } ** * @param {Subscriber} subscriber The Subscriber object to unsubcribe. * * @see subscribe() * * @method #unsubscribe * @memberOf Session */ this.unsubscribe = function (subscriber) { if (!subscriber) { const errorMsg = 'OT.Session.unsubscribe: subscriber cannot be null'; _logging.error(errorMsg); throw new Error(errorMsg); } if (!subscriber.stream) { _logging.warn('OT.Session.unsubscribe:: tried to unsubscribe a subscriber that had no stream'); return false; } _logging.debug("OT.Session.unsubscribe: subscriber " + subscriber.id); subscriber._destroy({ reason: 'Unsubscribe' }); return true; }; /** * Returns an array of local Subscriber objects for a given stream. * * @param {Stream} stream The stream for which you want to find subscribers. * * @returns {Array} An array of {@link Subscriber} objects for the specified stream. * * @see unsubscribe() * @see Subscriber * @see StreamEvent * @method #getSubscribersForStream * @memberOf Session */ this.getSubscribersForStream = function (stream) { return sessionObjects.subscribers.where({ streamId: stream.id }); }; /** * Returns the local Publisher object for a given stream. * * @param { Stream } stream The stream for which you want to find the Publisher. * * @returns { Publisher } A Publisher object for the specified stream. Returns *
null
if there is no local Publisher object
* for the specified stream.
*
* @see forceUnpublish()
* @see Subscriber
* @see StreamEvent
*
* @method #getPublisherForStream
* @memberOf Session
*/
this.getPublisherForStream = function (stream) {
let streamId;
let errorMsg;
if (typeof stream === 'string') {
streamId = stream;
} else if (typeof stream === 'object' && stream && Object.hasOwnProperty.call(stream, 'id')) {
streamId = stream.id;
} else {
errorMsg = 'Session.getPublisherForStream :: Invalid stream type';
_logging.error(errorMsg);
throw new Error(errorMsg);
}
return sessionObjects.publishers.where({
streamId
})[0];
}; // Private Session API: for internal OT use only
this._ = {
getProxyUrl() {
return proxyUrl;
},
getSocket() {
return _socket;
},
reconnecting: function () {
this.dispatchEvent(new Events.SessionReconnectingEvent());
}.bind(this),
reconnected: function () {
this.dispatchEvent(new Events.SessionReconnectedEvent());
if (this.sessionInfo.renegotiation) {
sessionObjects.publishers.where({
session: this
}).forEach(publisher => {
publisher._.iceRestart();
});
sessionObjects.subscribers.where({
session: this
}).forEach(subscriber => {
subscriber._.iceRestart();
});
}
}.bind(this),
// session.on("signal", function(SignalEvent))
// session.on("signal:{type}", function(SignalEvent))
dispatchSignal: function (fromConnection, type, data) {
const event = new Events.SignalEvent(type, data, fromConnection);
event.target = this; // signal a "signal" event
// NOTE: trigger doesn't support defaultAction, and therefore preventDefault.
this.trigger(eventNames.SIGNAL, event); // signal an "signal:{type}" event" if there was a custom type
if (type) {
this.dispatchEvent(event);
}
}.bind(this),
subscriberChannelUpdate(stream, subscriber, channel, attributes) {
if (!_socket) {
_logging.warn('You are disconnected, cannot update subscriber properties ', attributes);
return null;
}
return _socket.subscriberChannelUpdate(stream.id, subscriber.widgetId, channel.id, attributes);
},
streamCreate(name, streamId, audioFallbackEnabled, channels, minBitrate, completion) {
if (!_socket) {
_logging.warn('You are disconnected, cannot create stream ', streamId);
return;
}
_socket.streamCreate(name, streamId, audioFallbackEnabled, channels, minBitrate, undefined, // Do not expose maxBitrate to the end user
completion);
},
streamDestroy(streamId) {
if (!_socket) {
_logging.warn('You are disconnected, cannot destroy stream ', streamId);
return;
}
_socket.streamDestroy(streamId);
},
streamChannelUpdate(stream, channel, attributes) {
if (!_socket) {
_logging.warn('You are disconnected, cannot update stream properties ', attributes);
return;
}
_socket.streamChannelUpdate(stream.id, channel.id, attributes);
},
// allow these variables to be overridden in unit tests
// it's dirty, but I figure it can be cleaned up when we implement proper DI for our unit tests
setSocket(newSocket) {
_socket = newSocket;
},
setLogging(newLogging) {
_logging = newLogging;
},
setState,
setIceServers(iceServers) {
if (!iceServers) {
return;
}
_iceServerDetails = {
iceServers: adaptIceServers(iceServers),
timestamp: Date.now()
};
},
getOtIceServerInfo() {
const timeElapsed = !_iceServerDetails ? Infinity : Date.now() - _iceServerDetails.timestamp;
const validDuration = 24 * 60 * 60 * 1000; // 24 hours
const validTimeRemaining = validDuration - timeElapsed;
const fiveMinutes = 5 * 60 * 1000;
if (validTimeRemaining > fiveMinutes) {
return Promise.resolve({
transportPolicy: _session && _session.sessionInfo && _session.sessionInfo.clientCandidates,
servers: _iceServerDetails && _iceServerDetails.iceServers
});
}
if (!_token) {
// @todo why would this happen before connect() where a token is set?
// Need a token for getting ice servers from gsi
return Promise.resolve({
transportPolicy: _session && _session.sessionInfo && _session.sessionInfo.clientCandidates,
servers: [],
needRumorIceServersFallback: true
});
}
const clientVersion = localStaticConfig.clientVersion;
return getSessionInfo({
anvilUrl: (this.staticConfig || localStaticConfig).apiUrl,
sessionId,
token: _token,
connectionId: _connectionId,
clientVersion
}).then(sessionInfo => {
_session._.setIceServers(sessionInfo.iceServers);
if (!_iceServerDetails) {
// No ice servers provided by gsi
return {
transportPolicy: _session.sessionInfo.clientCandidates,
servers: [],
needRumorIceServersFallback: true
};
}
return {
transportPolicy: _session && _session.sessionInfo && _session.sessionInfo.clientCandidates,
servers: _iceServerDetails && _iceServerDetails.iceServers
};
});
},
getCodecFlags: () => ({
h264: _session.sessionInfo.h264,
vp9: _session.sessionInfo.vp9,
vp8: _session.sessionInfo.vp8
}),
getVideoCodecsCompatible: webRTCStream => testRemovingVideoCodecs({
RTCPeerConnection: windowMock.RTCPeerConnection,
env: OTHelpers.env,
stream: webRTCStream,
codecFlags: _session._.getCodecFlags()
}),
getIceConfig: () => {
if (!iceConfig) {
return _session._.getOtIceServerInfo();
}
const transportPolicy = (() => {
if (iceConfig && iceConfig.transportPolicy === 'relay') {
return 'relay';
}
return _session.sessionInfo.clientCandidates;
})();
const otIceServersInfoPromise = iceConfig.includeServers === 'custom' ? Promise.resolve({
servers: []
}) : _session._.getOtIceServerInfo();
return otIceServersInfoPromise.then(otIceServerInfo => assign(otIceServerInfo, {
transportPolicy,
servers: [...otIceServerInfo.servers, ...iceConfig.customServers]
}));
},
privateEvents: new EventEmitter()
};
/**
* Sends a signal to each client or a specified client in the session. Specify a
* to
property of the signal
parameter to limit the signal to
* be sent to a specific client; otherwise the signal is sent to each client connected to
* the session.
* * The following example sends a signal of type "foo" with a specified data payload ("hello") * to all clients connected to the session: *
* session.signal({ * type: "foo", * data: "hello" * }, * function(error) { * if (error) { * console.log("signal error: " + error.message); * } else { * console.log("signal sent"); * } * } * ); **
* Calling this method without specifying a recipient client (by setting the to
* property of the signal
parameter) results in multiple signals sent (one to each
* client in the session). For information on charges for signaling, see the
* OpenTok pricing page.
*
* The following example sends a signal of type "foo" with a data payload ("hello") to a * specific client connected to the session: *
* session.signal({ * type: "foo", * to: recipientConnection; // a Connection object * data: "hello" * }, * function(error) { * if (error) { * console.log("signal error: " + error.message); * } else { * console.log("signal sent"); * } * } * ); **
* Add an event handler for the signal
event to listen for all signals sent in
* the session. Add an event handler for the signal:type
event to listen for
* signals of a specified type only (replace type
, in signal:type
,
* with the type of signal to listen for). The Session object dispatches these events. (See
* events.)
*
* @param {Object} signal An object that contains the following properties defining the signal:
*
data
— (String) The data to send. The limit to the length of data
* string is 8kB. Do not set the data string to null
or
* undefined
.retryAfterReconnect
— (Boolean) Upon reconnecting to the session,
* whether to send any signals that were initiated while disconnected. If your client loses its
* connection to the OpenTok session, due to a drop in network connectivity, the client
* attempts to reconnect to the session, and the Session object dispatches a
* reconnecting
event. By default, signals initiated while disconnected are
* sent when (and if) the client reconnects to the OpenTok session. You can prevent this by
* setting the retryAfterReconnect
property to false
. (The default
* value is true
.)
* to
— (Connection) A Connection
* object corresponding to the client that the message is to be sent to. If you do not
* specify this property, the signal is sent to all clients connected to the session.type
— (String) The type of the signal. You can use the type to
* filter signals when setting an event handler for the signal:type
event
* (where you replace type
with the type string). The maximum length of the
* type
string is 128 characters, and it must contain only letters (A-Z and a-z),
* numbers (0-9), '-', '_', and '~'.Each property is optional. If you set none of the properties, you will send a signal * with no data or type to each client connected to the session.
* * @param {Function} completionHandler (Optional) A function that is called when sending the signal * succeeds or fails. This function takes one parameter —error
.
* On success, the completionHandler
function is not passed any
* arguments. On error, the function is passed an error
object, defined by the
* Error class. The error
object has the following
* properties:
*
* code
— (Number) An error code, which can be one of the following:
* 400 | One of the signal properties is invalid. | *
404 | The client specified by the to property is not connected
* to the session. |
*
413 | The type string exceeds the maximum length (128 bytes),
* or the data string exceeds the maximum size (8 kB). |
*
500 | You are not connected to the OpenTok session. | *
message
— (String) A description of the error.Note that the completionHandler
success result (error == null
)
* indicates that the options passed into the Session.signal()
method are valid
* and the signal was sent. It does not indicate that the signal was successfully
* received by any of the intended recipients.
*
* @method #signal
* @memberOf Session
* @see signal and signal:type events
*/
this.signal = function (options, completion) {
let _options = options;
let _completion = completion || function () {};
if (isFunction(_options)) {
_completion = _options;
_options = null;
}
if (this.isNot('connected')) {
const notConnectedErrorMsg = 'Unable to send signal - you are not connected to the session.';
dispatchOTError(otError(errors.NOT_CONNECTED, new Error(notConnectedErrorMsg), 500), _completion);
return;
}
function getErrorNameFromCode(code) {
switch (code) {
case 400:
case 413:
return errors.INVALID_PARAMETER;
case 429:
return errors.RATE_LIMIT_EXCEEDED;
case 404:
return errors.NOT_FOUND;
case 500:
return errors.NOT_CONNECTED;
case 403:
return errors.PERMISSION_DENIED;
case 2001:
return errors.UNEXPECTED_SERVER_RESPONSE;
default:
return undefined;
}
}
_socket.signal(_options, function (error) {
if (error) {
const errorName = getErrorNameFromCode(error.code);
if (errorName) {
error = otError(errorName, new Error(error.message), error.code);
}
_completion(error);
return;
}
for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
args[_key4 - 1] = arguments[_key4];
}
_completion(error, ...args);
}, this.logEvent);
if (options && options.data && typeof options.data !== 'string') {
_logging.warn('Signaling of anything other than Strings is deprecated. ' + 'Please update the data property to be a string.');
}
};
/**
* Forces a remote connection to leave the session.
*
*
* The forceDisconnect()
method is normally used as a moderation tool
* to remove users from an ongoing session.
*
* When a connection is terminated using the forceDisconnect()
,
* sessionDisconnected
, connectionDestroyed
and
* streamDestroyed
events are dispatched in the same way as they
* would be if the connection had terminated itself using the disconnect()
* method. However, the reason
property of a {@link ConnectionEvent} or
* {@link StreamEvent} object specifies "forceDisconnected"
as the reason
* for the destruction of the connection and stream(s).
*
* While you can use the forceDisconnect()
method to terminate your own connection,
* calling the disconnect()
method is simpler.
*
* The OT object dispatches an exception
event if the user's role
* does not include permissions required to force other users to disconnect.
* You define a user's role when you create the user token (see the
* Token creation overview).
* See ExceptionEvent and OT.on().
*
* The application throws an error if the session is not connected. *
* *
* connectionDestroyed
(ConnectionEvent)
* On clients other than which had the connection terminated.
*
* exception
(ExceptionEvent)
* The user's role does not allow forcing other user's to disconnect (event.code =
* 1530
),
* or the specified stream is not publishing to the session (event.code = 1535
).
*
* sessionDisconnected
* (SessionDisconnectEvent)
* On the client which has the connection terminated.
*
* streamDestroyed
(StreamEvent)
* If streams are stopped as a result of the connection ending.
*
connectionId
property of the Connection object).
*
* @param {Function} completionHandler (Optional) A function to be called when the call to the
* forceDiscononnect()
method succeeds or fails. This function takes one parameter
* — error
. On success, the completionHandler
function is
* not passed any arguments. On error, the function is passed an error
object
* parameter. The error
object, defined by the Error
* class, has two properties: code
(an integer)
* and message
(a string), which identify the cause of the failure.
* Calling forceDisconnect()
fails if the role assigned to your
* token is not "moderator"; in this case the error.name
property is set to
* "OT_PERMISSION_DENIED"
. The following code adds a completionHandler
* when calling the forceDisconnect()
method:
* * session.forceDisconnect(connection, function (error) { * if (error) { * console.log(error); * } else { * console.log("Connection forced to disconnect: " + connection.id); * } * }); ** * @method #forceDisconnect * @memberOf Session */ this.forceDisconnect = function (connectionOrConnectionId, completionHandler) { if (this.isNot('connected')) { const notConnectedErrorMsg = 'Cannot call forceDisconnect(). You are not ' + 'connected to the session.'; dispatchOTError(otError(errors.NOT_CONNECTED, new Error(notConnectedErrorMsg), ExceptionCodes.NOT_CONNECTED), completionHandler); return; } const connectionId = typeof connectionOrConnectionId === 'string' ? connectionOrConnectionId : connectionOrConnectionId.id; const invalidParameterErrorMsg = 'Invalid Parameter. Check that you have passed valid parameter values into the method call.'; if (!connectionId) { dispatchOTError(otError(errors.INVALID_PARAMETER, new Error(invalidParameterErrorMsg), ExceptionCodes.INVALID_PARAMETER), completionHandler); return; } const notPermittedErrorMsg = 'This token does not allow forceDisconnect. ' + 'The role must be at least `moderator` to enable this functionality'; if (!permittedTo('forceDisconnect')) { dispatchOTError(otError(errors.PERMISSION_DENIED, new Error(notPermittedErrorMsg), ExceptionCodes.UNABLE_TO_FORCE_DISCONNECT), completionHandler); return; } _socket.forceDisconnect(connectionId, function (err) { if (err) { dispatchOTError(otError(errors.INVALID_PARAMETER, new Error(invalidParameterErrorMsg), ExceptionCodes.INVALID_PARAMETER), completionHandler); } else if (completionHandler && isFunction(completionHandler)) { for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { args[_key5 - 1] = arguments[_key5]; } completionHandler(err, ...args); } }); }; /** * Forces the publisher of the specified stream to stop publishing the stream. * *
* Calling this method causes the Session object to dispatch a streamDestroyed
* event on all clients that are subscribed to the stream (including the client that is
* publishing the stream). The reason
property of the StreamEvent object is
* set to "forceUnpublished"
.
*
* The OT object dispatches an exception
event if the user's role
* does not include permissions required to force other users to unpublish.
* You define a user's role when you create the user token (see the
* Token creation overview).
* You pass the token string as a parameter of the connect()
method of the Session
* object. See ExceptionEvent and
* OT.on().
*
* exception
(ExceptionEvent)
* The user's role does not allow forcing other users to unpublish.
*
* streamDestroyed
(StreamEvent)
* The stream has been unpublished. The Session object dispatches this on all clients
* subscribed to the stream, as well as on the publisher's client.
*
forceUnpublish()
method succeeds or fails. This function takes one parameter
* — error
. On success, the completionHandler
function is
* not passed any arguments. On error, the function is passed an error
object
* parameter. The error
object, defined by the Error
* class, has two properties: code
(an integer)
* and message
(a string), which identify the cause of the failure. Calling
* forceUnpublish()
fails if the role assigned to your token is not "moderator";
* in this case the error.name
property is set to "OT_PERMISSION_DENIED"
.
* The following code adds a completion handler when calling the forceUnpublish()
* method:
* * session.forceUnpublish(stream, function (error) { * if (error) { * console.log(error); * } else { * console.log("Connection forced to disconnect: " + connection.id); * } * }); ** * @method #forceUnpublish * @memberOf Session */ this.forceUnpublish = function (streamOrStreamId, completionHandler) { if (completionHandler === void 0) { completionHandler = () => {}; } const dispatchError = err => dispatchOTError(otError(err.name, new Error(err.msg), err.code), completionHandler); const invalidParameterError = { msg: 'Invalid Parameter. Check that you have passed valid parameter values into the method call.', code: ExceptionCodes.INVALID_PARAMETER, name: errors.INVALID_PARAMETER }; const notConnectedError = { msg: 'Cannot call forceUnpublish(). You are not connected to the session.', code: ExceptionCodes.NOT_CONNECTED, name: errors.NOT_CONNECTED }; const notPermittedError = { msg: 'This token does not allow forceUnpublish. The role must be at least `moderator` to enable this ' + 'functionality', code: ExceptionCodes.UNABLE_TO_FORCE_UNPUBLISH, name: errors.PERMISSION_DENIED }; const notFoundError = { msg: 'The stream does not exist.', name: errors.NOT_FOUND }; const unexpectedError = { msg: 'An unexpected error occurred.', name: errors.UNEXPECTED_SERVER_RESPONSE, code: ExceptionCodes.UNEXPECTED_SERVER_RESPONSE }; if (!streamOrStreamId) { dispatchError(invalidParameterError); return; } if (_this.isNot('connected')) { dispatchError(notConnectedError); return; } const stream = getStream(streamOrStreamId); if (!permittedTo('forceUnpublish')) { // if this throws an error the handleJsException won't occur dispatchError(notPermittedError); return; } _socket.forceUnpublish(stream.id, err => { if (!err) { completionHandler(null); return; } if (err.code === '404') { dispatchError(notFoundError); } else if (err.code === '403') { dispatchError(notPermittedError); } else { dispatchError(unexpectedError); } }); }; this.isConnected = () => this.is('connected'); this.capabilities = new Capabilities([]); }; /** * Dispatched when an archive recording of the session starts. * * @name archiveStarted * @event * @memberof Session * @see ArchiveEvent * @see Archiving overview */ /** * Dispatched when an archive recording of the session stops. * * @name archiveStopped * @event * @memberof Session * @see ArchiveEvent * @see Archiving overview */ /** * Dispatched when a new client (including your own) has connected to the session, and for * every client in the session when you first connect. (The Session object also dispatches * a
sessionConnected
event when your local client connects.)
*
* @name connectionCreated
* @event
* @memberof Session
* @see ConnectionEvent
* @see OT.initSession()
*/
/**
* A client, other than your own, has disconnected from the session.
* @name connectionDestroyed
* @event
* @memberof Session
* @see ConnectionEvent
*/
/**
* The client has connected to an OpenTok session. This event is dispatched asynchronously
* in response to a successful call to the connect()
method of a Session
* object. Before calling the connect()
method, initialize the session by
* calling the OT.initSession()
method. For a code example and more details,
* see Session.connect().
* @name sessionConnected
* @event
* @memberof Session
* @see SessionConnectEvent
* @see Session.connect()
* @see OT.initSession()
*/
/**
* The client has disconnected from the session. This event may be dispatched asynchronously
* in response to a successful call to the disconnect()
method of the Session object.
* The event may also be disptached if a session connection is lost inadvertantly, as in the case
* of a lost network connection.
*
* The default behavior is that all Subscriber objects are unsubscribed and removed from the
* HTML DOM. Each Subscriber object dispatches a destroyed
event when the element is
* removed from the HTML DOM. If you call the preventDefault()
method in the event
* listener for the sessionDisconnect
event, the default behavior is prevented, and
* you can, optionally, clean up Subscriber objects using your own code.
*
The reason
property of the event object indicates the reason for the client
* being disconnected.
* @name sessionDisconnected
* @event
* @memberof Session
* @see Session.disconnect()
* @see Session.forceDisconnect()
* @see SessionDisconnectEvent
*/
/**
* The local client has lost its connection to an OpenTok session and is trying to reconnect.
* This results from a loss in network connectivity. If the client can reconnect to the session,
* the Session object dispatches a sessionReconnected
event. Otherwise, if the client
* cannot reconnect, the Session object dispatches a sessionDisconnected
event.
*
* In response to this event, you may want to provide a user interface notification, to let
* the user know that the app is trying to reconnect to the session and that audio-video streams
* are temporarily disconnected.
*
* @name sessionReconnecting
* @event
* @memberof Session
* @see Event
* @see sessionReconnected event
* @see sessionDisconnected event
*/
/**
* The local client has reconnected to the OpenTok session after its connection was lost
* temporarily. When the connection is lost, the Session object dispatches a
* sessionReconnecting
event, prior to the sessionReconnected
* event. If the client cannot reconnect to the session, the Session object dispatches a
* sessionDisconnected
event instead of this event.
*
* Any existing publishers and subscribers are automatically reconnected when client reconnects * and the Session object dispatches this event. *
* Any signals sent by other clients while your client was disconnected are received upon
* reconnecting. By default, signals initiated by the local client while disconnected
* (by calling the Session.signal()
method) are sent when the client reconnects
* to the OpenTok session. You can prevent this by setting the retryAfterReconnect
* property to false
in the signal
object you pass into the
* Session.signal() method.
*
* @name sessionReconnected
* @event
* @memberof Session
* @see Event
* @see sessionReconnecting event
* @see sessionDisconnected event
*/
/**
* A new stream, published by another client, has been created on this session. For streams
* published by your own client, the Publisher object dispatches a streamCreated
* event. For a code example and more details, see {@link StreamEvent}.
* @name streamCreated
* @event
* @memberof Session
* @see StreamEvent
* @see Session.publish()
*/
/**
* A stream from another client has stopped publishing to the session.
*
* The default behavior is that all Subscriber objects that are subscribed to the stream are
* unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a
* destroyed
event when the element is removed from the HTML DOM. If you call the
* preventDefault()
method in the event listener for the
* streamDestroyed
event, the default behavior is prevented and you can clean up
* a Subscriber object for the stream by calling its destroy()
method. See
* Session.getSubscribersForStream().
*
* For streams published by your own client, the Publisher object dispatches a
* streamDestroyed
event.
*
* For a code example and more details, see {@link StreamEvent}. * @name streamDestroyed * @event * @memberof Session * @see StreamEvent */ /** * Defines an event dispatched when property of a stream has changed. This can happen in * in the following conditions: *
*
videoDisabled
and videoEnabled
events in all
* conditions that cause the subscriber's stream to be disabled or enabled.
* videoDimensions
property of the Stream object has
* changed (see Stream.videoDimensions).
* videoType
property of the Stream object has changed.
* This can happen in a stream published by a mobile device. (See
* Stream.videoType.)
* data
— (String) The data string sent with the signal (if there
* is one).from
— (Connection) The Connection
* corresponding to the client that sent the signal.type
— (String) The type assigned to the signal (if there is
* one).
* You can register to receive all signals sent in the session, by adding an event handler
* for the signal
event. For example, the following code adds an event handler
* to process all signals sent in the session:
*
* session.on("signal", function(event) { * console.log("Signal sent from connection: " + event.from.id); * console.log("Signal data: " + event.data); * }); **
You can register for signals of a specfied type by adding an event handler for the
* signal:type
event (replacing type
with the actual type string
* to filter on).
*
* @name signal
* @event
* @memberof Session
* @see Session.signal()
* @see SignalEvent
* @see signal:type event
*/
/**
* A signal of the specified type was received from the session. The
* SignalEvent class defines this event object.
* It includes the following properties:
*
data
— (String) The data string sent with the signal.from
— (Connection) The Connection
* corresponding to the client that sent the signal.type
— (String) The type assigned to the signal (if there is one).
*
* You can register for signals of a specfied type by adding an event handler for the
* signal:type
event (replacing type
with the actual type string
* to filter on). For example, the following code adds an event handler for signals of
* type "foo":
*
* session.on("signal:foo", function(event) { * console.log("foo signal sent from connection " + event.from.id); * console.log("Signal data: " + event.data); * }); **
* You can register to receive all signals sent in the session, by adding an event
* handler for the signal
event.
*
* @name signal:type
* @event
* @memberof Session
* @see Session.signal()
* @see SignalEvent
* @see signal event
*/
return Session;
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(0)))
/***/ }),
/* 297 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const ExceptionCodes = __webpack_require__(11);
const OTErrorClass = __webpack_require__(33);
const knownErrorCodes = [400, 403, 409, ExceptionCodes.CONNECTION_LIMIT_EXCEEDED];
const reasons = {
CONNECT: 'ConnectToSession',
SESSION_STATE: 'GetSessionState'
};
/*
* Converts an error from RumorSocket to use generic exception codes
* @param {Error} error - Error object with reason property
* @return {Object} The converted error code and message
* @property {number} code
* @property {string} message
*/
module.exports = function convertRumorError(error) {
let code;
let message;
if (error.reason === reasons.CONNECT && error.code === ExceptionCodes.CONNECT_FAILED) {
code = error.code;
message = OTErrorClass.getTitleByCode(error.code);
} else if (error.code && knownErrorCodes.indexOf(Number(error.code)) > -1) {
code = ExceptionCodes.CONNECT_FAILED;
switch (error.reason) {
case reasons.CONNECT:
switch (error.code) {
case ExceptionCodes.CONNECTION_LIMIT_EXCEEDED:
code = error.code;
message = 'Cannot connect -- the limit for concurrent connections to the session ' + 'has been reached';
break;
default:
message = 'Received error response to connection create message.';
break;
}
break;
case reasons.SESSION_STATE:
message = 'Received error response to session read';
break;
default:
message = '';
break;
}
} else {
code = ExceptionCodes.UNEXPECTED_SERVER_RESPONSE;
message = 'Unexpected server response. Try this operation again later.';
}
return {
code,
message
};
};
/***/ }),
/* 298 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const env = __webpack_require__(3);
const hasOpenTokSupport = __webpack_require__(56).once;
module.exports = () => hasOpenTokSupport() && ['Chrome', 'Safari'].indexOf(env.name) !== -1;
/***/ }),
/* 299 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function serializeMessage(message) {
return JSON.stringify(message);
};
/***/ }),
/* 300 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
exports.__esModule = true;
exports.default = void 0;
var _default = (_ref) => {
let logger = _ref.logger,
obj = _ref.obj,
eventNames = _ref.eventNames;
eventNames.forEach(eventName => {
obj.on(eventName, function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return logger.spam('emitted', eventName, args);
});
});
};
exports.default = _default;
/***/ }),
/* 301 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable prefer-rest-params */
const isFunction = __webpack_require__(13);
const OTErrorClass = __webpack_require__(33);
const eventing = __webpack_require__(5);
const logging = __webpack_require__(1)('Dispatcher');
const RumorMessageTypes = __webpack_require__(166);
const unboxFromRumorMessage = __webpack_require__(670);
const Dispatcher = function Dispatcher() {
eventing(this);
this.callbacks = {};
};
module.exports = Dispatcher;
Dispatcher.prototype.registerCallback = function (transactionId, completion) {
this.callbacks[transactionId] = completion;
};
Dispatcher.prototype.triggerCallback = function (transactionId) {
/* , arg1, arg2, argN-1, argN */
if (!transactionId) {
return;
}
const completion = this.callbacks[transactionId];
if (completion && isFunction(completion)) {
const args = Array.prototype.slice.call(arguments);
args.shift();
completion(...args);
}
delete this.callbacks[transactionId];
};
Dispatcher.prototype.onClose = function (reason) {
this.emit('close', reason);
};
Dispatcher.prototype.onReconnected = function () {
this.emit('reconnected');
};
Dispatcher.prototype.onReconnecting = function () {
this.emit('reconnecting');
};
Dispatcher.prototype.dispatch = function (rumorMessage) {
// The special casing of STATUS messages is ugly. Need to think about
// how to better integrate this.
if (rumorMessage.type === RumorMessageTypes.STATUS) {
logging.debug(rumorMessage);
let error;
if (rumorMessage.isError) {
let message;
if (typeof rumorMessage.data === 'string') {
try {
const data = JSON.parse(rumorMessage.data);
if (data && typeof data === 'object') {
message = data.reason;
}
} catch (e) {
logging.warn('Failed to parse rumorMessage.data', e);
}
}
error = new OTErrorClass(rumorMessage.status, message);
}
this.triggerCallback(rumorMessage.transactionId, error, rumorMessage);
return;
}
const message = unboxFromRumorMessage(rumorMessage);
logging.debug(message.signature + ":", message);
switch (message.resource) {
case 'session':
this.dispatchSession(message);
break;
case 'connection':
this.dispatchConnection(message);
break;
case 'stream':
this.dispatchStream(message);
break;
case 'stream_channel':
this.dispatchStreamChannel(message);
break;
case 'subscriber':
this.dispatchSubscriber(message);
break;
case 'subscriber_channel':
this.dispatchSubscriberChannel(message);
break;
case 'signal':
this.dispatchSignal(message);
break;
case 'archive':
this.dispatchArchive(message);
break;
default:
logging.debug("Type " + message.resource + " is not currently implemented");
}
};
Dispatcher.prototype.dispatchSession = function (message) {
switch (message.method) {
case 'read':
this.emit('session#read', message.content, message.transactionId);
break;
default:
logging.debug(message.signature + " is not currently implemented");
}
};
Dispatcher.prototype.dispatchConnection = function (message) {
switch (message.method) {
case 'created':
this.emit('connection#created', message.content);
break;
case 'deleted':
this.emit('connection#deleted', message.params.connection, message.reason);
break;
default:
logging.debug(message.signature + " is not currently implemented");
}
};
Dispatcher.prototype.dispatchStream = function (message) {
switch (message.method) {
case 'created':
this.emit('stream#created', message.content, message.transactionId);
break;
case 'deleted':
this.emit('stream#deleted', message.params.stream, message.reason);
break;
case 'updated':
this.emit('stream#updated', message.params.stream, message.content);
break;
// The JSEP process
case 'generateoffer':
case 'answer':
case 'pranswer':
case 'offer':
case 'candidate':
this.dispatchJsep(message.method, message);
break;
default:
logging.debug(message.signature + " is not currently implemented");
}
};
Dispatcher.prototype.dispatchStreamChannel = function (message) {
switch (message.method) {
case 'updated':
this.emit('streamChannel#updated', message.params.stream, message.params.channel, message.content);
break;
default:
logging.debug(message.signature + " is not currently implemented");
}
}; // Dispatch JSEP messages
//
// generateoffer:
// Request to generate a offer for another Peer (or Prism). This kicks
// off the JSEP process.
//
// answer:
// generate a response to another peers offer, this contains our constraints
// and requirements.
//
// pranswer:
// a provisional answer, i.e. not the final one.
//
// candidate
//
//
Dispatcher.prototype.dispatchJsep = function (method, message) {
this.emit("jsep#" + method, message.params.stream, message.fromAddress, message);
};
Dispatcher.prototype.dispatchSubscriberChannel = function (message) {
switch (message.method) {
case 'updated':
this.emit('subscriberChannel#updated', message.params.stream, message.params.channel, message.content);
break;
case 'update':
// subscriberId, streamId, content
this.emit('subscriberChannel#update', message.params.subscriber, message.params.stream, message.content);
break;
default:
logging.debug(message.signature + " is not currently implemented");
}
};
Dispatcher.prototype.dispatchSubscriber = function (message) {
switch (message.method) {
case 'created':
this.emit('subscriber#created', message.params.stream, message.fromAddress, message.content.id);
break;
case 'deleted':
this.dispatchJsep('unsubscribe', message);
this.emit('subscriber#deleted', message.params.stream, message.fromAddress);
break;
// The JSEP process
case 'generateoffer':
case 'answer':
case 'pranswer':
case 'offer':
case 'candidate':
this.dispatchJsep(message.method, message);
break;
default:
logging.debug(message.signature + " is not currently implemented");
}
};
Dispatcher.prototype.dispatchSignal = function (message) {
if (message.method !== 'signal') {
logging.debug(message.signature + " is not currently implemented");
return;
}
this.emit('signal', message.fromAddress, message.content);
};
Dispatcher.prototype.dispatchArchive = function (message) {
switch (message.method) {
case 'created':
this.emit('archive#created', message.content);
break;
case 'updated':
this.emit('archive#updated', message.params.archive, message.content);
break;
default:
}
};
/***/ }),
/* 302 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-void */
const uuid = __webpack_require__(15);
const OTHelpers = __webpack_require__(4);
const hasBundleCapability = __webpack_require__(673);
const hasRTCPMuxCapability = __webpack_require__(674);
const serializeMessage = __webpack_require__(299);
const supportedCryptoScheme = __webpack_require__(675);
const staticConfig = __webpack_require__(28)().onlyLocal();
const Message = {};
module.exports = Message;
Message.connections = {};
Message.connections.create = function (opt) {
const apiKey = opt.apiKey;
const sessionId = opt.sessionId;
const connectionId = opt.connectionId;
const connectionEventsSuppressed = opt.connectionEventsSuppressed;
const capabilities = opt.capabilities;
return serializeMessage({
method: 'create',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/connection/" + connectionId,
content: {
userAgent: OTHelpers.env.userAgent,
clientVersion: staticConfig.clientVersion,
capabilities: capabilities || [],
connectionEventsSuppressed
}
});
};
Message.connections.destroy = function (opt) {
const apiKey = opt.apiKey;
const sessionId = opt.sessionId;
const connectionId = opt.connectionId;
return serializeMessage({
method: 'delete',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/connection/" + connectionId,
content: {}
});
};
Message.sessions = {};
Message.sessions.get = function (apiKey, sessionId) {
return serializeMessage({
method: 'read',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId,
content: {}
});
};
Message.streams = {};
Message.streams.get = function (apiKey, sessionId, streamId) {
return serializeMessage({
method: 'read',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId,
content: {}
});
};
Message.streams.channelFromOTChannel = function (channel) {
const raptorChannel = {
id: channel.id,
type: channel.type,
active: channel.active
};
if (channel.type === 'video') {
raptorChannel.width = channel.width;
raptorChannel.height = channel.height;
raptorChannel.orientation = channel.orientation;
raptorChannel.frameRate = channel.frameRate;
if (channel.source !== 'default') {
raptorChannel.source = channel.source;
}
raptorChannel.fitMode = channel.fitMode;
}
return raptorChannel;
};
Message.streams.create = function (apiKey, sessionId, streamId, name, audioFallbackEnabled, channels, minBitrate, maxBitrate) {
const messageContent = {
id: streamId,
name,
audioFallbackEnabled,
channel: channels.map(channel => Message.streams.channelFromOTChannel(channel))
};
if (minBitrate) {
messageContent.minBitrate = Math.round(minBitrate);
}
if (maxBitrate) {
messageContent.maxBitrate = Math.round(maxBitrate);
}
return serializeMessage({
method: 'create',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId,
content: messageContent
});
};
Message.streams.destroy = function (apiKey, sessionId, streamId) {
return serializeMessage({
method: 'delete',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId,
content: {}
});
};
Message.streamChannels = {};
Message.streamChannels.update = function (apiKey, sessionId, streamId, channelId, attributes) {
return serializeMessage({
method: 'update',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId + "/channel/" + channelId,
content: attributes
});
};
Message.subscribers = {};
Message.subscribers.create = function (apiKey, sessionId, streamId, subscriberId, connectionId, channelsToSubscribeTo) {
const content = {
id: subscriberId,
connection: connectionId,
keyManagementMethod: supportedCryptoScheme(),
bundleSupport: hasBundleCapability(),
rtcpMuxSupport: hasRTCPMuxCapability()
};
if (channelsToSubscribeTo) {
content.channel = channelsToSubscribeTo;
}
return serializeMessage({
method: 'create',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId + "/subscriber/" + subscriberId,
content
});
};
Message.subscribers.destroy = function (apiKey, sessionId, streamId, subscriberId) {
return serializeMessage({
method: 'delete',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId + "/subscriber/" + subscriberId,
content: {}
});
};
Message.subscribers.update = function (apiKey, sessionId, streamId, subscriberId, attributes) {
return serializeMessage({
method: 'update',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId + "/subscriber/" + subscriberId,
content: attributes
});
};
Message.subscriberChannels = {};
Message.subscriberChannels.update = function (apiKey, sessionId, streamId, subscriberId, channelId, attributes) {
return serializeMessage({
method: 'update',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + "/stream/" + streamId + "/subscriber/" + subscriberId + "/channel/" + channelId,
content: attributes
});
};
Message.signals = {};
Message.signals.create = function (apiKey, sessionId, toAddress, type, data) {
const content = {};
if (type !== void 0) {
content.type = type;
}
if (data !== void 0) {
content.data = data;
}
return serializeMessage({
method: 'signal',
uri: "/v2/partner/" + apiKey + "/session/" + sessionId + (toAddress !== void 0 ? "/connection/" + toAddress : '') + "/signal/" + uuid(),
content
});
};
/***/ }),
/* 303 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-useless-escape, no-underscore-dangle, no-void, no-prototype-builtins */
const clone = __webpack_require__(35);
const APIKEY = __webpack_require__(69);
const Connection = __webpack_require__(167);
const RaptorMessage = __webpack_require__(302);
const sessionTag = __webpack_require__(304);
const MAX_SIGNAL_DATA_LENGTH = 8192;
const MAX_SIGNAL_TYPE_LENGTH = 128; //
// Error Codes:
// 413 - Type too long
// 400 - Type is invalid
// 413 - Data too long
// 400 - Data is invalid (can't be parsed as JSON)
// 429 - Rate limit exceeded
// 500 - Websocket connection is down
// 404 - To connection does not exist
// 400 - To is invalid
//
module.exports = function Signal(sessionId, fromConnectionId, options) {
const isInvalidType = function isInvalidType(type) {
// Our format matches the unreserved characters from the URI RFC:
// http://www.ietf.org/rfc/rfc3986
return !/^[a-zA-Z0-9\-\._~]+$/.exec(type);
};
const validateTo = function validateTo(toAddress) {
if (!toAddress) {
return {
code: 400,
reason: 'The signal to field was invalid. Either set it to a OT.Connection, ' + 'OT.Session, or omit it entirely'
};
}
if (!(toAddress instanceof Connection || toAddress._tag === sessionTag)) {
return {
code: 400,
reason: 'The To field was invalid'
};
}
return null;
};
const validateType = function validateType(type) {
let error = null;
if (type === null || type === void 0) {
error = {
code: 400,
reason: 'The signal type was null or undefined. Either set it to a String value or ' + 'omit it'
};
} else if (type.length > MAX_SIGNAL_TYPE_LENGTH) {
error = {
code: 413,
reason: "The signal type was too long, the maximum length of it is " + MAX_SIGNAL_TYPE_LENGTH + " characters"
};
} else if (isInvalidType(type)) {
error = {
code: 400,
reason: 'The signal type was invalid, it can only contain letters, ' + 'numbers, \'-\', \'_\', and \'~\'.'
};
}
return error;
};
const validateData = function validateData(data) {
let error = null;
if (data === null || data === void 0) {
error = {
code: 400,
reason: 'The signal data was null or undefined. Either set it to a String value or ' + 'omit it'
};
} else {
try {
if (JSON.stringify(data).length > MAX_SIGNAL_DATA_LENGTH) {
error = {
code: 413,
reason: "The data field was too long, the maximum size of it is " + MAX_SIGNAL_DATA_LENGTH + " characters"
};
}
} catch (e) {
error = {
code: 400,
reason: 'The data field was not valid JSON'
};
}
}
return error;
};
const validateRetryAfterReconnect = function validateRetryAfterReconnect(retryAfterReconnect) {
let error = null;
if (!(retryAfterReconnect === true || retryAfterReconnect === false)) {
error = {
code: 400,
reason: 'The signal retryAfterReconnect was not true or false. Either set it to a Boolean ' + 'value or omit it'
};
}
return error;
};
this.toRaptorMessage = function () {
let to = this.to;
if (to && typeof to !== 'string') {
to = to.id;
}
return RaptorMessage.signals.create(APIKEY.value, sessionId, to, this.type, this.data);
};
this.toHash = function () {
return options;
};
this.error = null;
this.retryAfterReconnect = true;
if (options) {
if (options.hasOwnProperty('data')) {
this.data = clone(options.data);
this.error = validateData(this.data);
}
if (options.hasOwnProperty('to')) {
this.to = options.to;
if (!this.error) {
this.error = validateTo(this.to);
}
}
if (options.hasOwnProperty('type')) {
if (!this.error) {
this.error = validateType(options.type);
}
this.type = options.type;
}
if (options.hasOwnProperty('retryAfterReconnect')) {
if (!this.error) {
this.error = validateRetryAfterReconnect(options.retryAfterReconnect);
}
this.retryAfterReconnect = options.retryAfterReconnect;
}
}
this.valid = this.error === null;
};
/***/ }),
/* 304 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// This is used to break the dependency Raptor had on Session. It only needs to be able to know
// whether an object is an instanceof a Session. The dependency was an issue for node because
// Session depends on get_user_media.js which doesn't work in node.
module.exports = {};
/***/ }),
/* 305 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__(2);
var _regenerator = _interopRequireDefault(__webpack_require__(18));
var _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-shadow, prefer-rest-params, prefer-spread, no-param-reassign */
/* eslint-disable no-mixed-operators, no-cond-assign, one-var */
const Archive = __webpack_require__(306);
const now = __webpack_require__(46);
const Connection = __webpack_require__(167);
const DelayedEventQueue = __webpack_require__(678);
const Dispatcher = __webpack_require__(301);
const logging = __webpack_require__(1)('SessionDispatcher');
const sessionObjects = __webpack_require__(22);
const Stream = __webpack_require__(307);
const StreamChannel = __webpack_require__(145);
function constructBareConnection(fromAddress) {
return {
id: fromAddress,
creationTime: new Date().getTime(),
data: {},
capablities: {},
permissions: []
};
}
function parseStream(dict, session) {
const channel = dict.channel.map(channel => new StreamChannel(channel));
const connectionId = dict.connectionId ? dict.connectionId : dict.connection.id;
return new Stream(dict.id, dict.name, dict.creationTime, session.connections.get(connectionId), session, channel);
}
function parseAndAddStreamToSession(dict, session) {
if (session.streams.has(dict.id)) {
return undefined;
}
const stream = parseStream(dict, session);
session.streams.add(stream);
return stream;
}
function parseArchive(dict) {
return new Archive(dict.id, dict.name, dict.status);
}
function parseAndAddArchiveToSession(dict, session) {
if (session.archives.has(dict.id)) {
return undefined;
}
const archive = parseArchive(dict);
session.archives.add(archive);
return archive;
}
const DelayedSessionEvents = function DelayedSessionEvents(dispatcher) {
const eventQueues = {};
this.enqueue = function enqueue()
/* key, arg1, arg2, ..., argN */
{
const key = arguments[0];
const eventArgs = Array.prototype.slice.call(arguments, 1);
if (!eventQueues[key]) {
eventQueues[key] = new DelayedEventQueue(dispatcher);
}
eventQueues[key].enqueue.apply(eventQueues[key], eventArgs);
};
this.triggerConnectionCreated = function triggerConnectionCreated(connection) {
if (eventQueues["connectionCreated" + connection.id]) {
eventQueues["connectionCreated" + connection.id].triggerAll();
}
};
this.triggerSessionConnected = function triggerSessionConnected(connections) {
if (eventQueues.sessionConnected) {
eventQueues.sessionConnected.triggerAll();
}
connections.forEach(function (connection) {
this.triggerConnectionCreated(connection);
}, this);
};
};
const unconnectedStreams = {};
module.exports = function SessionDispatcher(session, _temp) {
let _ref = _temp === void 0 ? {} : _temp,
connectionEventsSuppressed = _ref.connectionEventsSuppressed;
const dispatcher = new Dispatcher();
let sessionStateReceived = false;
const delayedSessionEvents = new DelayedSessionEvents(dispatcher);
dispatcher.on('reconnecting', () => {
session._.reconnecting();
});
dispatcher.on('reconnected', () => {
session._.reconnected();
});
dispatcher.on('close', reason => {
const connection = session.connection;
if (!connection) {
return;
}
if (connection.destroyedReason()) {
logging.debug("" + ('Socket was closed but the connection had already ' + 'been destroyed. Reason: ') + connection.destroyedReason());
return;
}
connection.destroy(reason);
}); // This method adds connections to the session both on a connection#created and
// on a session#read. In the case of session#read sessionRead is set to true and
// we include our own connection.
const addConnection = function addConnection(connection, sessionRead) {
if (session.connections.has(connection.id)) {
// Don't add a duplicate connection, since we add them not only on connection#created, but
// also when stream#created or signal has an associated connection.
return session.connections.get(connection.id);
}
connection = Connection.fromHash(connection);
if (sessionRead || session.connection && connection.id !== session.connection.id) {
session.connections.add(connection);
delayedSessionEvents.triggerConnectionCreated(connection);
}
Object.keys(unconnectedStreams).forEach(streamId => {
const stream = unconnectedStreams[streamId];
if (stream && connection.id === stream.connection.id) {
// dispatch streamCreated event now that the connectionCreated has been dispatched
parseAndAddStreamToSession(stream, session);
delete unconnectedStreams[stream.id];
const payload = {
debug: sessionRead ? 'connection came in session#read' : 'connection came in connection#created',
streamId: stream.id,
connectionId: connection.id
};
session.logEvent('streamCreated', 'warning', payload);
}
});
return connection;
};
dispatcher.on('session#read', (content, transactionId) => {
let connection;
const state = {};
state.streams = [];
state.connections = [];
state.archives = [];
content.connection.forEach(connectionParams => {
connection = addConnection(connectionParams, true);
state.connections.push(connection);
});
content.stream.forEach(streamParams => {
state.streams.push(parseAndAddStreamToSession(streamParams, session));
});
(content.archive || content.archives).forEach(archiveParams => {
state.archives.push(parseAndAddArchiveToSession(archiveParams, session));
});
dispatcher.triggerCallback(transactionId, null, state);
sessionStateReceived = true;
delayedSessionEvents.triggerSessionConnected(session.connections);
});
dispatcher.on('connection#created', connection => {
addConnection(connection);
});
dispatcher.on('connection#deleted', (connection, reason) => {
connection = session.connections.get(connection);
if (!connection) {
logging.warn('A connection was deleted that we do not know about');
return;
}
connection.destroy(reason);
});
dispatcher.on('stream#created', (stream, transactionId) => {
if (connectionEventsSuppressed) {
if (stream.connection == null || stream.connection.id == null) {
session.logEvent('SessionDispatcher:stream#created', 'Event', {
connection: stream.connection,
info: 'Stream did not contain a connection object. Event ignored'
});
return;
}
addConnection(stream.connection);
}
const connectionId = stream.connectionId ? stream.connectionId : stream.connection.id;
if (session.connections.has(connectionId)) {
stream = parseAndAddStreamToSession(stream, session);
} else {
unconnectedStreams[stream.id] = stream;
const payload = {
debug: 'eventOrderError -- streamCreated event before connectionCreated',
streamId: stream.id
};
session.logEvent('streamCreated', 'warning', payload);
}
if (stream) {
if (stream.publisher) {
stream.publisher.setStream(stream);
}
dispatcher.triggerCallback(transactionId, null, stream);
}
});
dispatcher.on('stream#deleted', (streamId, reason) => {
const stream = session.streams.get(streamId);
if (!stream) {
logging.error("A stream does not exist with the id of " + streamId + ", for stream#deleted message!"); // @todo error
return;
}
stream.destroy(reason);
});
dispatcher.on('stream#updated', (streamId, content) => {
const stream = session.streams.get(streamId);
if (!stream) {
logging.error("A stream does not exist with the id of " + streamId + ", for stream#updated message!"); // @todo error
return;
}
stream._.update(content);
});
dispatcher.on('streamChannel#updated', (streamId, channelId, content) => {
let stream;
if (!(streamId && (stream = session.streams.get(streamId)))) {
logging.error('Unable to determine streamId, or the stream does not ' + 'exist, for streamChannel message!'); // @todo error
return;
}
stream._.updateChannel(channelId, content);
}); // Dispatch JSEP messages
//
// generateoffer:
// Request to generate a offer for another Peer (or Prism). This kicks
// off the JSEP process.
//
// answer:
// generate a response to another peers offer, this contains our constraints
// and requirements.
//
// pranswer:
// a provisional answer, i.e. not the final one.
//
// candidate
//
//
const jsepHandler = (method, streamId, fromAddress, message) => {
let subscriberFilter;
let actors;
const hasStreamId = {
streamId
};
const subscribers = sessionObjects.subscribers;
const publishers = sessionObjects.publishers;
if (message.params.subscriber) {
subscriberFilter = {
widgetId: message.params.subscriber
};
} else {
// if we don't know the subscriber, we will just match the stream id
subscriberFilter = hasStreamId;
} // Determine which subscriber/publisher objects should receive this message.
switch (method) {
case 'offer':
actors = [].concat(subscribers.where(subscriberFilter), publishers.where(hasStreamId)).slice(0, 1);
break;
case 'answer':
case 'pranswer':
actors = [].concat(publishers.where(hasStreamId), subscribers.where(subscriberFilter)).slice(0, 1);
break;
case 'generateoffer':
case 'unsubscribe':
actors = publishers.where(hasStreamId);
break;
case 'candidate':
actors = [].concat(subscribers.where(subscriberFilter), publishers.where(hasStreamId));
break;
default:
logging.debug("jsep#" + method + " is not currently implemented");
return;
}
if (actors.length === 0) {
return;
}
let fromConnection = session.connections.get(fromAddress);
if (!fromConnection && fromAddress.match(/^symphony\./)) {
fromConnection = Connection.fromHash({
id: fromAddress,
creationTime: Math.floor(now())
});
session.connections.add(fromConnection);
}
actors.forEach( /*#__PURE__*/function () {
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(actor) {
return _regenerator.default.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
_context.next = 3;
return actor.processMessage(method, fromAddress, message);
case 3:
_context.next = 8;
break;
case 5:
_context.prev = 5;
_context.t0 = _context["catch"](0);
logging.error('Error occurred during processMessage', _context.t0);
case 8:
case "end":
return _context.stop();
}
}, _callee, null, [[0, 5]]);
}));
return function (_x) {
return _ref2.apply(this, arguments);
};
}());
};
dispatcher.on('jsep#offer', jsepHandler.bind(null, 'offer'));
dispatcher.on('jsep#answer', jsepHandler.bind(null, 'answer'));
dispatcher.on('jsep#pranswer', jsepHandler.bind(null, 'pranswer'));
dispatcher.on('jsep#generateoffer', jsepHandler.bind(null, 'generateoffer'));
dispatcher.on('jsep#unsubscribe', jsepHandler.bind(null, 'unsubscribe'));
dispatcher.on('jsep#candidate', jsepHandler.bind(null, 'candidate'));
dispatcher.on('subscriberChannel#updated', (streamId, channelId, content) => {
if (!streamId || !session.streams.has(streamId)) {
logging.error('Unable to determine streamId, or the stream does not ' + 'exist, for subscriberChannel#updated message!'); // @todo error
return;
}
session.streams.get(streamId)._.updateChannel(channelId, content);
});
dispatcher.on('subscriberChannel#update', (subscriberId, streamId, content) => {
if (!streamId || !session.streams.has(streamId)) {
logging.error('Unable to determine streamId, or the stream does not ' + 'exist, for subscriberChannel#update message!'); // @todo error
return;
} // Hint to update for congestion control from the Media Server
if (!sessionObjects.subscribers.has(subscriberId)) {
logging.error('Unable to determine subscriberId, or the subscriber ' + 'does not exist, for subscriberChannel#update message!'); // @todo error
return;
} // We assume that an update on a Subscriber channel is to disableVideo
// we may need to be more specific in the future
sessionObjects.subscribers.get(subscriberId).disableVideo(content.active);
}); // Note: subscriber#created and subscriber#deleted messages are available but we currently
// don't have a use for them.
dispatcher.on('signal', (fromAddress, content) => {
if (connectionEventsSuppressed) {
let connection = content.connection || content.fromConnection;
if (connection == null || connection.id == null) {
connection = constructBareConnection(fromAddress);
session.logEvent('SessionDispatcher:Signal', 'Event', {
fromAddress,
connection: content.connection,
fromConnection: content.fromConnection,
info: 'Signal did not contain a connection object. One has been constructed',
constructedConnection: connection
});
}
addConnection(connection);
}
const signalType = content.type;
const data = content.data;
const fromConnection = session.connections.get(fromAddress);
if (session.connection && fromAddress === session.connection.connectionId) {
if (sessionStateReceived) {
session._.dispatchSignal(fromConnection, signalType, data);
} else {
delayedSessionEvents.enqueue('sessionConnected', 'signal', fromAddress, signalType, data);
}
} else if (session.connections.get(fromAddress)) {
session._.dispatchSignal(fromConnection, signalType, data);
} else if (fromAddress === '') {
// Server originated signal
session._.dispatchSignal(null, signalType, data);
} else {
delayedSessionEvents.enqueue("connectionCreated" + fromAddress, 'signal', fromAddress, signalType, data);
}
});
dispatcher.on('archive#created', archive => {
parseAndAddArchiveToSession(archive, session);
});
dispatcher.on('archive#updated', (archiveId, update) => {
const archive = session.archives.get(archiveId);
if (!archive) {
logging.error("An archive does not exist with the id of " + archiveId + ", for archive#updated message!"); // @todo error
return;
}
archive._.update(update);
});
return dispatcher;
};
/***/ }),
/* 306 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const Events = __webpack_require__(20)();
const eventing = __webpack_require__(5);
module.exports = function Archive(id, name, status) {
this.id = id;
this.name = name;
this.status = status;
this._ = {};
eventing(this); // Mass update, called by Raptor.Dispatcher
this._.update = attributes => {
Object.keys(attributes).forEach(key => {
const oldValue = this[key];
this[key] = attributes[key];
const event = new Events.ArchiveUpdatedEvent(this, key, oldValue, this[key]);
this.dispatchEvent(event);
});
};
this.destroy = () => {};
};
/***/ }),
/* 307 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// @todo enable the following disabled rules see OPENTOK-31136 for more info
/* eslint-disable no-shadow, no-underscore-dangle, max-len, no-use-before-define */
/* eslint-disable no-void, vars-on-top, no-var, no-restricted-syntax, no-prototype-builtins */
/* eslint-disable no-continue */
const logging = __webpack_require__(1)('Stream');
const OTHelpers = __webpack_require__(4);
const sessionObjects = __webpack_require__(22);
const eventing = __webpack_require__(5);
const Events = __webpack_require__(20)();
const validPropertyNames = ['name', 'archiving'];
/**
* Specifies a stream. A stream is a representation of a published stream in a session. When a
* client calls the Session.publish() method, a new stream is
* created. Properties of the Stream object provide information about the stream.
*
*
When a stream is added to a session, the Session object dispatches a
* streamCreatedEvent
. When a stream is destroyed, the Session object dispatches a
* streamDestroyed
event. The StreamEvent object, which defines these event objects,
* has a stream
property, which is an array of Stream object. For details and a code
* example, see {@link StreamEvent}.
When a connection to a session is made, the Session object dispatches a
* sessionConnected
event, defined by the SessionConnectEvent object. The
* SessionConnectEvent object has a streams
property, which is an array of Stream
* objects pertaining to the streams in the session at that time. For details and a code example,
* see {@link SessionConnectEvent}.
connection
property of the Session object to see if the stream is being published
* by the local web page.
*
* @property {Number} creationTime The timestamp for the creation
* of the stream. This value is calculated in milliseconds. You can convert this value to a
* Date object by calling new Date(creationTime)
, where creationTime
is
* the creationTime
property of the Stream object.
*
* @property {Number} frameRate The frame rate of the video stream. This property is only set if
* the publisher of the stream specifies a frame rate when calling the
* OT.initPublisher()
method; otherwise, this property is undefined.
*
* @property {Boolean} hasAudio Whether the stream has audio. This property can change if the
* publisher turns on or off audio (by calling
* Publisher.publishAudio()). When this occurs, the
* {@link Session} object dispatches a streamPropertyChanged
event (see
* {@link StreamPropertyChangedEvent}).
*
* @property {Boolean} hasVideo Whether the stream has video. This property can change if the
* publisher turns on or off video (by calling
* Publisher.publishVideo()). When this occurs, the
* {@link Session} object dispatches a streamPropertyChanged
event (see
* {@link StreamPropertyChangedEvent}).
*
* @property {String} name The name of the stream. Publishers can specify a name when publishing
* a stream (using the publish()
method of the publisher's Session object).
*
* @property {String} streamId The unique ID of the stream.
*
* @property {Object} videoDimensions This object has two properties: width
and
* height
. Both are numbers. The width
property is the width of the
* encoded stream; the height
property is the height of the encoded stream. (These
* are independent of the actual width of Publisher and Subscriber objects corresponding to the
* stream.) This property can change if a stream published from a mobile device resizes, based on
* a change in the device orientation. When the video dimensions change,
* the {@link Session} object dispatches a streamPropertyChanged
event
* (see {@link StreamPropertyChangedEvent}).
*
* @property {String} videoType The type of video — either "camera"
,
* "screen"
, or "custom"
.
* A "screen"
video uses screen sharing on the publisher
* as the video source; for other videos, this property is set to "camera"
.
* A "custom"
video uses a VideoTrack element as the video source on the publisher.
* (See the videoSource
property of the options
parameter passed
* into the OT.initPublisher() method.)
* This property can change if a stream published from a mobile device changes from a
* camera to a screen-sharing video type. When the video type changes, the {@link Session} object
* dispatches a streamPropertyChanged
event (see {@link StreamPropertyChangedEvent}).
*/
module.exports = function Stream(id, name, creationTime, connection, session, channel) {
const self = this;
let destroyedReason;
this.id = id;
this.streamId = id;
this.name = name;
this.creationTime = Number(creationTime);
this.connection = connection;
this.channel = channel;
this.publisher = sessionObjects.publishers.find({
streamId: this.id
});
eventing(this);
const onChannelUpdate = function onChannelUpdate(channel, key, oldValue, newValue) {
let _key = key;
switch (_key) {
case 'active':
_key = channel.type === 'audio' ? 'hasAudio' : 'hasVideo';
self[_key] = newValue;
break;
case 'disableWarning':
_key = channel.type === 'audio' ? 'audioDisableWarning' : 'videoDisableWarning';
self[_key] = newValue;
if (!self[channel.type === 'audio' ? 'hasAudio' : 'hasVideo']) {
return; // Do NOT event in this case.
}
break;
case 'fitMode':
_key = 'defaultFitMode';
self[_key] = newValue;
break;
case 'source':
_key = channel.type === 'audio' ? 'audioType' : 'videoType';
self[_key] = newValue;
break;
case 'videoDimensions':
self.videoDimensions = newValue;
break;
case 'orientation':
case 'width':
case 'height':
// We dispatch this via the videoDimensions key instead so do not
// trigger an event for them.
return;
default:
}
if (self.videoType === 'screen' && _key === 'hasVideo' && newValue === false) {
// if this stream is from a screen ignore hasVideo=false events from rumor
return;
}
self.dispatchEvent(new Events.StreamUpdatedEvent(self, _key, oldValue, newValue));
};
const associatedWidget = function associatedWidget() {
if (self.publisher) {
return self.publisher;
}
return sessionObjects.subscribers.find(subscriber => subscriber.stream && subscriber.stream.id === self.id && subscriber.session.id === session.id);
}; // Returns true if this stream is subscribe to.
const isBeingSubscribedTo = function isBeingSubscribedTo() {
// @fixme This is not strictly speaking the right test as a stream
// can be published and subscribed by the same connection. But the
// update features don't handle this case properly right now anyway.
//
// The issue is that the stream needs to know whether the stream is
// 'owned' by a publisher or a subscriber. The reason for that is that
// when a Publisher updates a stream channel then we need to send the
// `streamChannelUpdate` message, whereas if a Subscriber does then we
// need to send `subscriberChannelUpdate`. The current code will always
// send `streamChannelUpdate`.
return !self.publisher;
}; // Returns all channels that have a type of +type+.
this.getChannelsOfType = function getChannelsOfType(type) {
return self.channel.filter(channel => channel.type === type);
};
this.getChannel = function getChannel(id) {
for (let i = 0; i < self.channel.length; ++i) {
if (self.channel[i].id === id) {
return self.channel[i];
}
}
return null;
}; // implement the following using the channels
// * hasAudio
// * hasVideo
// * videoDimensions
const audioChannel = this.getChannelsOfType('audio')[0];
const videoChannel = this.getChannelsOfType('video')[0]; // @todo this should really be: "has at least one video/audio track" instead of
// "the first video/audio track"
this.hasAudio = audioChannel != null && audioChannel.active;
this.hasVideo = videoChannel != null && videoChannel.active;
this.videoType = videoChannel && videoChannel.source;
this.defaultFitMode = videoChannel && videoChannel.fitMode;
this.videoDimensions = {};
if (videoChannel) {
this.videoDimensions.width = videoChannel.width;
this.videoDimensions.height = videoChannel.height;
this.videoDimensions.orientation = videoChannel.orientation;
videoChannel.on('update', onChannelUpdate);
this.frameRate = videoChannel.frameRate;
}
if (audioChannel) {
audioChannel.on('update', onChannelUpdate);
}
this.setChannelActiveState = function setChannelActiveState(channelType, activeState, activeReason) {
const attributes = {
active: activeState
};
if (activeReason) {
attributes.activeReason = activeReason;
}
updateChannelsOfType(channelType, attributes);
};
this.setVideoDimensions = function setVideoDimensions(width, height) {
updateChannelsOfType('video', {
width,
height,
orientation: 0
});
};
this.setRestrictFrameRate = function setRestrictFrameRate(restrict) {
updateChannelsOfType('video', {
restrictFrameRate: restrict
});
};
this.setPreferredResolution = function setPreferredResolution(resolution) {
if (!isBeingSubscribedTo()) {
logging.warn('setPreferredResolution has no affect when called by a publisher');
return;
}
if (session.sessionInfo.p2pEnabled) {
logging.warn('Stream.setPreferredResolution will not work in a P2P Session');
return;
}
if (resolution && resolution.width === void 0 && resolution.height === void 0) {
return;
} // This duplicates some of the code in updateChannelsOfType. We do this for a
// couple of reasons:
// 1. Because most of the work that updateChannelsOfType does is in calling
// getChannelsOfType, which we need to do here anyway so that we can update
// the value of maxResolution in the Video Channel.
// 2. updateChannelsOfType on only sends a message to update the channel in
// Rumor. The client then expects to receive a subsequent channel update
// indicating that the update was successful. We don't receive those updates
// for preferredFrameRate/maxResolution so we need to complete both tasks and it's
// neater to do the related tasks right next to each other.
// 3. This code shouldn't be in Stream anyway. There is way too much coupling
// between Stream, Session, Publisher, and Subscriber. This will eventually be
// fixed, and when it is then it will be easier to exact the code if it's a
// single piece.
//
const video = self.getChannelsOfType('video')[0];
if (!video) {
return;
}
if (resolution && resolution.width) {
if (isNaN(parseInt(resolution.width, 10))) {
throw new OTHelpers.Error('stream preferred width must be an integer', 'Subscriber');
}
video.preferredWidth = parseInt(resolution.width, 10);
} else {
video.preferredWidth = void 0;
}
if (resolution && resolution.height) {
if (isNaN(parseInt(resolution.height, 10))) {
throw new OTHelpers.Error('stream preferred height must be an integer', 'Subscriber');
}
video.preferredHeight = parseInt(resolution.height, 10);
} else {
video.preferredHeight = void 0;
}
session._.subscriberChannelUpdate(self, associatedWidget(), video, {
preferredWidth: video.preferredWidth || 0,
preferredHeight: video.preferredHeight || 0
});
};
this.getPreferredResolution = function getPreferredResolution() {
const videoChannel = self.getChannelsOfType('video')[0];
if (!videoChannel || !videoChannel.preferredWidth && !videoChannel.preferredHeight) {
return void 0;
}
return {
width: videoChannel.preferredWidth,
height: videoChannel.preferredHeight
};
};
this.setPreferredFrameRate = function setPreferredFrameRate(preferredFrameRate) {
if (!isBeingSubscribedTo()) {
logging.warn('setPreferredFrameRate has no affect when called by a publisher');
return;
}
if (session.sessionInfo.p2pEnabled) {
logging.warn('Stream.setPreferredFrameRate will not work in a P2P Session');
return;
}
if (preferredFrameRate && isNaN(parseFloat(preferredFrameRate))) {
throw new OTHelpers.Error('stream preferred frameRate must be a number', 'Subscriber');
} // This duplicates some of the code in updateChannelsOfType. We do this for a
// couple of reasons:
// 1. Because most of the work that updateChannelsOfType does is in calling
// getChannelsOfType, which we need to do here anyway so that we can update
// the value of preferredFrameRate in the Video Channel.
// 2. updateChannelsOfType on only sends a message to update the channel in
// Rumor. The client then expects to receive a subsequent channel update
// indicating that the update was successful. We don't receive those updates
// for preferredFrameRate/maxResolution so we need to complete both tasks and it's
// neater to do the related tasks right next to each other.
// 3. This code shouldn't be in Stream anyway. There is way too much coupling
// between Stream, Session, Publisher, and Subscriber. This will eventually be
// fixed, and when it is then it will be easier to exact the code if it's a
// single piece.
//
const video = self.getChannelsOfType('video')[0];
if (video) {
video.preferredFrameRate = preferredFrameRate ? parseFloat(preferredFrameRate) : null;
session._.subscriberChannelUpdate(self, associatedWidget(), video, {
preferredFrameRate: video.preferredFrameRate || 0
});
}
};
this.getPreferredFrameRate = function getPreferredFrameRate() {
const videoChannel = self.getChannelsOfType('video')[0];
return videoChannel ? videoChannel.preferredFrameRate : null;
};
let updateChannelsOfType = function updateChannelsOfType(channelType, attributes) {
let setChannelActiveState;
if (!self.publisher) {
const subscriber = associatedWidget();
setChannelActiveState = channel => session._.subscriberChannelUpdate(self, subscriber, channel, attributes);
} else {
setChannelActiveState = channel => session._.streamChannelUpdate(self, channel, attributes);
}
self.getChannelsOfType(channelType).forEach(setChannelActiveState);
};
this.destroyed = false;
this.destroyedReason = void 0;
this.destroy = function destroy(reason, quiet) {
if (reason === void 0) {
reason = 'clientDisconnected';
}
destroyedReason = reason;
self.destroyed = true;
self.destroyedReason = destroyedReason;
if (quiet !== true) {
self.dispatchEvent(new Events.DestroyedEvent('destroyed', // This should be eventNames.STREAM_DESTROYED, but
// the value of that is currently shared with Session
self, destroyedReason));
}
}; // PRIVATE STUFF CALLED BY Raptor.Dispatcher
//
// Confusingly, this should not be called when you want to change
// the stream properties. This is used by Raptor dispatch to notify
// the stream that it's properties have been successfully updated
//
// @todo make this sane. Perhaps use setters for the properties that can
// send the appropriate Raptor message. This would require that Streams
// have access to their session.
this._ = {};
this._.updateProperty = function privateUpdateProperty(key, value) {
if (validPropertyNames.indexOf(key) === -1) {
logging.warn("Unknown stream property \"" + key + "\" was modified to \"" + value + "\".");
return;
}
const oldValue = self[key];
const newValue = value;
switch (key) {
case 'name':
self[key] = newValue;
break;
case 'archiving':
var widget = associatedWidget();
if (self.publisher && widget) {
widget._.archivingStatus(newValue);
}
self[key] = newValue;
break;
default:
}
const event = new Events.StreamUpdatedEvent(self, key, oldValue, newValue);
self.dispatchEvent(event);
}; // Mass update, called by Raptor.Dispatcher
this._.update = function privateUpdate(attributes) {
for (const key in attributes) {
if (!attributes.hasOwnProperty(key)) {
continue;
}
self._.updateProperty(key, attributes[key]);
}
};
this._.updateChannel = function privateUpdateChannel(channelId, attributes) {
const channel = self.getChannel(channelId);
if (channel) {
channel.update(attributes);
}
};
};
/***/ }),
/* 308 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
const _require = __webpack_require__(103)(),
getMediaDevices = _require.getMediaDevices;
/**
* Enumerates the audio input devices (such as microphones) and video input devices
* (cameras) available to the browser.
*
* The array of devices is passed in as the devices
parameter of
* the callback
function passed into the method.
*
* @param callback {Function} The callback function invoked when the list of devices
* devices is available. This function takes two parameters:
*
error
— This is set to an error object when
* there is an error in calling this method; it is set to null
* when the call succeeds.devices
— An array of objects corresponding to
* available microphones and cameras. Each object has three properties: kind
,
* deviceId
, and label
, each of which are strings.
*
* The kind
property is set to "audioInput"
for audio input
* devices or "videoInput"
for video input devices.
*
* The deviceId
property is a unique ID for the device. You can pass
* the deviceId
in as the audioSource
or videoSource
* property of the options
parameter of the
* OT.initPublisher() method.
*
* The label
property identifies the device. The label
* property is set to an empty string if the user has not previously granted access to
* a camera and microphone. In HTTP, the user must have granted access to a camera and
* microphone in the current page (for example, in response to a call to
* OT.initPublisher()
). In HTTPS, the user must have previously granted access
* to the camera and microphone in the current page or in a page previously loaded from the
* domain.
* Registers a method as an event listener for a specific event. *
* *
* The OT object dispatches one type of event an exception
event. The
* following code adds an event listener for the exception
event:
*
* OT.addEventListener("exception", exceptionHandler); * * function exceptionHandler(event) { * alert("exception event. \n code == " + event.code + "\n message == " + event.message); * } ** *
* If a handler is not registered for an event, the event is ignored locally. If the event * listener function does not exist, the event is ignored locally. *
*
* Throws an exception if the listener
name is invalid.
*
* Removes an event listener for a specific event. *
* *
* Throws an exception if the listener
name is invalid.
*
* The OT object dispatches one type of event an exception
event. The following
* code adds an event
* listener for the exception
event:
*
* OT.on("exception", function (event) { * // This is the event handler. * }); ** *
You can also pass in a third context
parameter (which is optional) to define the
* value of
* this
in the handler method:
* OT.on("exception", * function (event) { * // This is the event handler. * }), * session * ); ** *
* If you do not add a handler for an event, the event is ignored locally. *
* * @param {String} type The string identifying the type of event. * @param {Function} handler The handler function to process the event. This function takes the event * object as a parameter. * @param {Object} context (Optional) Defines the value ofthis
in the event handler
* function.
*
* @memberof OT
* @method on
* @see off()
* @see once()
* @see Events
*/
/**
* Adds an event handler function for an event. Once the handler is called, the specified handler
* method is
* removed as a handler for this event. (When you use the OT.on()
method to add an event
* handler, the handler
* is not removed when it is called.) The OT.once()
method is the equivilent of
* calling the OT.on()
* method and calling OT.off()
the first time the handler is invoked.
*
*
* The following code adds a one-time event handler for the exception
event:
*
* OT.once("exception", function (event) { * console.log(event); * } ** *
You can also pass in a third context
parameter (which is optional) to define the
* value of
* this
in the handler method:
* OT.once("exception", * function (event) { * // This is the event handler. * }, * session * ); ** *
* The method also supports an alternate syntax, in which the first parameter is an object that is a * hash map of * event names and handler functions and the second parameter (optional) is the context for this in * each handler: *
** OT.once( * {exeption: function (event) { * // This is the event handler. * } * }, * session * ); ** * @param {String} type The string identifying the type of event. You can specify multiple event * names in this string, * separating them with a space. The event handler will process the first occurence of the events. * After the first event, * the handler is removed (for all specified events). * @param {Function} handler The handler function to process the event. This function takes the event * object as a parameter. * @param {Object} context (Optional) Defines the value of
this
in the event handler
* function.
*
* @memberof OT
* @method once
* @see on()
* @see once()
* @see Events
*/
/**
* Removes an event handler.
*
* Pass in an event name and a handler method, the handler is removed for that event:
* *OT.off("exceptionEvent", exceptionEventHandler);* *
If you pass in an event name and no handler method, all handlers are removed for that * events:
* *OT.off("exceptionEvent");* *
* The method also supports an alternate syntax, in which the first parameter is an object that is a * hash map of * event names and handler functions and the second parameter (optional) is the context for matching * handlers: *
** OT.off( * { * exceptionEvent: exceptionEventHandler * }, * this * ); ** * @param {String} type (Optional) The string identifying the type of event. You can use a space to * specify multiple events, as in "eventName1 eventName2 eventName3". If you pass in no *
type
value (or other arguments), all event handlers are removed for the object.
* @param {Function} handler (Optional) The event handler function to remove. If you pass in no
* handler
, all event handlers are removed for the specified event type
.
* @param {Object} context (Optional) If you specify a context
, the event handler is
* removed for all specified events and handlers that use the specified context.
*
* @memberof OT
* @method off
* @see on()
* @see once()
* @see Events
*/
/**
* Dispatched by the OT class when the app encounters an exception.
* Note that you set up an event handler for the exception
event by calling the
* OT.on()
method.
*
* @name exception
* @event
* @borrows ExceptionEvent#message as this.message
* @memberof OT
* @see ExceptionEvent
*/
/***/ }),
/* 310 */
/***/ (function(module, exports, __webpack_require__) {
var content = __webpack_require__(311);
if(typeof content === 'string') content = [[module.i, content, '']];
var transform;
var insertInto;
var options = {"hmr":true}
options.transform = transform
options.insertInto = undefined;
var update = __webpack_require__(325)(content, options);
if(content.locals) module.exports = content.locals;
if(false) {}
/***/ }),
/* 311 */
/***/ (function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(312)(false);
// Imports
var urlEscape = __webpack_require__(313);
var ___CSS_LOADER_URL___0___ = urlEscape(__webpack_require__(314));
var ___CSS_LOADER_URL___1___ = urlEscape(__webpack_require__(315));
var ___CSS_LOADER_URL___2___ = urlEscape(__webpack_require__(316));
var ___CSS_LOADER_URL___3___ = urlEscape(__webpack_require__(317));
var ___CSS_LOADER_URL___4___ = urlEscape(__webpack_require__(318));
var ___CSS_LOADER_URL___5___ = urlEscape(__webpack_require__(319));
var ___CSS_LOADER_URL___6___ = urlEscape(__webpack_require__(320));
var ___CSS_LOADER_URL___7___ = urlEscape(__webpack_require__(321));
var ___CSS_LOADER_URL___8___ = urlEscape(__webpack_require__(322));
var ___CSS_LOADER_URL___9___ = urlEscape(__webpack_require__(323));
var ___CSS_LOADER_URL___10___ = urlEscape(__webpack_require__(324));
// Module
exports.push([module.i, "/*!\n * Copyright (c) 2017 TokBox, Inc.\n * Released under the MIT license\n * http://opensource.org/licenses/MIT\n */\n\n/**\n * OT Base styles\n */\n\n/* Root OT object, this is where our CSS reset happens */\n.OT_root,\n.OT_root * {\n color: #ffffff;\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font-family: Arial, Helvetica, sans-serif;\n vertical-align: baseline;\n}\n\n/**\n * Specific Element Reset\n */\n\n.OT_root h1,\n.OT_root h2,\n.OT_root h3,\n.OT_root h4,\n.OT_root h5,\n.OT_root h6 {\n color: #ffffff;\n font-family: Arial, Helvetica, sans-serif;\n font-size: 100%;\n font-weight: bold;\n}\n\n.OT_root header {\n\n}\n\n.OT_root footer {\n\n}\n\n.OT_root div {\n\n}\n\n.OT_root section {\n\n}\n\n.OT_root video {\n\n}\n\n.OT_root button {\n\n}\n\n.OT_root strong {\n font-weight: bold;\n}\n\n.OT_root em {\n font-style: italic;\n}\n\n.OT_root a,\n.OT_root a:link,\n.OT_root a:visited,\n.OT_root a:hover,\n.OT_root a:active {\n font-family: Arial, Helvetica, sans-serif;\n}\n\n.OT_root ul, .OT_root ol {\n margin: 1em 1em 1em 2em;\n}\n\n.OT_root ol {\n list-style: decimal outside;\n}\n\n.OT_root ul {\n list-style: disc outside;\n}\n\n.OT_root dl {\n margin: 4px;\n}\n\n.OT_root dl dt,\n.OT_root dl dd {\n float: left;\n margin: 0;\n padding: 0;\n}\n\n.OT_root dl dt {\n clear: left;\n text-align: right;\n width: 50px;\n}\n\n.OT_root dl dd {\n margin-left: 10px;\n}\n\n.OT_root img {\n border: 0 none;\n}\n\n/* Modal dialog styles */\n\n/* Modal dialog styles */\n\n.OT_dialog-centering {\n display: table;\n width: 100%;\n height: 100%;\n}\n\n.OT_dialog-centering-child {\n display: table-cell;\n vertical-align: middle;\n}\n\n.OT_dialog {\n position: relative;\n\n box-sizing: border-box;\n max-width: 576px;\n margin-right: auto;\n margin-left: auto;\n padding: 36px;\n text-align: center; /* centers all the inline content */\n\n background-color: #363636;\n color: #fff;\n box-shadow: 2px 4px 6px #999;\n font-family: 'Didact Gothic', sans-serif;\n font-size: 13px;\n line-height: 1.4;\n}\n\n.OT_dialog * {\n font-family: inherit;\n box-sizing: inherit;\n}\n\n.OT_closeButton {\n color: #999999;\n cursor: pointer;\n font-size: 32px;\n line-height: 36px;\n position: absolute;\n right: 18px;\n top: 0;\n}\n\n.OT_dialog-messages {\n text-align: center;\n}\n\n.OT_dialog-messages-main {\n margin-bottom: 36px;\n line-height: 36px;\n\n font-weight: 300;\n font-size: 24px;\n}\n\n.OT_dialog-messages-minor {\n margin-bottom: 18px;\n\n font-size: 13px;\n line-height: 18px;\n color: #A4A4A4;\n}\n\n.OT_dialog-messages-minor strong {\n color: #ffffff;\n}\n\n.OT_dialog-actions-card {\n display: inline-block;\n}\n\n.OT_dialog-button-title {\n margin-bottom: 18px;\n line-height: 18px;\n\n font-weight: 300;\n text-align: center;\n font-size: 14px;\n color: #999999;\n}\n.OT_dialog-button-title label {\n color: #999999;\n}\n\n.OT_dialog-button-title a,\n.OT_dialog-button-title a:link,\n.OT_dialog-button-title a:active {\n color: #02A1DE;\n}\n\n.OT_dialog-button-title strong {\n color: #ffffff;\n font-weight: 100;\n display: block;\n}\n\n.OT_dialog-button {\n display: inline-block;\n\n margin-bottom: 18px;\n padding: 0 1em;\n\n background-color: #1CA3DC;\n text-align: center;\n cursor: pointer;\n}\n\n.OT_dialog-button:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n.OT_dialog-button-large {\n line-height: 36px;\n padding-top: 9px;\n padding-bottom: 9px;\n\n font-weight: 100;\n font-size: 24px;\n}\n\n.OT_dialog-button-small {\n line-height: 18px;\n padding-top: 9px;\n padding-bottom: 9px;\n\n background-color: #444444;\n color: #999999;\n font-size: 16px;\n}\n\n.OT_dialog-progress-bar {\n display: inline-block; /* prevents margin collapse */\n width: 100%;\n margin-top: 5px;\n margin-bottom: 41px;\n\n border: 1px solid #4E4E4E;\n height: 8px;\n}\n\n.OT_dialog-progress-bar-fill {\n height: 100%;\n\n background-color: #29A4DA;\n}\n\n/* Helpers */\n\n.OT_centered {\n position: fixed;\n left: 50%;\n top: 50%;\n margin: 0;\n}\n\n.OT_dialog-hidden {\n display: none;\n}\n\n.OT_dialog-button-block {\n display: block;\n}\n\n.OT_dialog-no-natural-margin {\n margin-bottom: 0;\n}\n\n/* Publisher and Subscriber styles */\n\n.OT_publisher, .OT_subscriber {\n position: relative;\n min-width: 48px;\n min-height: 48px;\n}\n\n.OT_publisher .OT_video-element,\n.OT_subscriber .OT_video-element {\n display: block;\n position: absolute;\n width: 100%;\n height: 100%;\n\n transform-origin: 0 0;\n}\n\n/* Styles that are applied when the video element should be mirrored */\n.OT_publisher.OT_mirrored .OT_video-element {\n transform: scale(-1, 1);\n transform-origin: 50% 50%;\n}\n\n.OT_subscriber_error {\n background-color: #000;\n color: #fff;\n text-align: center;\n}\n\n.OT_subscriber_error > p {\n padding: 20px;\n}\n\n/* The publisher/subscriber name/mute background */\n.OT_publisher .OT_bar,\n.OT_subscriber .OT_bar,\n.OT_publisher .OT_name,\n.OT_subscriber .OT_name,\n.OT_publisher .OT_archiving,\n.OT_subscriber .OT_archiving,\n.OT_publisher .OT_archiving-status,\n.OT_subscriber .OT_archiving-status,\n.OT_publisher .OT_archiving-light-box,\n.OT_subscriber .OT_archiving-light-box {\n -ms-box-sizing: border-box;\n box-sizing: border-box;\n top: 0;\n left: 0;\n right: 0;\n display: block;\n height: 34px;\n position: absolute;\n}\n\n.OT_publisher .OT_bar,\n.OT_subscriber .OT_bar {\n background: rgba(0, 0, 0, 0.4);\n}\n\n.OT_publisher .OT_edge-bar-item,\n.OT_subscriber .OT_edge-bar-item {\n z-index: 1; /* required to get audio level meter underneath */\n}\n\n/* The publisher/subscriber name panel/archiving status bar */\n.OT_publisher .OT_name,\n.OT_subscriber .OT_name {\n background-color: transparent;\n color: #ffffff;\n font-size: 15px;\n line-height: 34px;\n font-weight: normal;\n padding: 0 4px 0 36px;\n}\n\n.OT_publisher .OT_archiving-status,\n.OT_subscriber .OT_archiving-status {\n background: rgba(0, 0, 0, 0.4);\n top: auto;\n bottom: 0;\n left: 34px;\n padding: 0 4px;\n color: rgba(255, 255, 255, 0.8);\n font-size: 15px;\n line-height: 34px;\n font-weight: normal;\n}\n\n.OT_micro .OT_archiving-status,\n.OT_micro:hover .OT_archiving-status,\n.OT_mini .OT_archiving-status,\n.OT_mini:hover .OT_archiving-status {\n display: none;\n}\n\n.OT_publisher .OT_archiving-light-box,\n.OT_subscriber .OT_archiving-light-box {\n background: rgba(0, 0, 0, 0.4);\n top: auto;\n bottom: 0;\n right: auto;\n width: 34px;\n height: 34px;\n}\n\n.OT_archiving-light {\n width: 7px;\n height: 7px;\n border-radius: 30px;\n position: absolute;\n top: 14px;\n left: 14px;\n background-color: #575757;\n box-shadow: 0 0 5px 1px #575757;\n}\n\n.OT_archiving-light.OT_active {\n background-color: #970d13;\n -webkit-animation: OT_pulse 1.3s ease-in;\n -moz-animation: OT_pulse 1.3s ease-in;\n -webkit-animation: OT_pulse 1.3s ease-in;\n -webkit-animation-iteration-count: infinite;\n -moz-animation-iteration-count: infinite;\n -webkit-animation-iteration-count: infinite;\n}\n\n.OT_mini .OT_bar,\n.OT_bar.OT_mode-mini,\n.OT_bar.OT_mode-mini-auto {\n bottom: 0;\n height: auto;\n}\n\n.OT_mini .OT_name.OT_mode-off,\n.OT_mini .OT_name.OT_mode-on,\n.OT_mini .OT_name.OT_mode-auto,\n.OT_mini:hover .OT_name.OT_mode-auto {\n display: none;\n}\n\n.OT_publisher .OT_name,\n.OT_subscriber .OT_name {\n left: 10px;\n right: 37px;\n height: 34px;\n padding-left: 0;\n}\n\n.OT_publisher .OT_mute,\n.OT_subscriber .OT_mute {\n border: none;\n cursor: pointer;\n display: block;\n position: absolute;\n text-align: center;\n text-indent: -9999em;\n background-color: transparent;\n background-repeat: no-repeat;\n}\n\n.OT_publisher .OT_mute,\n.OT_subscriber .OT_mute {\n right: 0;\n top: 0;\n border-left: 1px solid rgba(255, 255, 255, 0.2);\n height: 36px;\n width: 37px;\n}\n\n.OT_mini .OT_mute,\n.OT_publisher.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold,\n.OT_subscriber.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold {\n top: 50%;\n left: 50%;\n right: auto;\n margin-top: -18px;\n margin-left: -18.5px;\n border-left: none;\n}\n\n.OT_publisher .OT_mute {\n background-image: url(" + ___CSS_LOADER_URL___0___ + ");\n background-position: 9px 5px;\n}\n\n.OT_publisher .OT_mute.OT_active {\n background-image: url(" + ___CSS_LOADER_URL___1___ + ");\n background-position: 9px 4px;\n}\n\n.OT_subscriber .OT_mute {\n background-image: url(" + ___CSS_LOADER_URL___2___ + ");\n background-position: 8px 7px;\n}\n\n.OT_subscriber .OT_mute.OT_active {\n background-image: url(" + ___CSS_LOADER_URL___3___ + ");\n background-position: 7px 7px;\n}\n\n/**\n * Styles for display modes\n *\n * Note: It's important that these completely control the display and opacity\n * attributes, no other selectors should atempt to change them.\n */\n\n/* Default display mode transitions for various chrome elements */\n.OT_publisher .OT_edge-bar-item,\n.OT_subscriber .OT_edge-bar-item {\n transition-property: top, bottom, opacity;\n transition-duration: 0.5s;\n transition-timing-function: ease-in;\n}\n\n.OT_publisher .OT_edge-bar-item.OT_mode-off,\n.OT_subscriber .OT_edge-bar-item.OT_mode-off,\n.OT_publisher .OT_edge-bar-item.OT_mode-auto,\n.OT_subscriber .OT_edge-bar-item.OT_mode-auto,\n.OT_publisher .OT_edge-bar-item.OT_mode-mini-auto,\n.OT_subscriber .OT_edge-bar-item.OT_mode-mini-auto {\n top: -25px;\n opacity: 0;\n}\n\n.OT_publisher .OT_edge-bar-item.OT_mode-off,\n.OT_subscriber .OT_edge-bar-item.OT_mode-off {\n display: none;\n}\n\n.OT_mini .OT_mute.OT_mode-auto,\n.OT_publisher .OT_mute.OT_mode-mini-auto,\n.OT_subscriber .OT_mute.OT_mode-mini-auto {\n top: 50%;\n}\n\n.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-off,\n.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-off,\n.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,\n.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,\n.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto,\n.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto {\n top: auto;\n bottom: -25px;\n}\n\n.OT_publisher .OT_edge-bar-item.OT_mode-on,\n.OT_subscriber .OT_edge-bar-item.OT_mode-on,\n.OT_publisher .OT_edge-bar-item.OT_mode-auto.OT_mode-on-hold,\n.OT_subscriber .OT_edge-bar-item.OT_mode-auto.OT_mode-on-hold,\n.OT_publisher:hover .OT_edge-bar-item.OT_mode-auto,\n.OT_subscriber:hover .OT_edge-bar-item.OT_mode-auto,\n.OT_publisher:hover .OT_edge-bar-item.OT_mode-mini-auto,\n.OT_subscriber:hover .OT_edge-bar-item.OT_mode-mini-auto {\n top: 0;\n opacity: 1;\n}\n\n.OT_mini .OT_mute.OT_mode-on,\n.OT_mini:hover .OT_mute.OT_mode-auto,\n.OT_mute.OT_mode-mini,\n.OT_root:hover .OT_mute.OT_mode-mini-auto {\n top: 50%;\n}\n\n.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,\n.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,\n.OT_publisher:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,\n.OT_subscriber:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto {\n top: auto;\n bottom: 0;\n opacity: 1;\n}\n\n\n/* Contains the video element, used to fix video letter-boxing */\n.OT_widget-container {\n position: absolute;\n background-color: #000000;\n overflow: hidden;\n}\n\n.OT_hidden-audio {\n position: absolute !important;\n height: 1px !important;\n width: 1px !important;\n}\n\n/* Load animation */\n.OT_root .OT_video-loading {\n position: absolute;\n z-index: 1;\n width: 100%;\n height: 100%;\n display: none;\n\n background-color: rgba(0, 0, 0, .75);\n}\n\n.OT_root .OT_video-loading .OT_video-loading-spinner {\n background: url(" + ___CSS_LOADER_URL___4___ + ") no-repeat;\n position: absolute;\n width: 32px;\n height: 32px;\n left: 50%;\n top: 50%;\n margin-left: -16px;\n margin-top: -16px;\n animation: OT_spin 2s linear infinite;\n}\n@keyframes OT_spin {\n 100% {\n transform: rotate(360deg);\n }\n}\n\n.OT_publisher.OT_loading .OT_video-loading,\n.OT_subscriber.OT_loading .OT_video-loading {\n display: block;\n}\n\n.OT_publisher.OT_loading .OT_video-element,\n.OT_subscriber.OT_loading .OT_video-element {\n /*display: none;*/\n}\n\n.OT_video-centering {\n display: table;\n width: 100%;\n height: 100%;\n}\n\n.OT_video-container {\n display: table-cell;\n vertical-align: middle;\n}\n\n.OT_video-poster {\n position: absolute;\n z-index: 1;\n width: 100%;\n height: 100%;\n display: none;\n\n opacity: .25;\n\n background-repeat: no-repeat;\n background-image: url(" + ___CSS_LOADER_URL___5___ + ");\n background-size: auto 76%;\n}\n\n.OT_fit-mode-cover .OT_video-element {\n object-fit: cover;\n}\n\n/* Workaround for iOS freezing issue when cropping videos */\n/* https://bugs.webkit.org/show_bug.cgi?id=176439 */\n@media only screen\n and (orientation: portrait) {\n .OT_subscriber.OT_ForceContain.OT_fit-mode-cover .OT_video-element {\n object-fit: contain !important;\n }\n}\n\n.OT_fit-mode-contain .OT_video-element {\n object-fit: contain;\n}\n\n.OT_fit-mode-cover .OT_video-poster {\n background-position: center bottom;\n}\n\n.OT_fit-mode-contain .OT_video-poster {\n background-position: center;\n}\n\n.OT_audio-level-meter {\n position: absolute;\n width: 25%;\n max-width: 224px;\n min-width: 21px;\n top: 0;\n right: 0;\n overflow: hidden;\n}\n\n.OT_audio-level-meter:before {\n /* makes the height of the container equals its width */\n content: '';\n display: block;\n padding-top: 100%;\n}\n\n.OT_audio-level-meter__audio-only-img {\n position: absolute;\n top: 22%;\n right: 15%;\n width: 40%;\n\n opacity: .7;\n\n background: url(" + ___CSS_LOADER_URL___6___ + ") no-repeat center;\n}\n\n.OT_audio-level-meter__audio-only-img:before {\n /* makes the height of the container equals its width */\n content: '';\n display: block;\n padding-top: 100%;\n}\n\n.OT_audio-level-meter__value {\n will-change: transform;\n position: absolute;\n top: -100%;\n right: -100%;\n width: 200%;\n height: 200%;\n transform: scale(0);\n border-radius: 50%;\n background-image: radial-gradient(circle, rgba(151, 206, 0, 1) 0%, rgba(151, 206, 0, 0) 100%);\n}\n\n.OT_audio-level-meter.OT_mode-off {\n display: none;\n}\n\n.OT_audio-level-meter.OT_mode-on,\n.OT_audio-only .OT_audio-level-meter.OT_mode-auto {\n display: block;\n}\n\n.OT_audio-only.OT_publisher .OT_video-element,\n.OT_audio-only.OT_subscriber .OT_video-element {\n display: none;\n}\n\n\n.OT_video-disabled-indicator {\n opacity: 1;\n border: none;\n display: none;\n position: absolute;\n background-color: transparent;\n background-repeat: no-repeat;\n background-position: bottom right;\n pointer-events: none;\n top: 0;\n left: 0;\n bottom: 3px;\n right: 3px;\n}\n\n.OT_video-disabled {\n background-image: url(" + ___CSS_LOADER_URL___7___ + ");\n background-size: 33px auto;\n}\n\n.OT_video-disabled-warning {\n background-image: url(" + ___CSS_LOADER_URL___8___ + ");\n background-size: 33px auto;\n}\n\n.OT_video-disabled-indicator.OT_active {\n display: block;\n}\n\n.OT_audio-blocked-indicator {\n opacity: 1;\n border: none;\n display: none;\n position: absolute;\n background-color: transparent;\n background-repeat: no-repeat;\n background-position: center;\n pointer-events: none;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n.OT_audio-blocked {\n background-image: url(" + ___CSS_LOADER_URL___9___ + ");\n background-size: 90px auto;\n}\n\n.OT_container-audio-blocked {\n cursor: pointer;\n}\n\n.OT_container-audio-blocked.OT_mini .OT_edge-bar-item {\n display: none;\n}\n\n.OT_container-audio-blocked .OT_mute {\n display: none;\n}\n\n.OT_audio-blocked-indicator.OT_active {\n display: block;\n}\n\n.OT_video-unsupported {\n opacity: 1;\n border: none;\n display: none;\n position: absolute;\n background-color: transparent;\n background-repeat: no-repeat;\n background-position: center;\n background-image: url(" + ___CSS_LOADER_URL___10___ + ");\n background-size: 58px auto;\n pointer-events: none;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n margin-top: -30px;\n}\n\n.OT_video-unsupported-bar {\n display: none;\n position: absolute;\n width: 192%; /* copy the size of the audio meter bar for symmetry */\n height: 192%;\n top: -96% /* half of the size */;\n left: -96%;\n border-radius: 50%;\n\n background-color: rgba(0, 0, 0, .8);\n}\n\n.OT_video-unsupported-img {\n display: none;\n position: absolute;\n top: 11%;\n left: 15%;\n width: 70%;\n opacity: .7;\n background-image: url(" + ___CSS_LOADER_URL___10___ + ");\n background-repeat: no-repeat;\n background-position: center;\n background-size: 100% auto;\n}\n\n.OT_video-unsupported-img:before {\n /* makes the height of the container 93% of its width (90/97 px) */\n content: '';\n display: block;\n padding-top: 93%;\n}\n\n.OT_video-unsupported-text {\n display: flex;\n justify-content: center;\n align-items: center;\n text-align: center;\n height: 100%;\n margin-top: 40px;\n}\n\n.OT_video-unsupported.OT_active {\n display: block;\n}\n\n.OT_mini .OT_video-unsupported,\n.OT_micro .OT_video-unsupported {\n position: absolute;\n width: 25%;\n max-width: 224px;\n min-width: 21px;\n top: 0;\n left: 0;\n overflow: hidden;\n background: none;\n bottom: auto;\n right: auto;\n margin: auto;\n}\n\n.OT_mini .OT_video-unsupported:before,\n.OT_micro .OT_video-unsupported:before {\n /* makes the height of the container equals its width */\n content: '';\n display: block;\n padding-top: 100%;\n}\n\n.OT_mini .OT_video-unsupported-bar,\n.OT_micro .OT_video-unsupported-bar,\n.OT_mini .OT_video-unsupported-img,\n.OT_micro .OT_video-unsupported-img {\n display: block;\n}\n\n.OT_mini .OT_video-unsupported-text,\n.OT_micro .OT_video-unsupported-text {\n display: none;\n}\n\n.OT_hide-forced {\n display: none;\n}\n\n.OT_ModalDialog {\n position: fixed;\n top: 0px;\n left: 0px;\n right: 0px;\n bottom: 0px;\n z-index: 1000;\n background-color: rgba(0, 0, 0, 0.2);\n}\n\n#tb_alert {\n position: fixed;\n width: 570px;\n font-family: \"Lucida Grande\", Arial;\n top: 50%;\n left: 50%;\n margin-top: -75px;\n margin-left: -285px;\n}\n\n#tb_alert * {\n box-sizing: border-box;\n}\n\n#tb_alert>div,\n#tb_alert>div>div {\n box-shadow: 0 2px 2px rgba(0, 0, 0, 0.15);\n border: 1px solid rgba(255, 255, 255, 0.3);\n}\n\n#tb_alert>div {\n border-top-right-radius: 5px;\n border-bottom-right-radius: 5px;\n background-color: gray;\n position: relative;\n padding: 30px;\n overflow: visible;\n margin-left: 70px;\n color: #c8c5c5;\n border-bottom-color: rgba(255, 255, 255, 0.1);\n border-left-color: rgba(255, 255, 255, 0.1);\n\n\n background-color: #333;\n\n background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.2) 25%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.2) 75%, rgba(0, 0, 0, 0.2)), linear-gradient(45deg, rgba(0, 0, 0, 0.2) 25%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.2) 75%, rgba(0, 0, 0, 0.2));\n\n background-image: -ms-linear-gradient(45deg, rgba(0, 0, 0, 0.2) 25%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.2) 75%, rgba(0, 0, 0, 0.2)), -ms-linear-gradient(45deg, rgba(0, 0, 0, 0.2) 25%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.2) 75%, rgba(0, 0, 0, 0.2));\n\n background-size: 4px 4px;\n background-position: 0 0, 2px 2px;\n}\n\n#tb_alert>div>div {\n background-color: #9c1408;\n background-image: linear-gradient(0deg, #dd0c0a 0%, #9c1408 100%);\n\n margin: -1px 0;\n\n font-family: MS Trebuchet;\n font-weight: bold;\n font-size: 60px;\n text-shadow: -1px -1px 2px rgba(0, 0, 0, 0.5);\n\n color: white;\n width: 65px;\n position: absolute;\n padding: 42px 0;\n text-align: center;\n left: -70px;\n top: 0;\n bottom: 0;\n border-top-left-radius: 5px;\n border-bottom-left-radius: 5px;\n\n border-bottom-color: transparent;\n border-left: none;\n border-right: none;\n}\n\n#tb_alert>div>div:after,\n#tb_alert>div>div:before {\n content: \" \";\n position: absolute;\n right: -24px;\n top: 40%;\n border: 12px solid transparent;\n border-left-color: #9c1408;\n}\n\n#tb_alert>div>div:before {\n border-left-color: #bc3428;\n top: 39%;\n}\n\n#tb_alert>div>h1 {\n font-size: 24px;\n padding-bottom: 5px;\n color: #aaa;\n font-weight: bold;\n position: relative;\n text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3);\n margin: 0;\n}\n\n#tb_alert>div>p {\n margin: 0;\n text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3);\n font-size: 14px;\n line-height: 1.3em;\n}\n\n#tb_alert>div>.continue-text {\n margin-top: 12px\n}\n\n#tb_alert>div a {\n color: #a7d8df;\n text-decoration: none;\n}\n\n#tb_alert>div>ul {\n margin-bottom: 0;\n}\n\n#tb_alert>div>h1::before {\n content: attr(data-text);\n position: absolute;\n left: 0;\n color: white;\n -webkit-mask-image: -webkit-gradient(\n linear,\n left top, left bottom,\n from(rgba(0, 0, 0, 1)),\n color-stop(60%, rgba(0, 0, 0, 0)),\n to(rgba(0, 0, 0, 0))\n );\n}\n\n#tb_alert button {\n position: absolute;\n right: 8px;\n top: 5px;\n background-color: #000;\n color: #666;\n border: none;\n font-size: 20px;\n /** Hack to bring it up to the proper line top */\n line-height: 14px;\n padding: 0;\n padding-bottom: 3px;\n cursor: pointer;\n}\n\n#tb_alert #section-mobile-browser,\n#tb_alert #section-supported-mobile-browser {\n width: 200px;\n top: 0px;\n left: 25%;\n margin-top: 0;\n margin-left: 0;\n}\n\n@media all and (max-height: 300px) {\n #tb_alert {\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n margin-left: 0;\n margin-top: 0;\n }\n #tb_alert #section-mobile-browser,\n #tb_alert #section-supported-mobile-browser {\n margin-left: 0;\n margin-top: 10px;\n left: 0;\n }\n}\n\n#tb_alert #section-normal-install,\n#tb_alert #section-upgrade-install,\n#tb_alert #section-mobile-browser,\n#tb_alert #section-supported-mobile-browser {\n display: none;\n}\n", ""]);
/***/ }),
/* 312 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function (useSourceMap) {
var list = []; // return the list of modules as css string
list.toString = function toString() {
return this.map(function (item) {
var content = cssWithMappingToString(item, useSourceMap);
if (item[2]) {
return '@media ' + item[2] + '{' + content + '}';
} else {
return content;
}
}).join('');
}; // import a list of modules into the list
list.i = function (modules, mediaQuery) {
if (typeof modules === 'string') {
modules = [[null, modules, '']];
}
var alreadyImportedModules = {};
for (var i = 0; i < this.length; i++) {
var id = this[i][0];
if (id != null) {
alreadyImportedModules[id] = true;
}
}
for (i = 0; i < modules.length; i++) {
var item = modules[i]; // skip already imported module
// this implementation is not 100% perfect for weird media query combinations
// when a module is imported multiple times with different media queries.
// I hope this will never occur (Hey this way we have smaller bundles)
if (item[0] == null || !alreadyImportedModules[item[0]]) {
if (mediaQuery && !item[2]) {
item[2] = mediaQuery;
} else if (mediaQuery) {
item[2] = '(' + item[2] + ') and (' + mediaQuery + ')';
}
list.push(item);
}
}
};
return list;
};
function cssWithMappingToString(item, useSourceMap) {
var content = item[1] || '';
var cssMapping = item[3];
if (!cssMapping) {
return content;
}
if (useSourceMap && typeof btoa === 'function') {
var sourceMapping = toComment(cssMapping);
var sourceURLs = cssMapping.sources.map(function (source) {
return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */';
});
return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
}
return [content].join('\n');
} // Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
// eslint-disable-next-line no-undef
var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;
return '/*# ' + data + ' */';
}
/***/ }),
/* 313 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = function escape(url, needQuotes) {
if (typeof url !== 'string') {
return url;
} // If url is already wrapped in quotes, remove them
if (/^['"].*['"]$/.test(url)) {
url = url.slice(1, -1);
} // Should url be wrapped?
// See https://drafts.csswg.org/css-values-3/#urls
if (/["'() \t\n]/.test(url) || needQuotes) {
return '"' + url.replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
}
return url;
};
/***/ }),
/* 314 */
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAcCAMAAAC02HQrAAAA1VBMVEUAAAD3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pn3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pn3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj3+Pj39/j3+Pj3+Pn4+Pk/JRMlAAAAQ3RSTlMABAUHCQoLDhAQERwdHiAjLjAxOD9ASFBRVl1mbnZ6fH2LjI+QkaWqrrC1uLzAwcXJycrL1NXj5Ofo6u3w9fr7/P3+d4M3+QAAAQBJREFUGBlVwYdCglAABdCLlr5Unijm3hMUtBzlBLSr//9JgUToOQgVJgceJgU8aHgMeA38K50ZOpcQmTPwcyXn+JM8M3JJIqQypiIkeXelTyIkGZPwKS1NMia1lgKTVkaE3oQQGYsmHNqSMWnTgUFbMiZtGlD2dpaxrL1XgM0i4ZK8MeAmFhsAs29MGZniawagS63oMOQUNXYB5D0D1RMDpyoMLw/fiE2og/V+PVDR5AiBl0/2Uwik+vx4xV3a5G5Ye68Nd1czjUjZckm6VhmPciRzeCZICjwTJAViQq+3e+St167rAoHK8sLYZVkBYPCZAZ/eGa+2R5LH7Wrc0YFf/O9J3yBDFaoAAAAASUVORK5CYII="
/***/ }),
/* 315 */
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAdCAYAAABFRCf7AAADcElEQVRIiaWVXWhcRRTHf7NNd2aDtUKMIjTpg4ufFIuiUOmDEWm0Vi3VYhXRqIggQh4sWJFSig9+oOhTKSpIRUWMIBIr2kptoTbgU6ooxCiIjR+14kcJmf9sNceHnd3ebnc3Uv9wuXfOzPzmnDMz5zozGwdWAbc65w5RUJQ8cC2wDJgFJioh/MJCMrNxq2vOzK4HmIvRRemxKP0RJWt53o7S+d2Yzsx6gQ+AIUDAnUqpBLzXZd4RYFUlhB/bdZacc3PAOmAcCMC7wfvFwLNdoAPAyx09bXyYWRl4E7gDmAdGlNKFwLYu8GolhO9O87RJd64GbMrgEvB68P4osMWdXLtVV7czlooNpVRWSs8DO7NpR/B+3rBHsvetCgtCMTxwQCm9BbyQrc8F7/uBex3uRCeXO0PrUZ4NfKyUPgWeyj3bg/crDNsIRGwBaJQGorQ3Svdn2wHgc2BUKb0DPJHtjwfvbwRucc7tz+N+i9LFUdoXpfVN36I0CVwBTFI/q9e1LPxT8P4qYEdu70q12mYzWw1MYQzjeJF6zq+shHC4B7jklOBPP/TzSunh4P0DwKvAfb5c9krpe+CcwsEoZdbhEvBM9wxRAl5RShcA9wAngE3B+8tLpdLuwrhp4MNmK0pfRWkySr7NXS8+L5nZbWZWy/Vin1IaitJnUTqvwevJ71lgSSWEFKUfHG7Q2m/xqFJaGry/GXgfGPLl8mJgrXPur2JoUC8Qy3OpG+sAbGhEKT0ErAWOA6uBPWbW1wr9BOgFbgKezot0kAPYqJQA1gC/A9cA+82svzksSn1R+jNKX0SpnM/e1x3yqig92JhrZivM7FjO8bSZLSuCR/Ok16K0KMNHojQWpYko7Y7S1igN5PE3ROl4lNaZ2UVmNpPBU01orvZvZPCeKFXbBR+lEKVtUapFaSZKg9njqpl9aWYTrmXCImA7sCWb9lK/jj9TrwkrgA1AH3AQuKsSwkzbrLfxpgpsBtYDxf/R3xm2ExirhNCuHHZXTsmRwiat+S/zSt06eysVA/4pmGr/G3qm6ik28v29FKgCg8BS6pvS0KNRGgZ+Bb4FpsxsOkfUlMuwDcBWYOUZOHYM2AU8WQmhBifDv70O7PjX7KZ+4G7g3FM8zd6uBIaBy4AqxnIcZwFLCovPAhE4Sj38b4BDwEeVEFKD9S94Khjn486v3QAAAABJRU5ErkJggg=="
/***/ }),
/* 316 */
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAATCAYAAAB7u5a2AAABx0lEQVQ4jaWUv48NURiGn3ONmCs32ZBd28ht1gqyZAkF21ylQkEiSp2ehpDlD1BoFGqqVdJohYKI7MaPxMoVNghCWMF+7ybLUewnOXfcMWO9yeQ857zne8+XmZOBGjJpr0kvTIomvTZpS526UCO4DUwD64FjwCFgqZnnR+oc8LfgzKQ73vGsr42ZtGjSQFV9o8KfBCacZwCaef4YmAf2rzjcpN3A2WSpm/AssKcqPDNpDBjs410CViXzTwk/A7b1C4wxDgOngAsZcAXY2buDfp/6S4F3lDS8DjgBzDWAjX/Y/e/QgYS/AhsKHa+OMQ6GEJ4Cj4BOAxgq6aCowyZtdf4OtAr+FHDO+R4wWnVbihr3cQnICt4boO38GWj9a/icjwOACt4m4K3zEPA+AxaAtTWCnwN3lzHkEL8V/OPAGud9wK2GF9XR1Wae/1zG2AI+pGYI4VUIoRtjHAc2A9cz4LRPevYCZ+i9/4sJt4GXJU10gaPAzdI2TTro/5Tfz8XEe2LSZGmxq/SDNvP8BnA5WRrx4BwYBe6vONx1EnjovGvBLAAd4Adwuyq8UiaNmDTvr+a8SQ9MuvbfwckBHZPe+QEfTdpep+4XZmPBHiHgz74AAAAASUVORK5CYII="
/***/ }),
/* 317 */
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAUCAYAAACXtf2DAAACtklEQVQ4jZ2VSYiURxTHf+/T9Nc9iRrBuYySmIsXUU9iFMEFERRBvAjJLUQi5ioiHvSScfTmgqC4XAT1ZIgLuJHkICaaQAgKI2hAUBT30bjUq7bbv4eukXK029F3+eqtv/fqK6qQdEnSNUmT6CDB/bvgfjO4N9zj2RD8007xg1IABkwEzkma0qb4PGAPMBZYLtSD8eNwAEjqTlNI0gNJM4YU7w7ut4O7gvuhZFsR3C8NC5BBLiTIY0mzM8AvqbiC++pk+zLpE95XuwAws3vAQuBPYDRwWtL84P4tsDSLv5oaug4EYOawAMF9jMdoLxqNZcDvQA04UVYqL4G/svj7AF21mhJscrvCksYBFO7xc2AAGGg2mrdjvf4rcAyomNn+slLZmUEGBgsYdh945xZJmgvckDSrEJpK6ySBgV6q12O8ABwGPjGzfWWlsjdN9rpjoSfA+DYDXARGAksK4Is3XC1Ub4z1f4CDQGFmu6tleQSYk0U+p7WVeefLJc00s4fAeWB6Qeunvj0m2ugx9gO7kmlrtSxvBfcy6fXUZS6rgG/S+jLQUwCVNmMC9HqM14EtSe+rluWazN8YEv8IqKZ1E1qnaIDO0ucx3gX6kv6TpM3AM+D/IbGjgP60/gq4WQA33gMA2OQxPgHWJX1ttSwL4FAeZGYLgB2SasBs4A8L7qOBf9M0uXQB3a+TMYSmVctyDrA9mfcBK82smSdKWgCcAaa1bTm4fxbc/8uuCQX3RanAD5Ka6Wo5IGnE0HxJPZ03pQX5Org3MsD3AO5xXLPZXJ9BjkrqdFg6QjZkgG3Jtsw93pG0VFI9QU5K6voYQBHcTydAfwheBI9HgvvPAJIWS3qeIL9JGvUxkO7gfi1BrqTvwkG/pPmSnibIqTzXPgAyEVgBjAEu1qrVPbk/PVTHgb/NbPGg/RVIzOQqzSTBaQAAAABJRU5ErkJggg=="
/***/ }),
/* 318 */
/***/ (function(module, exports) {
module.exports = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0yMCAtMjAgMjQwIDI0MCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjA4Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxIiB4Mj0iMCIgeTE9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMDgiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjE2Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgyPSIwIiB5MT0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii4xNiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMzMiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iZSIgeDI9IjEiIHkxPSIxIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjMzIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii42NiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJmIiB4Mj0iMSIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuNjYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxtYXNrIGlkPSJnIj48ZyBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjQwIj48cGF0aCBzdHJva2U9InVybCgjYSkiIGQ9Ik04Ni42LTUwYTEwMCAxMDAgMCAwMTAgMTAwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDAgMTAwKSIvPjxwYXRoIHN0cm9rZT0idXJsKCNiKSIgZD0iTTg2LjYgNTBBMTAwIDEwMCAwIDAxMCAxMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2MpIiBkPSJNMCAxMDBhMTAwIDEwMCAwIDAxLTg2LjYtNTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2QpIiBkPSJNLTg2LjYgNTBhMTAwIDEwMCAwIDAxMC0xMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2UpIiBkPSJNLTg2LjYtNTBBMTAwIDEwMCAwIDAxMC0xMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2YpIiBkPSJNMC0xMDBhMTAwIDEwMCAwIDAxODYuNiA1MCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAwIDEwMCkiLz48L2c+PC9tYXNrPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4PSItMjAiIHk9Ii0yMCIgbWFzaz0idXJsKCNnKSIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg=="
/***/ }),
/* 319 */
/***/ (function(module, exports) {
module.exports = "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDcxIDQ2NCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48bGluZWFyR3JhZGllbnQgaWQ9ImEiIHgxPSIwIiB4Mj0iMCIgeTE9IjAiIHkyPSIxIj48c3RvcCBvZmZzZXQ9IjY2LjY2JSIgc3RvcC1jb2xvcj0iI2ZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBmaWxsPSJ1cmwoI2EpIiBkPSJNNzkgMzA4YzE0LjI1LTYuNSA1NC4yNS0xOS43NSA3MS0yOSA5LTMuMjUgMjUtMjEgMjUtMjFzMy43NS0xMyAzLTIyYy0xLjc1LTYuNzUtMTUtNDMtMTUtNDMtMi41IDMtNC43NDEgMy4yNTktNyAxLTMuMjUtNy41LTIwLjUtNDQuNS0xNi01NyAxLjI1LTcuNSAxMC02IDEwLTYtMTEuMjUtMzMuNzUtOC02Ny04LTY3cy4wNzMtNy4zNDYgNi0xNWMtMy40OC42MzctOSA0LTkgNCAyLjU2My0xMS43MjcgMTUtMjEgMTUtMjEgLjE0OC0uMzEyLTEuMzIxLTEuNDU0LTEwIDEgMS41LTIuNzggMTYuNjc1LTguNjU0IDMwLTExIDMuNzg3LTkuMzYxIDEyLjc4Mi0xNy4zOTggMjItMjItMi4zNjUgMy4xMzMtMyA2LTMgNnMxNS42NDctOC4wODggNDEtNmMtMTkuNzUgMi0yNCA2LTI0IDZzNzQuNS0xMC43NSAxMDQgMzdjNy41IDkuNSAyNC43NSA1NS43NSAxMCA4OSAzLjc1LTEuNSA0LjUtNC41IDkgMSAuMjUgMTQuNzUtMTEuNSA2My0xOSA2Mi0yLjc1IDEtNC0zLTQtMy0xMC43NSAyOS41LTE0IDM4LTE0IDM4LTIgNC4yNS0zLjc1IDE4LjUtMSAyMiAxLjI1IDQuNSAyMyAyMyAyMyAyM2wxMjcgNTNjMzcgMzUgMjMgMTM1IDIzIDEzNUwwIDQ2NHMtMy05Ni43NSAxNC0xMjBjNS4yNS02LjI1IDIxLjc1LTE5Ljc1IDY1LTM2eiIvPjwvc3ZnPg=="
/***/ }),
/* 320 */
/***/ (function(module, exports) {
module.exports = "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzkgODYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTkuNzU3IDQwLjkyNGMzLjczOC01LjE5MSAxMi43MTEtNC4zMDggMTIuNzExLTQuMzA4IDIuMjIzIDMuMDE0IDUuMTI2IDI0LjU4NiAzLjYyNCAyOC43MTgtMS40MDEgMS4zMDEtMTEuNjExIDEuNjI5LTEzLjM4LTEuNDM2LTEuMjI2LTguODA0LTIuOTU1LTIyLjk3NS0yLjk1NS0yMi45NzV6bTU4Ljc4NSAwYy0zLjczNy01LjE5MS0xMi43MTEtNC4zMDgtMTIuNzExLTQuMzA4LTIuMjIzIDMuMDE0LTUuMTI2IDI0LjU4Ni0zLjYyNCAyOC43MTggMS40MDEgMS4zMDEgMTEuNjExIDEuNjI5IDEzLjM4LTEuNDM2IDEuMjI2LTguODA0IDIuOTU1LTIyLjk3NSAyLjk1NS0yMi45NzV6Ii8+PHBhdGggZD0iTTY4LjY0NyA1OC42Yy43MjktNC43NTMgMi4zOC05LjU2MSAyLjM4LTE0LjgwNCAwLTIxLjQxMi0xNC4xMTUtMzguNzctMzEuNTI4LTM4Ljc3LTE3LjQxMiAwLTMxLjUyNyAxNy4zNTgtMzEuNTI3IDM4Ljc3IDAgNC41NDEuNTE1IDguOTM2IDEuODAyIDEyLjk1IDEuNjk4IDUuMjk1LTUuNTQyIDYuOTkxLTYuNjE2IDIuMDczQzIuNDEgNTUuMzk0IDAgNTEuNzg3IDAgNDguMTAzIDAgMjEuNTM2IDE3LjY4NSAwIDM5LjUgMCA2MS4zMTYgMCA3OSAyMS41MzYgNzkgNDguMTAzYzAgLjcxOC0yLjg5OSA5LjY5My0zLjI5MiAxMS40MDgtLjc1NCAzLjI5My03Ljc1MSAzLjU4OS03LjA2MS0uOTEyeiIvPjxwYXRoIGQ9Ik01LjA4NCA1MS4zODVjLS44MDQtMy43ODIuNTY5LTcuMzM1IDMuMTM0LTcuOTIxIDIuNjM2LS42MDMgNS40ODUgMi4xNSA2LjI4OSA2LjEzMi43OTcgMy45NDgtLjc1MiA3LjQ1Ny0zLjM4OCA3Ljg1OS0yLjU2Ni4zOTEtNS4yMzctMi4zMTgtNi4wMzQtNi4wN3ptNjguODM0IDBjLjgwNC0zLjc4Mi0uNTY4LTcuMzM1LTMuMTMzLTcuOTIxLTIuNjM2LS42MDMtNS40ODUgMi4xNS02LjI4OSA2LjEzMi0uNzk3IDMuOTQ4Ljc1MiA3LjQ1NyAzLjM4OSA3Ljg1OSAyLjU2NS4zOTEgNS4yMzctMi4zMTggNi4wMzQtNi4wN3ptLTIuMDM4IDguMjg4Yy0uOTI2IDE5LjY1OS0xNS4xMTIgMjQuNzU5LTI1Ljg1OSAyMC40NzUtNS40MDUtLjYwNi0zLjAzNCAxLjI2Mi0zLjAzNCAxLjI2MiAxMy42NjEgMy41NjIgMjYuMTY4IDMuNDk3IDMxLjI3My0yMC41NDktLjU4NS00LjUxMS0yLjM3OS0xLjE4Ny0yLjM3OS0xLjE4N3oiLz48cGF0aCBkPSJNNDEuNjYyIDc4LjQyMmw3LjU1My41NWMxLjE5Mi4xMDcgMi4xMiAxLjE1MyAyLjA3MiAyLjMzNWwtLjEwOSAyLjczOGMtLjA0NyAxLjE4Mi0xLjA1MSAyLjA1NC0yLjI0MyAxLjk0NmwtNy41NTMtLjU1Yy0xLjE5MS0uMTA3LTIuMTE5LTEuMTUzLTIuMDcyLTIuMzM1bC4xMDktMi43MzdjLjA0Ny0xLjE4MiAxLjA1Mi0yLjA1NCAyLjI0My0xLjk0N3oiLz48L2c+PC9zdmc+"
/***/ }),
/* 321 */
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFIAAAAoCAYAAABtla08AAAINUlEQVR42u2aaUxUVxTHcRBmAAEBRVTK4sKwDIsg+wCK7CqIw1CN1YobbbS2qYlJ06Qx1UpdqMbYWq2pSzWmH6ytNbXWJY1Lq7VuqBERtW64V0XFLYae0/xvcp3MMAMzDz6IyT/ge2ce5/7ucpY3Ts3NzZ1ygF57AJ0gO0G2jyZPmdbFyclJSAV1EeoEaUUSLGdSV5KLLFxzFmA7QVqGqDqjixhWkxCVeyRVl38wM6bwj6yYItYK47BAuu9B0gCqs6Ng2r494KQtkj/Dz2jHraw6qw2fdSE4rNmcCPCvZONP8iF1I6kdBdMaQJWZLeJqRWa2kPJAxXY+GxE+zxLI03GRh8lGSwoi9WCY8FWlCEh+8JOnT7MfPGjMuXX7Tt61hoaCi/9cKmKdv3BxeEtim/UbNpnbQiqF4MmT7kqrbr4lkMcTo46TTSpJB5g+8NHuVWnWuaampvhmO/7duHmrGluoO4C6OsJZGRrkDIld43ZqUOTnlkDSmXmabAoBU0vqBf+6KgFSxQ9++uzZ8rZApM81TJ8xM5me0Z/UF7PuBmdVdkGEb5gYDeQmyZNW3SJLIP9Kj64lGyMpmxRN6sOfIbkoAhKOdnv2/PmB1kB88eLFo+olyyrps3rSINIAzLonnqlqK8R9w+L86vtrt5L2nhug3Vc3ULu/Liz8AOuXESlZZONH6kmr7gtLIA9lRNeRzVukAvj3BslLnJNKgfScO69K+/Lly0ZbQW7e8tNK+pwBjqaSIjDrXgJkW1ciAZvbQjQ+RDahpBBKd5ZZsqN758hmImk4KQHnpDd8UwSkCyJarx07d4+3BeKJmlMHyX4qaRxpBCmNFE4KENvHDpAutVERn1kCVBMfeRRgYvZnx62wZPdnZkw92VQA5GClQXYRBze2S+iJmpPVVoJLA9l9QKokjcWKTCT1R5rhLg70NuSsziT16diIKkuAjibrTpJNDkn/e17CahtAjlAWJAYkb29Sb1LE9Rs391kILk8mVkyuIpuZcLKUlEmKkra1WuSTNuesEPzwoEploSVAh9Oiz+BIyd9dOHhtx4OEpFpVg6gbNK3yXX1j48N6U5Dz5i/gc/FDrMY3sTLiSMEkXxGxzUEUAGnbxlPaksMlHUXWAlHS8URCPseSohZbCSLjSSU7ixLXdzhIWVKq4Y7t2a/2bN0qGeKly1fYsVmk6RgIDz4J0bonyUOcjeYqm/8hRoYbWkigV2NH9CHAS60EkUkkw47hSRs6FqT1LR5AVcsrueXlK1d5AO+RpmBrZZEiefByytPCanRGNLZY0uF52gNDYr9sCRB8MHY0SJu2OJWKS2WQV65e4y31DmkCImEi0hBfufRime0RIhpbKen0/Ny9OYNW2ghyYytABjNIaxNuKttAWk6HPLn0k0FevdZwFinPWFIuKZbUV16NVko6jbWSDoPO3pOf8K0jQWLSQ0S9bdpkYck+m7vfWpAiHfKgBsZiGSSt0FqcTeU8WETqAHE2CgcAVd3Gkm4MD3xXYeI6B4NMItvKbcUpQ9gP+KMWnSsW+TaYJtoo+avBWLoKoK0CCSDud+7eXWQGZAXqV3YoQjQCfixJ8+fzj9ta3JHhlUeJ8wJOY2ws6eRKpPS3oqTvHAESEz9ya0naXL5WH6pt3FqSOhTHkTcKEXc6k1POh4Q9YJu/03TT4a8PoGMFI4i2EqSbOZAYaBkpCyD92RkG6KCSbjI/H0HEISBnlOZPFdcEzI2GTO4KBZICGKyAKLTEmJOB2txf5MbgohBINCl4FTqmpJMB2W+HiRn1Q2l6lXyPmiEP6VVE2TfGoaMYrHyPdtAnyI0jEOn9RLWmNEhvBBE7SjpFQZaShtLK+1S+T12lRwxUvrZlVPp8jE1PikeO7C/nyEqBDCB1t7+kUx4kKUWclea0yZC5BIGpiJSNSD9QgFR0RQKkL6KxHSWdsiARHJNYewoGrzG1/bk4dTPSunL2EyDjcbb7MQ+lQfZmkKiN7SjpFAM5CWAyGcwyY84YsZ1lUcbRNNtQMAdtQWGvQ0DyVjzYAKQfQFodeAeC1C8vzymXIZqD+ZEh/2OyLSalS/3VbnJZ+VqDXGjMrTCFuK4s66vVZUNfqaDolcbjOcb899sLpEE+I20GifywXe2QR3KElu99PzqjGufhREqB1pjCnG3IL3fY1v733r2FMsiGhutn0LAoJWWIGbPxjKwgjUbF0m52mPhigrpdXOecEq9pR6MkHbu2LOtrcZ9y3d0ODTb15y9MePz48aF79+8fvXnr9sljx2u2I7KNxDuaMPGVECoRs7mC4eT7SIruFNfNHK15MKuM2evwNq+4qjxvGnd5CHwNNynawW4cOlUZdG8b55IIJHmkItwrZHH6QxB3OSL9kTtAGpIvZiQB3Z4SKBfXQtEE9sashWAW87Bt3sYZNR6zn4uzJwWDKUKXfaKCdqUoBpLxSjYe9nqGiwWRBGipuGZ3Qm76itYLbbJI/PEhUApfw73uOIy9xfse3M9F9BuFJHcYrseSouGkHtCVtkuGTTikI8XgZzhg9SeF4VqcvSWiaSvNHQ8JwkNjIfEHemCmNLD1RaEfLs18mlgNuN6PFALHo7CyU5W2g00gFAQF4ozvibH04muwDbWraSFAyt/AAMzewgGR8uCeWn77xzBxPxgzPRCDDMZ14bQ/3jqGKGoHf2Hjgx3kw5LbaJDYWb52t9FMgw4AuWNWukNeuOYqOsmQi2jgws4PA/DD/z0B2x0/veCs4naw0cgybezid7X9jV3rX2RSs0wfLkll4pBGcgifg+NYxe1kJ2ycTaRq66uG/wBOl0vjcw70xwAAAABJRU5ErkJggg=="
/***/ }),
/* 322 */
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFIAAAAoCAYAAABtla08AAAGMElEQVR4Ae2aA7D0yBaAc7oH12vbRmlLaxYWb23btm3btm2899a2bWuYtPZ01cmtU9lJrib315yqr9I3Oem/5/s7acwEnehEJzoxCcX2O+wEeIgRBDDaGjAZOgQ6ihRpLklHZDJIXK1WWymMIhGGkVBKCWMM+Iv/f/b5t7faYtM/sGgIS7j8RNLjceUVl41GvGN1BFiHy9sgtRWaYbhvuVQ6o1VOvV5/tLe3dyssKoZuh8xClkDEi2MMS6ZjR0cScxdK/+HgnJsmLccYOx0e/PUGUqfTJDEHkV5go9lcMQoj4R8RpSIRRUr4a9baTJFCCNfqESKJ7RYJibK0xoi05EhFRTxMi1Rit6xHAuLaKRLwEVi6q1x+EhlVpd3d3Wfh4VQkQhRhxthYLg7SRGqdLlIp7UVOHf+JhEhEMscUolVje3p63saeeOFoKsT7fjj++BNuw2I/0ouUENmGaQcQEilQvUU6xuWC0kqmVWCt8df6kG7WLoFA20VSCOyNh0RKPT+SyrTWtQsvuvTYCy84z3+oAdbgAiLGIvHjTz6bFuu/B3lKKfVkFKknwih6EnnipZdfXQZzepAupXSGSCfwUGZtkrx3t/0dSQGnnXbmdocdetArQoj+4VR23wMP3bj/vnv9Sv/rBmkish09ca655thHSrlWq4TFF1vkNDxsgjiUnPqZnHPABIq47jx7pPMcecShfz7x1DO7D6eit99576X1113nVd8rqLGAuDaNitJonTGIqHgQGQjDsJglMrUH5iDSEQbRa6y2yrNvv/PuWVmV/PTzLz8steTit1B9FtGJeZrJksmWdBzBMcami4xUkaY1A1Qe94WIaPGBApJhaERrLrXkElf8+NPPz6YMLs1DDjn0Wn9PnI/UiQadM4jNEkhzVsEGE8nIHESM1j5/KqRX+/IEiOQ/yifNBlEkpnb00cccesbpp13T3983H88/48xzrrvm6it/8U5JXgX5G6nSvSq1R5LATR7aYGkwMG1RSwkWABH+4jUb3vT/uJ1Z0xpjraTBRltrxUQhksIRmgTJyy69+Pv99tv3qYX6FxgU+fU33352xGEHf5wisU7nNWJpZRMkAjZ6aIN1mwV7h29Jo2wCHlveu/GV169z65E+T6koexCh6c+EEiky3lnxQKFjUeVyOeI5AOBzIiayRhJryd7YYnkIHgvB0qk9Tdql6N3XH4bRUIOIIIKJSiRb0hkSEpZKRd1CpEq8GxtIyCVmDSgFl94GacTgaJw1rUlYhYng0c4ewaUsmKRIJjpiqMSOCh9QeI+UYECmtQIsxEu6OorEcv6Rl0gu0woh8MhFkmSCTXVI4pC704WCFRJvSRNJSzrMMEZO2iKZTCHAZYnmvXCny7ed5vfZK3viHSBdIFCKEFj2+nt+73nw8m2uedcLJlktA++VNMEPaR45aYukcKnnCfY3/DFbZS8t7eHxNgsPM0N1hXhJJwwM1QbpoQFlog2R13a/zBxEYHAQEUYUM6qiVwEyBYoM6JFNF2kFLelI5KQf+fVI4dJFCguDS7oAyx2R6SFQJKRedSDj/cMg/RXQ6ZE05GSIDAaXdCi1I3L021SQWNJ1RLY5OiIdL4/yvuw8ADfWPFrSciaMyH8tEQPwf1uGG54g5+KlJGTmsrxsQdl5PKidnPFe2QS///7Hu+VS6WX/HYnf0sevGL7lXydwod2/9DykZq0s5yff0sgSWCigNOH7TPHL7ufj+/TH8P/+qYpL4HkBDiRYpEXeM8/89/9zzjn7EtY64dfd1nqccM7Bs8+9MKy8555/8TnKS+5MufH6EZVASkgPzf+mJXroet17JirU0ALST3nT0y5ONyLpeo1y64ih+vuQfsoTOeRFSJXa+SvyB90TUmdw49EjLaKpMQ0mzEeTzkWsd/oI6fzfiKM8gWg6X6OjpXstu5ZHnmIb0GFiu29MIUfUewkmVrEN3RqVQ/bY8FzNcquMBv/pCNUZ5pHHem01KdN/I/DG66/lLhKSvTO5M84kav5C5z2ZfyAivi9i9VGd45RH7UWJbjwGG/7NYsRECt7jiOToHedKAui8SW4CsxyRc54mKH/8f7ELhCCACyNcIl/wI+FaAJyc8yzRtinQPzWzuFZrFHq/AAAAAElFTkSuQmCC"
/***/ }),
/* 323 */
/***/ (function(module, exports) {
module.exports = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjkwIj48ZGVmcz48cGF0aCBkPSJNNjcgMTJMNi40NDggNzIuNTUyIDAgMzFWMThMMjYgMGw0MSAxMnptMyA3bDYgNDctMjkgMTgtMzUuNTAyLTYuNDk4TDcwIDE5eiIgaWQ9ImEiLz48L2RlZnM+PHJlY3Qgd2lkdGg9IjE1MCIgaGVpZ2h0PSI5MCIgcng9IjM1IiByeT0iNDUiIG9wYWNpdHk9Ii41Ii8+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNikiPjxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj48dXNlIHhsaW5rOmhyZWY9IiNhIi8+PC9tYXNrPjxwYXRoIGQ9Ik0zOS4yNDkgNTEuMzEyYy42OTcgMTAuMzcgMi43ODUgMTcuODk3IDUuMjUxIDE3Ljg5NyAzLjAzOCAwIDUuNS0xMS40MTcgNS41LTI1LjVzLTIuNDYyLTI1LjUtNS41LTI1LjVjLTIuNTEgMC00LjYyOCA3Ljc5Ny01LjI4NyAxOC40NTNBOC45ODkgOC45ODkgMCAwMTQzIDQ0YTguOTg4IDguOTg4IDAgMDEtMy43NTEgNy4zMTJ6TTIwLjk4NSAzMi4yMjRsMTUuNzQ2LTE2Ljg3N2E3LjM4NSA3LjM4NSAwIDAxMTAuMzc0LS40MkM1MS43MDIgMTkuMTE0IDU0IDI5LjIwOCA1NCA0NS4yMDhjMCAxNC41MjctMi4zNDMgMjMuODgtNy4wMyAyOC4wNThhNy4yOCA3LjI4IDAgMDEtMTAuMTY4LS40NjhMMjAuNDA1IDU1LjIyNEgxMmE1IDUgMCAwMS01LTV2LTEzYTUgNSAwIDAxNS01aDguOTg1eiIgZmlsbD0iI0ZGRiIgbWFzaz0idXJsKCNiKSIvPjwvZz48cGF0aCBkPSJNMTA2LjUgMTMuNUw0NC45OTggNzUuMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg=="
/***/ }),
/* 324 */
/***/ (function(module, exports) {
module.exports = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTciIGhlaWdodD0iOTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik03MCAxMkw5LjQ0OCA3Mi41NTIgMCA2MmwzLTQ0TDI5IDBsNDEgMTJ6bTggMmwxIDUyLTI5IDE4LTM1LjUwMi02LjQ5OEw3OCAxNHoiIGlkPSJhIi8+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAzKSI+PG1hc2sgaWQ9ImIiIGZpbGw9IiNmZmYiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48L21hc2s+PHBhdGggZD0iTTkuMTEgMjAuOTY4SDQ4LjFhNSA1IDAgMDE1IDVWNTguMThhNSA1IDAgMDEtNSA1SDkuMTFhNSA1IDAgMDEtNS01VjI1Ljk3YTUgNSAwIDAxNS01em00Ny4wOCAxMy4zOTRjMC0uMzQ1IDUuNDcyLTMuMTU5IDE2LjQxNS04LjQ0M2EzIDMgMCAwMTQuMzA0IDIuNzAydjI2LjgzNWEzIDMgMCAwMS00LjMwNSAyLjcwMWMtMTAuOTQyLTUuMjg2LTE2LjQxMy04LjEtMTYuNDEzLTguNDQ2VjM0LjM2MnoiIGZpbGw9IiNGRkYiIG1hc2s9InVybCgjYikiLz48L2c+PHBhdGggZD0iTTgxLjUgMTYuNUwxOS45OTggNzguMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg=="
/***/ }),
/* 325 */
/***/ (function(module, exports, __webpack_require__) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
var stylesInDom = {};
var memoize = function (fn) {
var memo;
return function () {
if (typeof memo === "undefined") memo = fn.apply(this, arguments);
return memo;
};
};
var isOldIE = memoize(function () {
// Test for IE <= 9 as proposed by Browserhacks
// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
// Tests for existence of standard globals is to allow style-loader
// to operate correctly into non-standard environments
// @see https://github.com/webpack-contrib/style-loader/issues/177
return window && document && document.all && !window.atob;
});
var getTarget = function (target, parent) {
if (parent){
return parent.querySelector(target);
}
return document.querySelector(target);
};
var getElement = (function (fn) {
var memo = {};
return function(target, parent) {
// If passing function in options, then use it for resolve "head" element.
// Useful for Shadow Root style i.e
// {
// insertInto: function () { return document.querySelector("#foo").shadowRoot }
// }
if (typeof target === 'function') {
return target();
}
if (typeof memo[target] === "undefined") {
var styleTarget = getTarget.call(this, target, parent);
// Special case to return head of iframe instead of iframe itself
if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
try {
// This will throw an exception if access to iframe is blocked
// due to cross-origin restrictions
styleTarget = styleTarget.contentDocument.head;
} catch(e) {
styleTarget = null;
}
}
memo[target] = styleTarget;
}
return memo[target]
};
})();
var singleton = null;
var singletonCounter = 0;
var stylesInsertedAtTop = [];
var fixUrls = __webpack_require__(326);
module.exports = function(list, options) {
if (typeof DEBUG !== "undefined" && DEBUG) {
if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
}
options = options || {};
options.attrs = typeof options.attrs === "object" ? options.attrs : {};
// Force single-tag solution on IE6-9, which has a hard limit on the # of ";
};
Theme.prototype.initTheme = function () {
var _this = this; // Only watch for reactivity on client side
if (typeof document === 'undefined') return; // If we get here somehow, ensure
// existing instance is removed
if (this.vueInstance) this.vueInstance.$destroy(); // Use Vue instance to track reactivity
// TODO: Update to use RFC if merged
// https://github.com/vuejs/rfcs/blob/advanced-reactivity-api/active-rfcs/0000-advanced-reactivity-api.md
this.vueInstance = new vue__WEBPACK_IMPORTED_MODULE_2___default.a({
data: {
themes: this.themes
},
watch: {
themes: {
immediate: true,
deep: true,
handler: function handler() {
return _this.applyTheme();
}
}
}
});
};
Object.defineProperty(Theme.prototype, "currentTheme", {
get: function get() {
var target = this.dark ? 'dark' : 'light';
return this.themes[target];
},
enumerable: true,
configurable: true
});
Object.defineProperty(Theme.prototype, "generatedStyles", {
get: function get() {
var theme = this.parsedTheme;
/* istanbul ignore next */
var options = this.options || {};
var css;
if (options.themeCache != null) {
css = options.themeCache.get(theme);
/* istanbul ignore if */
if (css != null) return css;
}
css = _utils__WEBPACK_IMPORTED_MODULE_1__["genStyles"](theme, options.customProperties);
if (options.minifyTheme != null) {
css = options.minifyTheme(css);
}
if (options.themeCache != null) {
options.themeCache.set(theme, css);
}
return css;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Theme.prototype, "parsedTheme", {
get: function get() {
/* istanbul ignore next */
var theme = this.currentTheme || {};
return _utils__WEBPACK_IMPORTED_MODULE_1__["parse"](theme);
},
enumerable: true,
configurable: true
});
Object.defineProperty(Theme.prototype, "isVueMeta23", {
// Is using v2.3 of vue-meta
// https://github.com/nuxt/vue-meta/releases/tag/v2.3.0
get: function get() {
return typeof this.vueMeta.addApp === 'function';
},
enumerable: true,
configurable: true
});
Theme.property = 'theme';
return Theme;
}(_service__WEBPACK_IMPORTED_MODULE_0__["Service"]);
/***/ }),
/***/ "./src/services/theme/utils.ts":
/*!*************************************!*\
!*** ./src/services/theme/utils.ts ***!
\*************************************/
/*! exports provided: parse, genStyles, genVariations, lighten, darken */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return parse; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "genStyles", function() { return genStyles; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "genVariations", function() { return genVariations; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "lighten", function() { return lighten; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "darken", function() { return darken; });
/* harmony import */ var _util_colorUtils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../util/colorUtils */ "./src/util/colorUtils.ts");
/* harmony import */ var _util_color_transformSRGB__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../util/color/transformSRGB */ "./src/util/color/transformSRGB.ts");
/* harmony import */ var _util_color_transformCIELAB__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../util/color/transformCIELAB */ "./src/util/color/transformCIELAB.ts");
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 __rest = undefined && undefined.__rest || function (s, e) {
var t = {};
for (var p in s) {
if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
}
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
var __read = undefined && undefined.__read || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o),
r,
ar = [],
e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) {
ar.push(r.value);
}
} catch (error) {
e = {
error: error
};
} finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
} finally {
if (e) throw e.error;
}
}
return ar;
};
function parse(theme, isItem) {
if (isItem === void 0) {
isItem = false;
}
var anchor = theme.anchor,
variant = __rest(theme, ["anchor"]);
var colors = Object.keys(variant);
var parsedTheme = {};
for (var i = 0; i < colors.length; ++i) {
var name = colors[i];
var value = theme[name];
if (value == null) continue;
if (isItem) {
/* istanbul ignore else */
if (name === 'base' || name.startsWith('lighten') || name.startsWith('darken')) {
parsedTheme[name] = Object(_util_colorUtils__WEBPACK_IMPORTED_MODULE_0__["colorToHex"])(value);
}
} else if (_typeof(value) === 'object') {
parsedTheme[name] = parse(value, true);
} else {
parsedTheme[name] = genVariations(name, Object(_util_colorUtils__WEBPACK_IMPORTED_MODULE_0__["colorToInt"])(value));
}
}
if (!isItem) {
parsedTheme.anchor = anchor || parsedTheme.base || parsedTheme.primary.base;
}
return parsedTheme;
}
/**
* Generate the CSS for a base color (.primary)
*/
var genBaseColor = function genBaseColor(name, value) {
return "\n.v-application ." + name + " {\n background-color: " + value + " !important;\n border-color: " + value + " !important;\n}\n.v-application ." + name + "--text {\n color: " + value + " !important;\n caret-color: " + value + " !important;\n}";
};
/**
* Generate the CSS for a variant color (.primary.darken-2)
*/
var genVariantColor = function genVariantColor(name, variant, value) {
var _a = __read(variant.split(/(\d)/, 2), 2),
type = _a[0],
n = _a[1];
return "\n.v-application ." + name + "." + type + "-" + n + " {\n background-color: " + value + " !important;\n border-color: " + value + " !important;\n}\n.v-application ." + name + "--text.text--" + type + "-" + n + " {\n color: " + value + " !important;\n caret-color: " + value + " !important;\n}";
};
var genColorVariableName = function genColorVariableName(name, variant) {
if (variant === void 0) {
variant = 'base';
}
return "--v-" + name + "-" + variant;
};
var genColorVariable = function genColorVariable(name, variant) {
if (variant === void 0) {
variant = 'base';
}
return "var(" + genColorVariableName(name, variant) + ")";
};
function genStyles(theme, cssVar) {
if (cssVar === void 0) {
cssVar = false;
}
var anchor = theme.anchor,
variant = __rest(theme, ["anchor"]);
var colors = Object.keys(variant);
if (!colors.length) return '';
var variablesCss = '';
var css = '';
var aColor = cssVar ? genColorVariable('anchor') : anchor;
css += ".v-application a { color: " + aColor + "; }";
cssVar && (variablesCss += " " + genColorVariableName('anchor') + ": " + anchor + ";\n");
for (var i = 0; i < colors.length; ++i) {
var name = colors[i];
var value = theme[name];
css += genBaseColor(name, cssVar ? genColorVariable(name) : value.base);
cssVar && (variablesCss += " " + genColorVariableName(name) + ": " + value.base + ";\n");
var variants = Object.keys(value);
for (var i_1 = 0; i_1 < variants.length; ++i_1) {
var variant_1 = variants[i_1];
var variantValue = value[variant_1];
if (variant_1 === 'base') continue;
css += genVariantColor(name, variant_1, cssVar ? genColorVariable(name, variant_1) : variantValue);
cssVar && (variablesCss += " " + genColorVariableName(name, variant_1) + ": " + variantValue + ";\n");
}
}
if (cssVar) {
variablesCss = ":root {\n" + variablesCss + "}\n\n";
}
return variablesCss + css;
}
function genVariations(name, value) {
var values = {
base: Object(_util_colorUtils__WEBPACK_IMPORTED_MODULE_0__["intToHex"])(value)
};
for (var i = 5; i > 0; --i) {
values["lighten" + i] = Object(_util_colorUtils__WEBPACK_IMPORTED_MODULE_0__["intToHex"])(lighten(value, i));
}
for (var i = 1; i <= 4; ++i) {
values["darken" + i] = Object(_util_colorUtils__WEBPACK_IMPORTED_MODULE_0__["intToHex"])(darken(value, i));
}
return values;
}
function lighten(value, amount) {
var lab = _util_color_transformCIELAB__WEBPACK_IMPORTED_MODULE_2__["fromXYZ"](_util_color_transformSRGB__WEBPACK_IMPORTED_MODULE_1__["toXYZ"](value));
lab[0] = lab[0] + amount * 10;
return _util_color_transformSRGB__WEBPACK_IMPORTED_MODULE_1__["fromXYZ"](_util_color_transformCIELAB__WEBPACK_IMPORTED_MODULE_2__["toXYZ"](lab));
}
function darken(value, amount) {
var lab = _util_color_transformCIELAB__WEBPACK_IMPORTED_MODULE_2__["fromXYZ"](_util_color_transformSRGB__WEBPACK_IMPORTED_MODULE_1__["toXYZ"](value));
lab[0] = lab[0] - amount * 10;
return _util_color_transformSRGB__WEBPACK_IMPORTED_MODULE_1__["fromXYZ"](_util_color_transformCIELAB__WEBPACK_IMPORTED_MODULE_2__["toXYZ"](lab));
}
/***/ }),
/***/ "./src/styles/components/_selection-controls.sass":
/*!********************************************************!*\
!*** ./src/styles/components/_selection-controls.sass ***!
\********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "./src/styles/main.sass":
/*!******************************!*\
!*** ./src/styles/main.sass ***!
\******************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
// extracted by mini-css-extract-plugin
/***/ }),
/***/ "./src/util/color/transformCIELAB.ts":
/*!*******************************************!*\
!*** ./src/util/color/transformCIELAB.ts ***!
\*******************************************/
/*! exports provided: fromXYZ, toXYZ */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromXYZ", function() { return fromXYZ; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toXYZ", function() { return toXYZ; });
var delta = 0.20689655172413793; // 6÷29
var cielabForwardTransform = function cielabForwardTransform(t) {
return t > Math.pow(delta, 3) ? Math.cbrt(t) : t / (3 * Math.pow(delta, 2)) + 4 / 29;
};
var cielabReverseTransform = function cielabReverseTransform(t) {
return t > delta ? Math.pow(t, 3) : 3 * Math.pow(delta, 2) * (t - 4 / 29);
};
function fromXYZ(xyz) {
var transform = cielabForwardTransform;
var transformedY = transform(xyz[1]);
return [116 * transformedY - 16, 500 * (transform(xyz[0] / 0.95047) - transformedY), 200 * (transformedY - transform(xyz[2] / 1.08883))];
}
function toXYZ(lab) {
var transform = cielabReverseTransform;
var Ln = (lab[0] + 16) / 116;
return [transform(Ln + lab[1] / 500) * 0.95047, transform(Ln), transform(Ln - lab[2] / 200) * 1.08883];
}
/***/ }),
/***/ "./src/util/color/transformSRGB.ts":
/*!*****************************************!*\
!*** ./src/util/color/transformSRGB.ts ***!
\*****************************************/
/*! exports provided: fromXYZ, toXYZ */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fromXYZ", function() { return fromXYZ; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toXYZ", function() { return toXYZ; });
/* harmony import */ var _util_helpers__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../util/helpers */ "./src/util/helpers.ts");
// For converting XYZ to sRGB
var srgbForwardMatrix = [[3.2406, -1.5372, -0.4986], [-0.9689, 1.8758, 0.0415], [0.0557, -0.2040, 1.0570]]; // Forward gamma adjust
var srgbForwardTransform = function srgbForwardTransform(C) {
return C <= 0.0031308 ? C * 12.92 : 1.055 * Math.pow(C, 1 / 2.4) - 0.055;
}; // For converting sRGB to XYZ
var srgbReverseMatrix = [[0.4124, 0.3576, 0.1805], [0.2126, 0.7152, 0.0722], [0.0193, 0.1192, 0.9505]]; // Reverse gamma adjust
var srgbReverseTransform = function srgbReverseTransform(C) {
return C <= 0.04045 ? C / 12.92 : Math.pow((C + 0.055) / 1.055, 2.4);
};
function fromXYZ(xyz) {
var rgb = Array(3);
var transform = srgbForwardTransform;
var matrix = srgbForwardMatrix; // Matrix transform, then gamma adjustment
for (var i = 0; i < 3; ++i) {
rgb[i] = Math.round(Object(_util_helpers__WEBPACK_IMPORTED_MODULE_0__["clamp"])(transform(matrix[i][0] * xyz[0] + matrix[i][1] * xyz[1] + matrix[i][2] * xyz[2])) * 255);
} // Rescale back to [0, 255]
return (rgb[0] << 16) + (rgb[1] << 8) + (rgb[2] << 0);
}
function toXYZ(rgb) {
var xyz = [0, 0, 0];
var transform = srgbReverseTransform;
var matrix = srgbReverseMatrix; // Rescale from [0, 255] to [0, 1] then adjust sRGB gamma to linear RGB
var r = transform((rgb >> 16 & 0xff) / 255);
var g = transform((rgb >> 8 & 0xff) / 255);
var b = transform((rgb >> 0 & 0xff) / 255); // Matrix color space transform
for (var i = 0; i < 3; ++i) {
xyz[i] = matrix[i][0] * r + matrix[i][1] * g + matrix[i][2] * b;
}
return xyz;
}
/***/ }),
/***/ "./src/util/colorUtils.ts":
/*!********************************!*\
!*** ./src/util/colorUtils.ts ***!
\********************************/
/*! exports provided: colorToInt, intToHex, colorToHex, HSVAtoRGBA, RGBAtoHSVA, HSVAtoHSLA, HSLAtoHSVA, RGBAtoCSS, RGBtoCSS, RGBAtoHex, HexToRGBA, HexToHSVA, HSVAtoHex, parseHex, RGBtoInt, contrastRatio */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "colorToInt", function() { return colorToInt; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "intToHex", function() { return intToHex; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "colorToHex", function() { return colorToHex; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HSVAtoRGBA", function() { return HSVAtoRGBA; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RGBAtoHSVA", function() { return RGBAtoHSVA; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HSVAtoHSLA", function() { return HSVAtoHSLA; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HSLAtoHSVA", function() { return HSLAtoHSVA; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RGBAtoCSS", function() { return RGBAtoCSS; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RGBtoCSS", function() { return RGBtoCSS; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RGBAtoHex", function() { return RGBAtoHex; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HexToRGBA", function() { return HexToRGBA; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HexToHSVA", function() { return HexToHSVA; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HSVAtoHex", function() { return HSVAtoHex; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parseHex", function() { return parseHex; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RGBtoInt", function() { return RGBtoInt; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "contrastRatio", function() { return contrastRatio; });
/* harmony import */ var _console__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./console */ "./src/util/console.ts");
/* harmony import */ var _helpers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./helpers */ "./src/util/helpers.ts");
/* harmony import */ var _color_transformSRGB__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./color/transformSRGB */ "./src/util/color/transformSRGB.ts");
var __assign = undefined && undefined.__assign || function () {
__assign = Object.assign || function (t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) {
if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = undefined && undefined.__read || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o),
r,
ar = [],
e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) {
ar.push(r.value);
}
} catch (error) {
e = {
error: error
};
} finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
} finally {
if (e) throw e.error;
}
}
return ar;
};
function colorToInt(color) {
var rgb;
if (typeof color === 'number') {
rgb = color;
} else if (typeof color === 'string') {
var c = color[0] === '#' ? color.substring(1) : color;
if (c.length === 3) {
c = c.split('').map(function (char) {
return char + char;
}).join('');
}
if (c.length !== 6) {
Object(_console__WEBPACK_IMPORTED_MODULE_0__["consoleWarn"])("'" + color + "' is not a valid rgb color");
}
rgb = parseInt(c, 16);
} else {
throw new TypeError("Colors can only be numbers or strings, recieved " + (color == null ? color : color.constructor.name) + " instead");
}
if (rgb < 0) {
Object(_console__WEBPACK_IMPORTED_MODULE_0__["consoleWarn"])("Colors cannot be negative: '" + color + "'");
rgb = 0;
} else if (rgb > 0xffffff || isNaN(rgb)) {
Object(_console__WEBPACK_IMPORTED_MODULE_0__["consoleWarn"])("'" + color + "' is not a valid rgb color");
rgb = 0xffffff;
}
return rgb;
}
function intToHex(color) {
var hexColor = color.toString(16);
if (hexColor.length < 6) hexColor = '0'.repeat(6 - hexColor.length) + hexColor;
return '#' + hexColor;
}
function colorToHex(color) {
return intToHex(colorToInt(color));
}
/**
* Converts HSVA to RGBA. Based on formula from https://en.wikipedia.org/wiki/HSL_and_HSV
*
* @param color HSVA color as an array [0-360, 0-1, 0-1, 0-1]
*/
function HSVAtoRGBA(hsva) {
var h = hsva.h,
s = hsva.s,
v = hsva.v,
a = hsva.a;
var f = function f(n) {
var k = (n + h / 60) % 6;
return v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
};
var rgb = [f(5), f(3), f(1)].map(function (v) {
return Math.round(v * 255);
});
return {
r: rgb[0],
g: rgb[1],
b: rgb[2],
a: a
};
}
/**
* Converts RGBA to HSVA. Based on formula from https://en.wikipedia.org/wiki/HSL_and_HSV
*
* @param color RGBA color as an array [0-255, 0-255, 0-255, 0-1]
*/
function RGBAtoHSVA(rgba) {
if (!rgba) return {
h: 0,
s: 1,
v: 1,
a: 1
};
var r = rgba.r / 255;
var g = rgba.g / 255;
var b = rgba.b / 255;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h = 0;
if (max !== min) {
if (max === r) {
h = 60 * (0 + (g - b) / (max - min));
} else if (max === g) {
h = 60 * (2 + (b - r) / (max - min));
} else if (max === b) {
h = 60 * (4 + (r - g) / (max - min));
}
}
if (h < 0) h = h + 360;
var s = max === 0 ? 0 : (max - min) / max;
var hsv = [h, s, max];
return {
h: hsv[0],
s: hsv[1],
v: hsv[2],
a: rgba.a
};
}
function HSVAtoHSLA(hsva) {
var h = hsva.h,
s = hsva.s,
v = hsva.v,
a = hsva.a;
var l = v - v * s / 2;
var sprime = l === 1 || l === 0 ? 0 : (v - l) / Math.min(l, 1 - l);
return {
h: h,
s: sprime,
l: l,
a: a
};
}
function HSLAtoHSVA(hsl) {
var h = hsl.h,
s = hsl.s,
l = hsl.l,
a = hsl.a;
var v = l + s * Math.min(l, 1 - l);
var sprime = v === 0 ? 0 : 2 - 2 * l / v;
return {
h: h,
s: sprime,
v: v,
a: a
};
}
function RGBAtoCSS(rgba) {
return "rgba(" + rgba.r + ", " + rgba.g + ", " + rgba.b + ", " + rgba.a + ")";
}
function RGBtoCSS(rgba) {
return RGBAtoCSS(__assign({}, rgba, {
a: 1
}));
}
function RGBAtoHex(rgba) {
var toHex = function toHex(v) {
var h = Math.round(v).toString(16);
return ('00'.substr(0, 2 - h.length) + h).toUpperCase();
};
return "#" + [toHex(rgba.r), toHex(rgba.g), toHex(rgba.b), toHex(Math.round(rgba.a * 255))].join('');
}
function HexToRGBA(hex) {
var rgba = Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["chunk"])(hex.slice(1), 2).map(function (c) {
return parseInt(c, 16);
});
return {
r: rgba[0],
g: rgba[1],
b: rgba[2],
a: Math.round(rgba[3] / 255 * 100) / 100
};
}
function HexToHSVA(hex) {
var rgb = HexToRGBA(hex);
return RGBAtoHSVA(rgb);
}
function HSVAtoHex(hsva) {
return RGBAtoHex(HSVAtoRGBA(hsva));
}
function parseHex(hex) {
if (hex.startsWith('#')) {
hex = hex.slice(1);
}
hex = hex.replace(/([^0-9a-f])/gi, 'F');
if (hex.length === 3) {
hex = hex.split('').map(function (x) {
return x + x;
}).join('');
}
if (hex.length === 6) {
hex = Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["padEnd"])(hex, 8, 'F');
} else {
hex = Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["padEnd"])(Object(_helpers__WEBPACK_IMPORTED_MODULE_1__["padEnd"])(hex, 6), 8, 'F');
}
return ("#" + hex).toUpperCase().substr(0, 9);
}
function RGBtoInt(rgba) {
return (rgba.r << 16) + (rgba.g << 8) + rgba.b;
}
/**
* Returns the contrast ratio (1-21) between two colors.
*
* @param c1 First color
* @param c2 Second color
*/
function contrastRatio(c1, c2) {
var _a = __read(Object(_color_transformSRGB__WEBPACK_IMPORTED_MODULE_2__["toXYZ"])(RGBtoInt(c1)), 2),
y1 = _a[1];
var _b = __read(Object(_color_transformSRGB__WEBPACK_IMPORTED_MODULE_2__["toXYZ"])(RGBtoInt(c2)), 2),
y2 = _b[1];
return (Math.max(y1, y2) + 0.05) / (Math.min(y1, y2) + 0.05);
}
/***/ }),
/***/ "./src/util/colors.ts":
/*!****************************!*\
!*** ./src/util/colors.ts ***!
\****************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var red = Object.freeze({
base: '#f44336',
lighten5: '#ffebee',
lighten4: '#ffcdd2',
lighten3: '#ef9a9a',
lighten2: '#e57373',
lighten1: '#ef5350',
darken1: '#e53935',
darken2: '#d32f2f',
darken3: '#c62828',
darken4: '#b71c1c',
accent1: '#ff8a80',
accent2: '#ff5252',
accent3: '#ff1744',
accent4: '#d50000'
});
var pink = Object.freeze({
base: '#e91e63',
lighten5: '#fce4ec',
lighten4: '#f8bbd0',
lighten3: '#f48fb1',
lighten2: '#f06292',
lighten1: '#ec407a',
darken1: '#d81b60',
darken2: '#c2185b',
darken3: '#ad1457',
darken4: '#880e4f',
accent1: '#ff80ab',
accent2: '#ff4081',
accent3: '#f50057',
accent4: '#c51162'
});
var purple = Object.freeze({
base: '#9c27b0',
lighten5: '#f3e5f5',
lighten4: '#e1bee7',
lighten3: '#ce93d8',
lighten2: '#ba68c8',
lighten1: '#ab47bc',
darken1: '#8e24aa',
darken2: '#7b1fa2',
darken3: '#6a1b9a',
darken4: '#4a148c',
accent1: '#ea80fc',
accent2: '#e040fb',
accent3: '#d500f9',
accent4: '#aa00ff'
});
var deepPurple = Object.freeze({
base: '#673ab7',
lighten5: '#ede7f6',
lighten4: '#d1c4e9',
lighten3: '#b39ddb',
lighten2: '#9575cd',
lighten1: '#7e57c2',
darken1: '#5e35b1',
darken2: '#512da8',
darken3: '#4527a0',
darken4: '#311b92',
accent1: '#b388ff',
accent2: '#7c4dff',
accent3: '#651fff',
accent4: '#6200ea'
});
var indigo = Object.freeze({
base: '#3f51b5',
lighten5: '#e8eaf6',
lighten4: '#c5cae9',
lighten3: '#9fa8da',
lighten2: '#7986cb',
lighten1: '#5c6bc0',
darken1: '#3949ab',
darken2: '#303f9f',
darken3: '#283593',
darken4: '#1a237e',
accent1: '#8c9eff',
accent2: '#536dfe',
accent3: '#3d5afe',
accent4: '#304ffe'
});
var blue = Object.freeze({
base: '#2196f3',
lighten5: '#e3f2fd',
lighten4: '#bbdefb',
lighten3: '#90caf9',
lighten2: '#64b5f6',
lighten1: '#42a5f5',
darken1: '#1e88e5',
darken2: '#1976d2',
darken3: '#1565c0',
darken4: '#0d47a1',
accent1: '#82b1ff',
accent2: '#448aff',
accent3: '#2979ff',
accent4: '#2962ff'
});
var lightBlue = Object.freeze({
base: '#03a9f4',
lighten5: '#e1f5fe',
lighten4: '#b3e5fc',
lighten3: '#81d4fa',
lighten2: '#4fc3f7',
lighten1: '#29b6f6',
darken1: '#039be5',
darken2: '#0288d1',
darken3: '#0277bd',
darken4: '#01579b',
accent1: '#80d8ff',
accent2: '#40c4ff',
accent3: '#00b0ff',
accent4: '#0091ea'
});
var cyan = Object.freeze({
base: '#00bcd4',
lighten5: '#e0f7fa',
lighten4: '#b2ebf2',
lighten3: '#80deea',
lighten2: '#4dd0e1',
lighten1: '#26c6da',
darken1: '#00acc1',
darken2: '#0097a7',
darken3: '#00838f',
darken4: '#006064',
accent1: '#84ffff',
accent2: '#18ffff',
accent3: '#00e5ff',
accent4: '#00b8d4'
});
var teal = Object.freeze({
base: '#009688',
lighten5: '#e0f2f1',
lighten4: '#b2dfdb',
lighten3: '#80cbc4',
lighten2: '#4db6ac',
lighten1: '#26a69a',
darken1: '#00897b',
darken2: '#00796b',
darken3: '#00695c',
darken4: '#004d40',
accent1: '#a7ffeb',
accent2: '#64ffda',
accent3: '#1de9b6',
accent4: '#00bfa5'
});
var green = Object.freeze({
base: '#4caf50',
lighten5: '#e8f5e9',
lighten4: '#c8e6c9',
lighten3: '#a5d6a7',
lighten2: '#81c784',
lighten1: '#66bb6a',
darken1: '#43a047',
darken2: '#388e3c',
darken3: '#2e7d32',
darken4: '#1b5e20',
accent1: '#b9f6ca',
accent2: '#69f0ae',
accent3: '#00e676',
accent4: '#00c853'
});
var lightGreen = Object.freeze({
base: '#8bc34a',
lighten5: '#f1f8e9',
lighten4: '#dcedc8',
lighten3: '#c5e1a5',
lighten2: '#aed581',
lighten1: '#9ccc65',
darken1: '#7cb342',
darken2: '#689f38',
darken3: '#558b2f',
darken4: '#33691e',
accent1: '#ccff90',
accent2: '#b2ff59',
accent3: '#76ff03',
accent4: '#64dd17'
});
var lime = Object.freeze({
base: '#cddc39',
lighten5: '#f9fbe7',
lighten4: '#f0f4c3',
lighten3: '#e6ee9c',
lighten2: '#dce775',
lighten1: '#d4e157',
darken1: '#c0ca33',
darken2: '#afb42b',
darken3: '#9e9d24',
darken4: '#827717',
accent1: '#f4ff81',
accent2: '#eeff41',
accent3: '#c6ff00',
accent4: '#aeea00'
});
var yellow = Object.freeze({
base: '#ffeb3b',
lighten5: '#fffde7',
lighten4: '#fff9c4',
lighten3: '#fff59d',
lighten2: '#fff176',
lighten1: '#ffee58',
darken1: '#fdd835',
darken2: '#fbc02d',
darken3: '#f9a825',
darken4: '#f57f17',
accent1: '#ffff8d',
accent2: '#ffff00',
accent3: '#ffea00',
accent4: '#ffd600'
});
var amber = Object.freeze({
base: '#ffc107',
lighten5: '#fff8e1',
lighten4: '#ffecb3',
lighten3: '#ffe082',
lighten2: '#ffd54f',
lighten1: '#ffca28',
darken1: '#ffb300',
darken2: '#ffa000',
darken3: '#ff8f00',
darken4: '#ff6f00',
accent1: '#ffe57f',
accent2: '#ffd740',
accent3: '#ffc400',
accent4: '#ffab00'
});
var orange = Object.freeze({
base: '#ff9800',
lighten5: '#fff3e0',
lighten4: '#ffe0b2',
lighten3: '#ffcc80',
lighten2: '#ffb74d',
lighten1: '#ffa726',
darken1: '#fb8c00',
darken2: '#f57c00',
darken3: '#ef6c00',
darken4: '#e65100',
accent1: '#ffd180',
accent2: '#ffab40',
accent3: '#ff9100',
accent4: '#ff6d00'
});
var deepOrange = Object.freeze({
base: '#ff5722',
lighten5: '#fbe9e7',
lighten4: '#ffccbc',
lighten3: '#ffab91',
lighten2: '#ff8a65',
lighten1: '#ff7043',
darken1: '#f4511e',
darken2: '#e64a19',
darken3: '#d84315',
darken4: '#bf360c',
accent1: '#ff9e80',
accent2: '#ff6e40',
accent3: '#ff3d00',
accent4: '#dd2c00'
});
var brown = Object.freeze({
base: '#795548',
lighten5: '#efebe9',
lighten4: '#d7ccc8',
lighten3: '#bcaaa4',
lighten2: '#a1887f',
lighten1: '#8d6e63',
darken1: '#6d4c41',
darken2: '#5d4037',
darken3: '#4e342e',
darken4: '#3e2723'
});
var blueGrey = Object.freeze({
base: '#607d8b',
lighten5: '#eceff1',
lighten4: '#cfd8dc',
lighten3: '#b0bec5',
lighten2: '#90a4ae',
lighten1: '#78909c',
darken1: '#546e7a',
darken2: '#455a64',
darken3: '#37474f',
darken4: '#263238'
});
var grey = Object.freeze({
base: '#9e9e9e',
lighten5: '#fafafa',
lighten4: '#f5f5f5',
lighten3: '#eeeeee',
lighten2: '#e0e0e0',
lighten1: '#bdbdbd',
darken1: '#757575',
darken2: '#616161',
darken3: '#424242',
darken4: '#212121'
});
var shades = Object.freeze({
black: '#000000',
white: '#ffffff',
transparent: 'transparent'
});
/* harmony default export */ __webpack_exports__["default"] = (Object.freeze({
red: red,
pink: pink,
purple: purple,
deepPurple: deepPurple,
indigo: indigo,
blue: blue,
lightBlue: lightBlue,
cyan: cyan,
teal: teal,
green: green,
lightGreen: lightGreen,
lime: lime,
yellow: yellow,
amber: amber,
orange: orange,
deepOrange: deepOrange,
brown: brown,
blueGrey: blueGrey,
grey: grey,
shades: shades
}));
/***/ }),
/***/ "./src/util/console.ts":
/*!*****************************!*\
!*** ./src/util/console.ts ***!
\*****************************/
/*! exports provided: consoleInfo, consoleWarn, consoleError, deprecate, breaking, removed */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "consoleInfo", function() { return consoleInfo; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "consoleWarn", function() { return consoleWarn; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "consoleError", function() { return consoleError; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "deprecate", function() { return deprecate; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "breaking", function() { return breaking; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removed", function() { return removed; });
function createMessage(message, vm, parent) {
if (parent) {
vm = {
_isVue: true,
$parent: parent,
$options: vm
};
}
if (vm) {
// Only show each message once per instance
vm.$_alreadyWarned = vm.$_alreadyWarned || [];
if (vm.$_alreadyWarned.includes(message)) return;
vm.$_alreadyWarned.push(message);
}
return "[Vuetify] " + message + (vm ? generateComponentTrace(vm) : '');
}
function consoleInfo(message, vm, parent) {
var newMessage = createMessage(message, vm, parent);
newMessage != null && console.info(newMessage);
}
function consoleWarn(message, vm, parent) {
var newMessage = createMessage(message, vm, parent);
newMessage != null && console.warn(newMessage);
}
function consoleError(message, vm, parent) {
var newMessage = createMessage(message, vm, parent);
newMessage != null && console.error(newMessage);
}
function deprecate(original, replacement, vm, parent) {
consoleWarn("[UPGRADE] '" + original + "' is deprecated, use '" + replacement + "' instead.", vm, parent);
}
function breaking(original, replacement, vm, parent) {
consoleError("[BREAKING] '" + original + "' has been removed, use '" + replacement + "' instead. For more information, see the upgrade guide https://github.com/vuetifyjs/vuetify/releases/tag/v2.0.0#user-content-upgrade-guide", vm, parent);
}
function removed(original, vm, parent) {
consoleWarn("[REMOVED] '" + original + "' has been removed. You can safely omit it.", vm, parent);
}
/**
* Shamelessly stolen from vuejs/vue/blob/dev/src/core/util/debug.js
*/
var classifyRE = /(?:^|[-_])(\w)/g;
var classify = function classify(str) {
return str.replace(classifyRE, function (c) {
return c.toUpperCase();
}).replace(/[-_]/g, '');
};
function formatComponentName(vm, includeFile) {
if (vm.$root === vm) {
return '