If you pass in multiple event names and a handler method, the handler is\n * registered for each of those events:
\n *\n * \n * The method also supports an alternate syntax, in which the first parameter is an object\n * that is a hash map of event names and handler functions and the second parameter (optional)\n * is the context for this in each handler:\n *
\n * \n * If you do not add a handler for an event, the event is ignored locally.\n *
\n *\n * @param {String} type The string identifying the type of event. You can specify multiple event\n * names in this string, separating them with a space. The event handler will process each of\n * the events.\n * @param {Function} handler The handler function to process the event. This function takes\n * the event object as a parameter.\n * @param {Object} context (Optional) Defines the value of If you pass in one event name and a handler method, the handler is removed for that\n * event:
\n *\n * If you pass in multiple event names and a handler method, the handler is removed for\n * those events:
\n *\n * \n * The method also supports an alternate syntax, in which the first parameter is an object that\n * is a hash map of event names and handler functions and the second parameter (optional) is\n * the context for this in each handler:\n *
\n * \n * The following code adds a one-time event handler for one event:\n *
\n *\n * If you pass in multiple event names and a handler method, the handler is registered\n * for each of those events:
\n *\n * \n * The method also supports an alternate syntax, in which the first parameter is an object that\n * is a hash map of event names and handler functions and the second parameter (optional) is the\n * context for this in each handler:\n *
\n * \n * This method registers a method as an event listener for a specific event.\n *
\n * If a handler is not registered for an event, the event is ignored locally. If the\n * event listener function does not exist, the event is ignored locally.\n *
\n * \n * The Error class also defines the Error thrown when the promise returned by one of\n * the following methods is rejected:\n *\n *
\n * While you are connected to the session, the Session object dispatches a\n * connectionDestroyed
event when another client disconnects from the Session.\n * (When you disconnect, the Session object also dispatches a sessionDisconnected
\n * event, defined by the {@link SessionDisconnectEvent} class.)\n *\n *
The following code keeps a running total of the number of connections to a session\n * by monitoring the connections
property of the sessionConnect
,\n * connectionCreated
and connectionDestroyed
events:
\n *\n * This example assumes that there is an input text field in the HTML DOM\n * with the id
set to \"connectionCountField\"
:
\n *\n * Depending on the context, this description may allow the developer to refine\n * the course of action they take in response to an event.
\n *\n * The following code connects to a session and sets up an event listener for when\n * a stream published by another client is created:
\n *\n * The following code connects to a session and sets up an event listener for when\n * other clients' streams end:
\n *\n * The following code publishes a stream and adds an event listener for when the streaming\n * starts
\n *\n * The following code publishes a stream, and leaves the Publisher in the HTML DOM\n * when the streaming stops:
\n *\n * Depending on the context, this description may allow the developer to refine\n * the course of action they take in response to an event.
\n *\n * \n * The following code initializes a session and sets up an event listener for when a session is\n * disconnected.\n *
\n * \n *\n * @class SessionDisconnectEvent\n * @augments Event\n */\n\n\n Events.SessionDisconnectEvent = function SessionDisconnectEvent(type, reason, cancelable) {\n return new Event(type, cancelable, {\n reason\n });\n };\n /**\n * Prevents the default behavior associated with the event from taking place.\n *\n * \n * For the sessionDisconnectEvent
, the default behavior is that all\n * Subscriber objects are unsubscribed and removed from the HTML DOM. Each\n * Subscriber object dispatches a destroyed
event when the element\n * is removed from the HTML DOM. If you call the preventDefault()
\n * method in the event listener for the sessionDisconnect
event,\n * the default behavior is prevented, and you can, optionally, clean up\n * Subscriber objects using your own code).\n *
\n * \n * To see whether an event has a default behavior, check the\n * cancelable
property of the event object.\n *
\n * \n * Call the preventDefault()
method in the event listener function\n * for the event.\n *
\n *\n * @method #preventDefault\n * @memberof SessionDisconnectEvent\n */\n\n /**\n * The Session object dispatches a streamPropertyChanged
event in the\n * following circumstances:\n *\n * \n * - A stream has started or stopped publishing audio or video (see\n * Publisher.publishAudio() and\n * Publisher.publishVideo()).\n * This change results from a call to the
publishAudio()
or\n * publishVideo()
methods of the Publish object. Note that a\n * subscriber's video can be disabled or enabled for reasons other than the\n * publisher disabling or enabling it. A Subscriber object dispatches\n * videoDisabled
and videoEnabled
events in all\n * conditions that cause the subscriber's stream to be disabled or enabled.\n * \n * - The
videoDimensions
property of the Stream object has\n * changed (see Stream.videoDimensions).\n * \n * - The
videoType
property of the Stream object has changed.\n * This can happen in a stream published by a mobile device. (See\n * Stream.videoType.)\n * \n *
\n *\n * @class StreamPropertyChangedEvent\n * @property {String} changedProperty The property of the stream that changed. This value\n * is either \"hasAudio\"
, \"hasVideo\"
, or \"videoDimensions\"
.\n * @property {Object} newValue The new value of the property (after the change).\n * @property {Object} oldValue The old value of the property (before the change).\n * @property {Stream} stream The Stream object for which a property has changed.\n *\n * @see Publisher.publishAudio()\n * @see Publisher.publishVideo()\n * @see Stream.videoDimensions\n * @augments Event\n */\n\n\n Events.StreamPropertyChangedEvent = function StreamPropertyChangedEvent(type, stream, changedProperty, oldValue, newValue) {\n return new Event(type, false, {\n stream,\n changedProperty,\n oldValue,\n newValue\n });\n };\n /**\n * Dispatched when the video dimensions of the video change for a screen-sharing\n * video stream (when the user resizes the window being captured).\n *\n * @class VideoDimensionsChangedEvent\n * @property {Object} newValue The new video dimensions (after the change). This object has two\n * properties: height
(the height, in pixels) and width
(the width,\n * in pixels).\n * @property {Object} oldValue The old video dimensions (before the change). This object has two\n * properties: height
(the old height, in pixels) and width
(the old\n * width, in pixels).\n *\n * @see Publisher videoDimensionsChanged\n * event\n * @see Subscriber videoDimensionsChanged\n * event\n * @augments Event\n */\n\n\n Events.VideoDimensionsChangedEvent = function VideoDimensionsChangedEvent(target, oldValue, newValue) {\n return new Event('videoDimensionsChanged', false, {\n target,\n oldValue,\n newValue\n });\n };\n /**\n * Defines event objects for the archiveStarted
and archiveStopped
\n * events. The Session object dispatches these events when an archive recording of the session\n * starts and stops.\n *\n * @property {String} id The archive ID.\n * @property {String} name The name of the archive. You can assign an archive a name when you\n * create it, using the OpenTok REST API or one\n * of the OpenTok server SDKs.\n *\n * @class ArchiveEvent\n * @augments Event\n */\n\n\n Events.ArchiveEvent = function ArchiveEvent(type, archive) {\n return new Event(type, false, {\n id: archive.id,\n name: archive.name,\n status: archive.status,\n archive\n });\n };\n\n Events.ArchiveUpdatedEvent = function ArchiveUpdatedEvent(stream, key, oldValue, newValue) {\n return new Event('updated', false, {\n target: stream,\n changedProperty: key,\n oldValue,\n newValue\n });\n };\n /**\n * The Session object dispatches a signal event when the client receives a signal from the\n * session.\n *\n * @class SignalEvent\n * @property {String} type The type assigned to the signal (if there is one). Use the type to\n * filter signals received (by adding an event handler for signal:type1 or signal:type2, etc.)\n * @property {String} data The data string sent with the signal (if there is one).\n * @property {Connection} from The Connection corresponding to the client that sent the\n * signal.\n *\n * @see Session.signal()\n * @see Session events (signal and signal:type)\n * @augments Event\n */\n\n\n Events.SignalEvent = function SignalEvent(type, data, from) {\n return new Event(type ? `signal:${type}` : eventNames.SIGNAL, false, {\n data,\n from\n });\n };\n\n Events.StreamUpdatedEvent = function StreamUpdatedEvent(stream, key, oldValue, newValue) {\n return new Event('updated', false, {\n target: stream,\n changedProperty: key,\n oldValue,\n newValue\n });\n };\n\n Events.DestroyedEvent = function DestroyedEvent(type, target, reason) {\n return new Event(type, false, {\n target,\n reason\n });\n };\n\n Events.ConnectionStateChangedEvent = function ConnectionStateChangedEvent(type, target) {\n return new Event(type, false, {\n target\n });\n };\n /**\n * Defines the event object for the videoDisabled
and videoEnabled
\n * events dispatched by the Subscriber.\n *\n * @class VideoEnabledChangedEvent\n *\n * @property {Boolean} cancelable Whether the event has a default behavior that is cancelable\n * (true
) or not (false
). You can cancel the default behavior by\n * calling the preventDefault()
method of the event object in the callback\n * function. (See preventDefault().)\n *\n * @property {String} reason The reason the video was disabled or enabled. This can be set to one\n * of the following values:\n *\n * \n *\n * \"publishVideo\"
— The publisher started or stopped publishing video,\n * by calling publishVideo(true)
or publishVideo(false)
. \n *\n * \"quality\"
— The OpenTok Media Router starts or stops sending video\n * to the subscriber based on stream quality changes. This feature of the OpenTok Media\n * Router has a subscriber drop the video stream when connectivity degrades. (The subscriber\n * continues to receive the audio stream, if there is one.)\n * \n * If connectivity improves to support video again, the Subscriber object dispatches\n * a videoEnabled
event, and the Subscriber resumes receiving video.\n *
\n * By default, the Subscriber displays a video disabled indicator when a\n * videoDisabled
event with this reason is dispatched and removes the indicator\n * when the videoEnabled
event with this reason is dispatched. You can control\n * the display of this icon by calling the setStyle()
method of the Subscriber,\n * setting the videoDisabledDisplayMode
property(or you can set the style when\n * calling the Session.subscribe()
method, setting the style
property\n * of the properties
parameter).\n *
\n * This feature is only available in sessions that use the OpenTok Media Router (sessions with\n * the media mode\n * set to routed), not in sessions with the media mode set to relayed.\n *
\n *\n * \"subscribeToVideo\"
— The subscriber started or stopped subscribing to\n * video, by calling subscribeToVideo(true)
or\n * subscribeToVideo(false)
. \n *\n * \"codecNotSupported\"
— The subscriber stopped subscribing to video due\n * to an incompatible codec. \n *\n * \"codecChanged\"
— The subscriber video was enabled after a codec change\n * from an incompatible codec. \n *\n *
\n *\n * @property {Object} target The object that dispatched the event.\n *\n * @property {String} type The type of event: \"videoDisabled\"
or\n * \"videoEnabled\"
.\n *\n * @see Subscriber videoDisabled event\n * @see Subscriber videoEnabled event\n * @augments Event\n */\n\n\n Events.VideoEnabledChangedEvent = function VideoEnabledChangedEvent(type, properties) {\n return new Event(type, false, {\n reason: properties.reason\n });\n };\n\n Events.VideoDisableWarningEvent = function VideoDisableWarningEvent(type\n /* , properties */\n ) {\n return new Event(type, false);\n };\n /**\n * Dispatched periodically by a Subscriber or Publisher object to indicate the audio\n * level. This event is dispatched up to 60 times per second, depending on the browser.\n *\n * @property {Number} audioLevel The audio level, from 0 to 1.0. Adjust this value logarithmically\n * for use in adjusting a user interface element, such as a volume meter. Use a moving average\n * to smooth the data.\n *\n * @class AudioLevelUpdatedEvent\n * @augments Event\n */\n\n\n Events.AudioLevelUpdatedEvent = function AudioLevelUpdatedEvent(audioLevel) {\n return new Event(eventNames.AUDIO_LEVEL_UPDATED, false, {\n audioLevel\n });\n };\n /**\n * Dispatched by a Publisher when the user has stopped sharing one or all media types\n * (video, audio, or screen).\n *\n * @property {MediaStreamTrack} track The media track that has ended. This property is undefined\n * if all media tracks have stopped. Check the kind
property of this object to\n * see if the track is an audio track or a video track.\n *\n * @class MediaStoppedEvent\n * @augments Event\n * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack\n */\n\n\n Events.MediaStoppedEvent = function MediaStoppedEvent(target, track) {\n return new Event(eventNames.MEDIA_STOPPED, true, {\n target,\n track\n });\n };\n /**\n * Dispatched by a Publisher when its audio is muted by a moderator.\n *\n * @class MuteForcedEvent\n * @augments Event\n */\n\n\n Events.MuteForcedEvent = function MuteForcedEvent() {\n return new Event(eventNames.MUTE_FORCED, false);\n };\n /**\n * Dispatched by a Subscriber or Publisher object to indicate the video
element\n * (or object
element in Internet Explorer) was created. Add a listener for this event\n * when you set the insertDefaultUI
option to false
in the call to the\n * OT.initPublisher() method or the\n * Session.subscribe() method. The element
\n * property of the event object is a reference to the Publisher's video
element\n * (or the object
element in Internet Explorer). Add it to the HTML DOM to display the\n * video. (When you set the insertDefaultUI
option to false
, the\n * video
element is not inserted into the DOM automatically.)\n * \n * Add a listener for this event only if you have set the insertDefaultUI
option to\n * false
. If you have not set insertDefaultUI
option\n * to false
, do not move the video
element (or the object
\n * element containing the video in Internet Explorer) in the HTML DOM. Doing so causes the\n * Publisher or Subscriber object to be destroyed.\n *\n * @property {Number} element A reference to the Publisher or Subscriber's video
\n * element (or in Internet Explorer the object
element containing the video).\n * Add it to the HTML DOM to display the video.\n *\n * @class VideoElementCreatedEvent\n * @augments Event\n */\n\n\n Events.VideoElementCreatedEvent = function VideoElementCreatedEvent(element) {\n return new Event(eventNames.VIDEO_ELEMENT_CREATED, false, {\n element\n });\n };\n\n return Events;\n};\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isFunction = __webpack_require__(13),\n isLength = __webpack_require__(112);\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\nmodule.exports = isArrayLike;\n\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// TODO: Eliminate the need for this module, which is globally tracking these objects.\nconst Collection = __webpack_require__(246);\n\nconst sessionObjects = {\n // Publishers are id'd by their guid\n publishers: new Collection('guid'),\n // Subscribers are id'd by their widgetId\n subscribers: new Collection('widgetId'),\n sessions: new Collection()\n};\nmodule.exports = sessionObjects;\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// Event names lookup\nmodule.exports = {\n // Activity Status for cams/mics\n ACTIVE: 'active',\n INACTIVE: 'inactive',\n UNKNOWN: 'unknown',\n // Archive types\n PER_SESSION: 'perSession',\n PER_STREAM: 'perStream',\n // Events\n EXCEPTION: 'exception',\n ISSUE_REPORTED: 'issueReported',\n // Session Events\n SESSION_CONNECTED: 'sessionConnected',\n SESSION_RECONNECTING: 'sessionReconnecting',\n SESSION_RECONNECTED: 'sessionReconnected',\n SESSION_DISCONNECTED: 'sessionDisconnected',\n STREAM_CREATED: 'streamCreated',\n STREAM_DESTROYED: 'streamDestroyed',\n CONNECTION_CREATED: 'connectionCreated',\n CONNECTION_DESTROYED: 'connectionDestroyed',\n SIGNAL: 'signal',\n STREAM_PROPERTY_CHANGED: 'streamPropertyChanged',\n MICROPHONE_LEVEL_CHANGED: 'microphoneLevelChanged',\n // Publisher Events\n RESIZE: 'resize',\n SETTINGS_BUTTON_CLICK: 'settingsButtonClick',\n DEVICE_INACTIVE: 'deviceInactive',\n INVALID_DEVICE_NAME: 'invalidDeviceName',\n ACCESS_ALLOWED: 'accessAllowed',\n ACCESS_DENIED: 'accessDenied',\n ACCESS_DIALOG_OPENED: 'accessDialogOpened',\n ACCESS_DIALOG_CLOSED: 'accessDialogClosed',\n ECHO_CANCELLATION_MODE_CHANGED: 'echoCancellationModeChanged',\n MEDIA_STOPPED: 'mediaStopped',\n PUBLISHER_DESTROYED: 'destroyed',\n MUTE_FORCED: 'muteForced',\n // Subscriber Events\n SUBSCRIBER_DESTROYED: 'destroyed',\n SUBSCRIBER_CONNECTED: 'connected',\n SUBSCRIBER_DISCONNECTED: 'disconnected',\n // DeviceManager Events\n DEVICES_DETECTED: 'devicesDetected',\n // DevicePanel Events\n DEVICES_SELECTED: 'devicesSelected',\n CLOSE_BUTTON_CLICK: 'closeButtonClick',\n MICLEVEL: 'microphoneActivityLevel',\n MICGAINCHANGED: 'microphoneGainChanged',\n // Environment Loader\n ENV_LOADED: 'envLoaded',\n ENV_UNLOADED: 'envUnloaded',\n // Audio activity Events\n AUDIO_LEVEL_UPDATED: 'audioLevelUpdated',\n VIDEO_ELEMENT_CREATED: 'videoElementCreated'\n};\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports) {\n\nfunction _extends() {\n module.exports = _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n };\n\n return _extends.apply(this, arguments);\n}\n\nmodule.exports = _extends;\n\n/***/ }),\n/* 26 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n\n\nvar logDisabled_ = true;\nvar deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nfunction extractVersion(uastring, expr, pos) {\n var match = uastring.match(expr);\n return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nfunction wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n if (!window.RTCPeerConnection) {\n return;\n }\n var proto = window.RTCPeerConnection.prototype;\n var nativeAddEventListener = proto.addEventListener;\n proto.addEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap) {\n return nativeAddEventListener.apply(this, arguments);\n }\n var wrappedCallback = function(e) {\n var modifiedEvent = wrapper(e);\n if (modifiedEvent) {\n cb(modifiedEvent);\n }\n };\n this._eventMap = this._eventMap || {};\n this._eventMap[cb] = wrappedCallback;\n return nativeAddEventListener.apply(this, [nativeEventName,\n wrappedCallback]);\n };\n\n var nativeRemoveEventListener = proto.removeEventListener;\n proto.removeEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap || !this._eventMap\n || !this._eventMap[cb]) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n var unwrappedCb = this._eventMap[cb];\n delete this._eventMap[cb];\n return nativeRemoveEventListener.apply(this, [nativeEventName,\n unwrappedCb]);\n };\n\n Object.defineProperty(proto, 'on' + eventNameToWrap, {\n get: function() {\n return this['_on' + eventNameToWrap];\n },\n set: function(cb) {\n if (this['_on' + eventNameToWrap]) {\n this.removeEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap]);\n delete this['_on' + eventNameToWrap];\n }\n if (cb) {\n this.addEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap] = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n}\n\n// Utility methods.\nmodule.exports = {\n extractVersion: extractVersion,\n wrapPeerConnectionEvent: wrapPeerConnectionEvent,\n disableLog: function(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n logDisabled_ = bool;\n return (bool) ? 'adapter.js logging disabled' :\n 'adapter.js logging enabled';\n },\n\n /**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\n disableWarnings: function(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n deprecationWarnings_ = !bool;\n return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n },\n\n log: function() {\n if (typeof window === 'object') {\n if (logDisabled_) {\n return;\n }\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log.apply(console, arguments);\n }\n }\n },\n\n /**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\n deprecated: function(oldMethod, newMethod) {\n if (!deprecationWarnings_) {\n return;\n }\n console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n ' instead.');\n },\n\n /**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n * properties.\n */\n detectBrowser: function(window) {\n var navigator = window && window.navigator;\n\n // Returned result object.\n var result = {};\n result.browser = null;\n result.version = null;\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator) {\n result.browser = 'Not a browser.';\n return result;\n }\n\n if (navigator.mozGetUserMedia) { // Firefox.\n result.browser = 'firefox';\n result.version = extractVersion(navigator.userAgent,\n /Firefox\\/(\\d+)\\./, 1);\n } else if (navigator.webkitGetUserMedia) {\n // Chrome, Chromium, Webview, Opera.\n // Version matches Chrome/WebRTC version.\n result.browser = 'chrome';\n result.version = extractVersion(navigator.userAgent,\n /Chrom(e|ium)\\/(\\d+)\\./, 2);\n } else if (navigator.mediaDevices &&\n navigator.userAgent.match(/Edge\\/(\\d+).(\\d+)$/)) { // Edge.\n result.browser = 'edge';\n result.version = extractVersion(navigator.userAgent,\n /Edge\\/(\\d+).(\\d+)$/, 2);\n } else if (window.RTCPeerConnection &&\n navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n result.browser = 'safari';\n result.version = extractVersion(navigator.userAgent,\n /AppleWebKit\\/(\\d+)\\./, 1);\n } else { // Default fallthrough: not supported.\n result.browser = 'Not a supported browser.';\n return result;\n }\n\n return result;\n }\n};\n\n\n/***/ }),\n/* 27 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Symbol = __webpack_require__(36),\n getRawTag = __webpack_require__(348),\n objectToString = __webpack_require__(349);\n\n/** `Object#toString` result references. */\nvar nullTag = '[object Null]',\n undefinedTag = '[object Undefined]';\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n}\n\nmodule.exports = baseGetTag;\n\n\n/***/ }),\n/* 28 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayLikeKeys = __webpack_require__(175),\n baseKeys = __webpack_require__(114),\n isArrayLike = __webpack_require__(22);\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\nmodule.exports = keys;\n\n\n/***/ }),\n/* 29 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isValue = __webpack_require__(40);\n\nmodule.exports = function (value) {\n\tif (!isValue(value)) throw new TypeError(\"Cannot use null or undefined\");\n\treturn value;\n};\n\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isValue = __webpack_require__(53)\n , isPlainFunction = __webpack_require__(196)\n , assign = __webpack_require__(125)\n , normalizeOpts = __webpack_require__(197)\n , contains = __webpack_require__(198);\n\nvar d = (module.exports = function (dscr, value/*, options*/) {\n\tvar c, e, w, options, desc;\n\tif (arguments.length < 2 || typeof dscr !== \"string\") {\n\t\toptions = value;\n\t\tvalue = dscr;\n\t\tdscr = null;\n\t} else {\n\t\toptions = arguments[2];\n\t}\n\tif (isValue(dscr)) {\n\t\tc = contains.call(dscr, \"c\");\n\t\te = contains.call(dscr, \"e\");\n\t\tw = contains.call(dscr, \"w\");\n\t} else {\n\t\tc = w = true;\n\t\te = false;\n\t}\n\n\tdesc = { value: value, configurable: c, enumerable: e, writable: w };\n\treturn !options ? desc : assign(normalizeOpts(options), desc);\n});\n\nd.gs = function (dscr, get, set/*, options*/) {\n\tvar c, e, options, desc;\n\tif (typeof dscr !== \"string\") {\n\t\toptions = set;\n\t\tset = get;\n\t\tget = dscr;\n\t\tdscr = null;\n\t} else {\n\t\toptions = arguments[3];\n\t}\n\tif (!isValue(get)) {\n\t\tget = undefined;\n\t} else if (!isPlainFunction(get)) {\n\t\toptions = get;\n\t\tget = set = undefined;\n\t} else if (!isValue(set)) {\n\t\tset = undefined;\n\t} else if (!isPlainFunction(set)) {\n\t\toptions = set;\n\t\tset = undefined;\n\t}\n\tif (isValue(dscr)) {\n\t\tc = contains.call(dscr, \"c\");\n\t\te = contains.call(dscr, \"e\");\n\t} else {\n\t\tc = true;\n\t\te = false;\n\t}\n\n\tdesc = { get: get, set: set, configurable: c, enumerable: e };\n\treturn !options ? desc : assign(normalizeOpts(options), desc);\n};\n\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports) {\n\n/**\n * A specialized version of `_.map` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\nfunction arrayMap(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length,\n result = Array(length);\n\n while (++index < length) {\n result[index] = iteratee(array[index], index, array);\n }\n return result;\n}\n\nmodule.exports = arrayMap;\n\n\n/***/ }),\n/* 32 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseMatches = __webpack_require__(492),\n baseMatchesProperty = __webpack_require__(498),\n identity = __webpack_require__(88),\n isArray = __webpack_require__(10),\n property = __webpack_require__(503);\n\n/**\n * The base implementation of `_.iteratee`.\n *\n * @private\n * @param {*} [value=_.identity] The value to convert to an iteratee.\n * @returns {Function} Returns the iteratee.\n */\nfunction baseIteratee(value) {\n // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.\n // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.\n if (typeof value == 'function') {\n return value;\n }\n if (value == null) {\n return identity;\n }\n if (typeof value == 'object') {\n return isArray(value)\n ? baseMatchesProperty(value[0], value[1])\n : baseMatches(value);\n }\n return property(value);\n}\n\nmodule.exports = baseIteratee;\n\n\n/***/ }),\n/* 33 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-param-reassign */\nconst OTHelpers = __webpack_require__(4); // A mixin to encapsulate the basic widget behaviour. This needs a better name,\n// it's not actually a widget. It's actually \"Behaviour that can be applied to\n// an object to make it support the basic Chrome widget workflow\"...but that would\n// probably been too long a name.\n\n\nmodule.exports = function Widget(widget, options) {\n let _mode;\n\n const _options = options || {}; //\n // @param [String] mode\n // 'on', 'off', or 'auto'\n //\n\n\n widget.setDisplayMode = function (mode) {\n const newMode = mode || 'auto';\n\n if (_mode === newMode) {\n return;\n }\n\n OTHelpers.removeClass(this.domElement, `OT_mode-${_mode}`);\n OTHelpers.addClass(this.domElement, `OT_mode-${newMode}`);\n _mode = newMode;\n };\n\n widget.getDisplayMode = function () {\n return _mode;\n };\n\n widget.showAfterLoading = function () {\n OTHelpers.removeClass(this.domElement, 'OT_hide-forced');\n };\n\n widget.hideWhileLoading = function () {\n OTHelpers.addClass(this.domElement, 'OT_hide-forced');\n };\n\n widget.destroy = function () {\n if (_options.onDestroy) {\n _options.onDestroy(this.domElement);\n }\n\n if (this.domElement) {\n OTHelpers.removeElement(this.domElement);\n }\n\n return widget;\n };\n\n widget.appendTo = function (parent) {\n // create the element under parent\n this.domElement = OTHelpers.createElement(_options.nodeName || 'div', _options.htmlAttributes, _options.htmlContent);\n\n if (_options.onCreate) {\n _options.onCreate(this.domElement);\n }\n\n widget.setDisplayMode(_options.mode);\n\n if (_options.mode === 'auto') {\n // if the mode is auto we hold the \"on mode\" for 2 seconds\n // this will let the proper widgets nicely fade away and help discoverability\n OTHelpers.addClass(widget.domElement, 'OT_mode-on-hold');\n setTimeout(() => {\n OTHelpers.removeClass(widget.domElement, 'OT_mode-on-hold');\n }, 2000);\n } // add the widget to the parent\n\n\n parent.appendChild(this.domElement);\n return widget;\n };\n};\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseClone = __webpack_require__(77);\n\n/** Used to compose bitmasks for cloning. */\nvar CLONE_SYMBOLS_FLAG = 4;\n\n/**\n * Creates a shallow clone of `value`.\n *\n * **Note:** This method is loosely based on the\n * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)\n * and supports cloning arrays, array buffers, booleans, date objects, maps,\n * numbers, `Object` objects, regexes, sets, strings, symbols, and typed\n * arrays. The own enumerable properties of `arguments` objects are cloned\n * as plain objects. An empty object is returned for uncloneable values such\n * as error objects, functions, DOM nodes, and WeakMaps.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to clone.\n * @returns {*} Returns the cloned value.\n * @see _.cloneDeep\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var shallow = _.clone(objects);\n * console.log(shallow[0] === objects[0]);\n * // => true\n */\nfunction clone(value) {\n return baseClone(value, CLONE_SYMBOLS_FLAG);\n}\n\nmodule.exports = clone;\n\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseIsNative = __webpack_require__(347),\n getValue = __webpack_require__(352);\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\nmodule.exports = getNative;\n\n\n/***/ }),\n/* 36 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar root = __webpack_require__(12);\n\n/** Built-in value references. */\nvar Symbol = root.Symbol;\n\nmodule.exports = Symbol;\n\n\n/***/ }),\n/* 37 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar assignValue = __webpack_require__(83),\n baseAssignValue = __webpack_require__(62);\n\n/**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\nfunction copyObject(source, props, object, customizer) {\n var isNew = !object;\n object || (object = {});\n\n var index = -1,\n length = props.length;\n\n while (++index < length) {\n var key = props[index];\n\n var newValue = customizer\n ? customizer(object[key], source[key], key, object, source)\n : undefined;\n\n if (newValue === undefined) {\n newValue = source[key];\n }\n if (isNew) {\n baseAssignValue(object, key, newValue);\n } else {\n assignValue(object, key, newValue);\n }\n }\n return object;\n}\n\nmodule.exports = copyObject;\n\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar DataView = __webpack_require__(376),\n Map = __webpack_require__(109),\n Promise = __webpack_require__(377),\n Set = __webpack_require__(183),\n WeakMap = __webpack_require__(184),\n baseGetTag = __webpack_require__(27),\n toSource = __webpack_require__(172);\n\n/** `Object#toString` result references. */\nvar mapTag = '[object Map]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n setTag = '[object Set]',\n weakMapTag = '[object WeakMap]';\n\nvar dataViewTag = '[object DataView]';\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n (Map && getTag(new Map) != mapTag) ||\n (Promise && getTag(Promise.resolve()) != promiseTag) ||\n (Set && getTag(new Set) != setTag) ||\n (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n getTag = function(value) {\n var result = baseGetTag(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : '';\n\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString: return dataViewTag;\n case mapCtorString: return mapTag;\n case promiseCtorString: return promiseTag;\n case setCtorString: return setTag;\n case weakMapCtorString: return weakMapTag;\n }\n }\n return result;\n };\n}\n\nmodule.exports = getTag;\n\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function eventListener() {\n if (errorListener !== undefined) {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n var errorListener;\n\n // Adding an error listener is not optional because\n // if an error is thrown on an event emitter we cannot\n // guarantee that the actual event we are waiting will\n // be fired. The result could be a silent way to create\n // memory or file descriptor leaks, which is something\n // we should avoid.\n if (name !== 'error') {\n errorListener = function errorListener(err) {\n emitter.removeListener(name, eventListener);\n reject(err);\n };\n\n emitter.once('error', errorListener);\n }\n\n emitter.once(name, eventListener);\n });\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 40 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _undefined = __webpack_require__(395)(); // Support ES3 engines\n\nmodule.exports = function (val) { return val !== _undefined && val !== null; };\n\n\n/***/ }),\n/* 41 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = __webpack_require__(409)()\n\t? __webpack_require__(91).Symbol\n\t: __webpack_require__(412);\n\n\n/***/ }),\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\nvar _createClass2 = _interopRequireDefault(__webpack_require__(43));\n\n/* global __PROPERTIES__ */\nconst defaultAxios = __webpack_require__(204);\n\nconst cloneDeep = __webpack_require__(54);\n\nconst pick = __webpack_require__(466);\n\nconst mapKeys = __webpack_require__(513);\n\nconst logging = __webpack_require__(0)('StaticConfig');\n\nconst _require = __webpack_require__(138),\n prependProxyToUrlIfNeeded = _require.prependProxyToUrlIfNeeded;\n/**\n * @typedef {Object} builtInConfig properties that are baked in and are injected through webpack\n * @property {String} version Build version (comes from package.json)\n * @property {String} buildHash Commit hash this build was built from\n * @property {Object} minimumVersion\n * @property {String} minimumVersion.firefox Minimum version of Firefox needed to support OpenTok\n * @property {String} minimumVersion.chrome Minimum version of Chrome needed to support OpenTok\n * @property {Boolean} debug If true sets logging level to DEBUG else sets it to WARN\n * @property {String} websiteURL Used to construct urls to the TokBox website\n * @property {String} loggingURL Where to send analytical events\n * @property {String} apiURL The API to talk to (Anvil)\n*/\n\n/** @type builtInConfig */\n\n\nconst builtInConfig = cloneDeep({\"version\":\"v2.20.3\",\"buildHash\":\"68d8eaac8\",\"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\"});\nconst whitelistAllowedRuntimeProperties = pick(['apiURL', 'assetURL', 'cdnURL', 'sessionInfoOverrides', 'loggingURL']);\nconst liveConfigMap = {\n apiUrl: 'apiURL',\n loggingUrl: 'loggingURL'\n};\nconst normalizeLiveConfig = mapKeys(key => liveConfigMap[key]);\n\nfunction staticConfigFactory(_temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n _ref$axios = _ref.axios,\n axios = _ref$axios === void 0 ? defaultAxios : _ref$axios;\n\n /**\n * @class StaticConfig\n */\n let StaticConfig = /*#__PURE__*/function () {\n StaticConfig.onlyLocal = function onlyLocal() {\n const runtimeProperties = cloneDeep((typeof window !== undefined ? window : global).OTProperties);\n return new StaticConfig((0, _extends2.default)({}, builtInConfig, whitelistAllowedRuntimeProperties(runtimeProperties)));\n }\n /**\n * Construct a StaticConfig instance with baked in, runtime, and live configuration\n *\n * @static\n * @param {any} { sessionId, token, useIpWhitelistConfigUrl }\n * @memberof StaticConfig\n * @return {Promise} A promise to an instance of StaticConfig\n */\n ;\n\n StaticConfig.get = function get(_ref2) {\n let partnerId = _ref2.partnerId,\n token = _ref2.token,\n useIpWhitelistConfigUrl = _ref2.useIpWhitelistConfigUrl,\n proxyUrl = _ref2.proxyUrl;\n\n const getLiveConfig = () => {\n const localStaticConfig = this.onlyLocal();\n\n if (!localStaticConfig.configUrl) {\n return Promise.resolve({});\n }\n\n if (useIpWhitelistConfigUrl === true && !localStaticConfig.ipWhitelistConfigUrl) {\n return Promise.resolve({});\n }\n\n let finalConfigUrl = useIpWhitelistConfigUrl === true ? localStaticConfig.ipWhitelistConfigUrl : localStaticConfig.configUrl;\n finalConfigUrl = prependProxyToUrlIfNeeded(finalConfigUrl, proxyUrl);\n return axios.get(`${finalConfigUrl}/project/${partnerId}/config.json`, {\n headers: {\n 'X-TB-TOKEN-AUTH': token\n }\n }).then((_ref3) => {\n let data = _ref3.data;\n return data;\n });\n };\n\n return getLiveConfig().catch(err => {\n logging.error('could not reach live config service', err);\n return {};\n }).then(liveConfig => {\n const runtimeProperties = cloneDeep((typeof window !== undefined ? window : global).OTProperties);\n const config = (0, _extends2.default)({}, builtInConfig, whitelistAllowedRuntimeProperties(runtimeProperties), normalizeLiveConfig(liveConfig));\n return new StaticConfig(config);\n });\n };\n\n function StaticConfig(config) {\n Object.defineProperty(this, 'config', {\n value: Object.freeze(cloneDeep(config))\n });\n }\n\n (0, _createClass2.default)(StaticConfig, [{\n key: \"configUrl\",\n get: function get() {\n return this.config.configURL;\n }\n }, {\n key: \"ipWhitelistConfigUrl\",\n get: function get() {\n return this.config.ipWhitelistConfigURL;\n }\n }, {\n key: \"apiUrl\",\n get: function get() {\n return this.config.apiURL;\n }\n }, {\n key: \"loggingUrl\",\n get: function get() {\n return this.config.loggingURL;\n }\n }, {\n key: \"apiEnabled\",\n get: function get() {\n return typeof this.config.apiEnabled !== 'undefined' ? this.config.apiEnabled : true;\n }\n }, {\n key: \"version\",\n get: function get() {\n return this.config.version;\n }\n }, {\n key: \"clientVersion\",\n get: function get() {\n return `js-${(this.version || 'unknown').replace('v', '')}`;\n }\n }, {\n key: \"buildHash\",\n get: function get() {\n return this.config.buildHash;\n }\n }, {\n key: \"minimumVersion\",\n get: function get() {\n return this.config.minimumVersion;\n }\n }, {\n key: \"websiteUrl\",\n get: function get() {\n return this.config.websiteURL;\n }\n }, {\n key: \"debug\",\n get: function get() {\n return this.config.debug === 'true' || this.config.debug === true;\n }\n }, {\n key: \"sessionInfoOverrides\",\n get: function get() {\n return this.config.sessionInfoOverrides;\n }\n }, {\n key: \"cdnUrl\",\n get: function get() {\n return this.config.cdnURL || `${(typeof window !== undefined ? window : global).location.protocol}//${(typeof window !== undefined ? window : global).location.host}`;\n }\n }, {\n key: \"assetUrl\",\n get: function get() {\n return this.config.assetURL || `${this.cdnUrl}/webrtc/${this.version}`;\n }\n }]);\n return StaticConfig;\n }();\n\n return StaticConfig;\n}\n\nmodule.exports = staticConfigFactory;\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 43 */\n/***/ (function(module, exports) {\n\nfunction _defineProperties(target, props) {\n for (var i = 0; i < props.length; i++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if (\"value\" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, descriptor.key, descriptor);\n }\n}\n\nfunction _createClass(Constructor, protoProps, staticProps) {\n if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n if (staticProps) _defineProperties(Constructor, staticProps);\n return Constructor;\n}\n\nmodule.exports = _createClass;\n\n/***/ }),\n/* 44 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isSymbol = __webpack_require__(70);\n\n/** Used as references for various `Number` constants. */\nvar INFINITY = 1 / 0;\n\n/**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\nfunction toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n}\n\nmodule.exports = toKey;\n\n\n/***/ }),\n/* 45 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar basePick = __webpack_require__(510),\n flatRest = __webpack_require__(137);\n\n/**\n * Creates an object composed of the picked `object` properties.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pick(object, ['a', 'c']);\n * // => { 'a': 1, 'c': 3 }\n */\nvar pick = flatRest(function(object, paths) {\n return object == null ? {} : basePick(object, paths);\n});\n\nmodule.exports = pick;\n\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\nvar _createClass2 = _interopRequireDefault(__webpack_require__(43));\n\nvar _defineProperty2 = _interopRequireDefault(__webpack_require__(16));\n\n/* eslint-disable no-underscore-dangle */\nconst guidStorage = __webpack_require__(238);\n\nconst Analytics = __webpack_require__(241);\n\nconst SessionInfo = __webpack_require__(141);\n\nconst StaticConfig = __webpack_require__(42)();\n\nconst eventing = __webpack_require__(5);\n\nconst sanitizeQosData = __webpack_require__(533);\n\nconst LOG_VERSION = '2';\n\nlet AnalyticsHelper = /*#__PURE__*/function () {\n function AnalyticsHelper(_temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n _ref$staticConfig = _ref.staticConfig,\n staticConfig = _ref$staticConfig === void 0 ? StaticConfig.onlyLocal() : _ref$staticConfig,\n _ref$sessionInfo = _ref.sessionInfo,\n sessionInfo = _ref$sessionInfo === void 0 ? new SessionInfo() : _ref$sessionInfo,\n ajax = _ref.ajax,\n queue = _ref.queue;\n\n (0, _defineProperty2.default)(this, \"getTurnServerName\", iceServers => {\n let turnNoTLSServerName;\n let turnTLSServerName; // In case we don't find the server name we will be returning nothing\n // which will be ignored and relay server will not be present in the logs\n\n if (!iceServers || typeof iceServers !== 'object') return turnNoTLSServerName;\n const servers = Array.isArray(iceServers) ? iceServers : [iceServers];\n servers.forEach(server => {\n const urls = server.urls || server.url;\n const arrUrl = Array.isArray(urls) ? urls : [urls];\n arrUrl.forEach(url => {\n // Index where server name value starts\n const prefixIndex = url.indexOf(':') + 1; // Index of the port number\n\n const suffixIndex = url.lastIndexOf(':'); // We assume that TLS turn, i.e. turns, is the one which contains\n // the domain name.\n\n if (url.includes('turns')) {\n turnTLSServerName = url.substring(prefixIndex, suffixIndex);\n } else {\n turnNoTLSServerName = url.substring(prefixIndex, suffixIndex);\n }\n });\n });\n return turnTLSServerName || turnNoTLSServerName;\n });\n this.ajax = ajax;\n this.queue = queue;\n this.sessionInfo = sessionInfo;\n this.staticConfig = staticConfig;\n }\n\n var _proto = AnalyticsHelper.prototype;\n\n _proto._getCommon = function _getCommon() {\n return {\n clientVersion: this.staticConfig.clientVersion,\n buildHash: this.staticConfig.buildHash,\n source: (typeof window !== undefined ? window : global).location && (typeof window !== undefined ? window : global).location.href,\n logVersion: LOG_VERSION,\n apiServer: this.staticConfig.apiUrl,\n clientSystemTime: new Date().getTime(),\n sessionId: this.sessionInfo.sessionId,\n mediaServerName: this.sessionInfo.mediaServerName,\n relayServer: this.getTurnServerName(this.sessionInfo.iceServers),\n p2p: this.sessionInfo.p2pEnabled,\n messagingServer: this.sessionInfo.messagingServer,\n messagingUrl: this.sessionInfo.messagingURL,\n version: this.staticConfig.version,\n partnerId: this.sessionInfo.partnerId\n };\n };\n\n _proto.logError = function logError(code, type, message, details, options) {\n if (options === void 0) {\n options = {};\n }\n\n guidStorage.get((error, guid) => {\n if (error) {\n // @todo\n return;\n }\n\n const args = [code, type, message, details, (0, _extends2.default)({\n guid\n }, this.combineWithCommon(options))];\n AnalyticsHelper.emit('logError', ...args);\n\n this._analytics.logError(...args);\n });\n };\n\n _proto.combineWithCommon = function combineWithCommon(options) {\n return (0, _extends2.default)({}, this._getCommon(), options);\n };\n\n _proto.logEvent = function logEvent(options, throttle, completionHandler) {\n if (options === void 0) {\n options = {};\n }\n\n guidStorage.get((error, guid) => {\n if (error) {\n // @todo\n return;\n }\n\n const logData = (0, _extends2.default)({\n guid\n }, this.combineWithCommon(options));\n AnalyticsHelper.emit('logEvent', logData);\n\n this._analytics.logEvent(logData, false, throttle, completionHandler);\n });\n };\n\n _proto.logQOS = function logQOS(options) {\n if (options === void 0) {\n options = {};\n }\n\n guidStorage.get((error, guid) => {\n if (error) {\n // @todo\n return;\n }\n\n const qosData = (0, _extends2.default)({\n guid,\n duration: 0\n }, this.combineWithCommon(options));\n sanitizeQosData(qosData);\n AnalyticsHelper.emit('logQOS', qosData);\n\n this._analytics.logQOS(qosData);\n });\n };\n\n (0, _createClass2.default)(AnalyticsHelper, [{\n key: \"staticConfig\",\n get: function get() {\n return this._staticConfig;\n },\n set: function set(staticConfig) {\n this._staticConfig = staticConfig;\n this._analytics = new Analytics({\n loggingUrl: this.staticConfig.loggingUrl,\n ajax: this.ajax,\n queue: this.queue\n });\n }\n }]);\n return AnalyticsHelper;\n}();\n\neventing(AnalyticsHelper);\nmodule.exports = AnalyticsHelper;\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst performance = (typeof window !== undefined ? window : global).performance || {};\n/**\n * Returns the number of milliseconds since the the UNIX epoch\n *\n * When available (performance api exists), this function will not be skewed\n * by clock adjustments. Only use if you require this functionality, otherwise\n * use Date.now().\n *\n * @returns {number} Number of milliseconds since UNIX epoch\n */\n\nmodule.exports = function highResolutionNow() {\n if (performance.now) {\n return performance.timing.navigationStart + performance.now();\n }\n\n return Date.now();\n};\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar before = __webpack_require__(566);\n\n/**\n * Creates a function that is restricted to invoking `func` once. Repeat calls\n * to the function return the value of the first invocation. The `func` is\n * invoked with the `this` binding and arguments of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var initialize = _.once(createApplication);\n * initialize();\n * initialize();\n * // => `createApplication` is invoked once\n */\nfunction once(func) {\n return before(2, func);\n}\n\nmodule.exports = once;\n\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-void, one-var, prefer-const, no-shadow, vars-on-top, no-var */\n\n/* eslint-disable no-mixed-operators */\nconst assign = __webpack_require__(7);\n\nconst find = __webpack_require__(60);\n\nconst findIndex = __webpack_require__(270);\n\nconst intersection = __webpack_require__(127);\n\nconst uniq = __webpack_require__(271);\n\nconst logging = __webpack_require__(0)('SDPHelpers');\n\nconst START_MEDIA_SSRC = 10000;\nconst START_RTX_SSRC = 20000; // Here are the structure of the rtpmap attribute and the media line, most of the\n// complex Regular Expressions in this code are matching against one of these two\n// formats:\n// * a=rtpmap: / [/]\n// * m= / \n//\n// References:\n// * https://tools.ietf.org/html/rfc4566\n// * http://en.wikipedia.org/wiki/Session_Description_Protocol\n//\n//\n\nconst SDPHelpers = {\n getSections(sdp) {\n return sdp.split(/\\r\\n|\\r|\\n/).reduce((accum, line) => {\n const match = line.match(/^m=(\\w+) \\d+/);\n\n if (match) {\n accum.sections[accum.section = match[1]] = []; // eslint-disable-line no-param-reassign\n }\n\n accum.sections[accum.section].push(line);\n return accum;\n }, {\n sections: {\n header: []\n },\n section: 'header'\n }).sections;\n },\n\n getCodecsAndCodecMap(sdp, mediaType) {\n const section = SDPHelpers.getSections(sdp)[mediaType];\n\n if (!section) {\n throw new Error(`no mediaType ${mediaType}`);\n }\n\n const codecs = section[0].match(/m=\\w+ \\d+ [A-Z/]+ ([\\d ]+)$/)[1].split(' ');\n const codecMap = assign(...section.filter(line => line.match(/^a=rtpmap:\\d+/)).map(line => line.match(/^a=rtpmap:(\\d+) ([\\w-]+)/).splice(1)).map((_ref) => {\n let num = _ref[0],\n codec = _ref[1];\n return {\n [num]: codec\n };\n }));\n return {\n codecs,\n codecMap\n };\n },\n\n getCodecs(sdp, mediaType) {\n const codecsAndCodecMap = SDPHelpers.getCodecsAndCodecMap(sdp, mediaType);\n return codecsAndCodecMap.codecs.map(num => codecsAndCodecMap.codecMap[num] || 'Unknown codec');\n },\n\n mediaDirections: {\n INACTIVE: 'inactive',\n RECVONLY: 'recvonly'\n }\n};\nmodule.exports = SDPHelpers; // Search through sdpLines to find the Media Line of type +mediaType+.\n\nSDPHelpers.getMLineIndex = function getMLineIndex(sdpLines, mediaType) {\n const targetMLine = `m=${mediaType}`; // Find the index of the media line for +type+\n\n return findIndex(sdpLines, line => {\n if (line.indexOf(targetMLine) !== -1) {\n return true;\n }\n\n return false;\n });\n}; // Grab a M line of a particular +mediaType+ from sdpLines.\n\n\nSDPHelpers.getMLine = function getMLine(sdpLines, mediaType) {\n const mLineIndex = SDPHelpers.getMLineIndex(sdpLines, mediaType);\n return mLineIndex > -1 ? sdpLines[mLineIndex] : void 0;\n};\n\nSDPHelpers.hasMediaType = (sdp, mediaType) => {\n const mLineRegex = new RegExp(`^m=${mediaType}`);\n const sdpLines = sdp.split('\\r\\n');\n return findIndex(sdpLines, line => mLineRegex.test(line)) >= 0;\n};\n\nSDPHelpers.hasMLinePayloadType = function hasMLinePayloadType(sdpLines, mediaType, payloadType) {\n const mLine = SDPHelpers.getMLine(sdpLines, mediaType);\n const payloadTypes = SDPHelpers.getMLinePayloadTypes(mLine, mediaType);\n return payloadTypes.indexOf(payloadType) > -1;\n}; // Extract the payload types for a give Media Line.\n//\n\n\nSDPHelpers.getMLinePayloadTypes = function getMLinePayloadTypes(mediaLine, mediaType) {\n const mLineSelector = new RegExp(`^m=${mediaType} \\\\d+(/\\\\d+)? [a-zA-Z0-9/]+(( [a-zA-Z0-9/]+)+)$`, 'i'); // Get all payload types that the line supports\n\n const payloadTypes = mediaLine.match(mLineSelector);\n\n if (!payloadTypes || payloadTypes.length < 2) {\n // Error, invalid M line?\n return [];\n }\n\n return payloadTypes[2].trim().split(' ');\n}; // Splits SDP into sessionpart and mediasections. Ensures CRLF.\n\n\nSDPHelpers.splitSections = function splitSections(sdp) {\n const parts = sdp.split('\\nm='); // eslint-disable-next-line prefer-template\n\n return parts.map((part, index) => (index > 0 ? 'm=' + part : part).trim() + '\\r\\n');\n}; // Changes the media direction\n\n\nSDPHelpers.changeMediaDirection = function changeMediaDirection(sdp, newDirection) {\n const replaceDirection = mediaSdpPart => {\n const currentDirection = newDirection === SDPHelpers.mediaDirections.RECVONLY ? 'a=inactive' : 'a=recvonly';\n const regex = new RegExp(currentDirection, 'g');\n return mediaSdpPart.replace(regex, `a=${newDirection}`);\n }; // Splits SDP into session part and media sections.\n\n\n const sections = SDPHelpers.splitSections(sdp); // Preserve the session part since we don't need to modify it.\n\n const sessionSdpPart = sections.shift(); // Replace all the recvonly by inactive or viceversa, depending the active argument\n\n const mediaSdpPart = sections.join('');\n const editedMediaSdpPart = replaceDirection(mediaSdpPart); // Return the session and new media sections concatenated\n\n return sessionSdpPart + editedMediaSdpPart;\n};\n\nSDPHelpers.removeTypesFromMLine = function removeTypesFromMLine(mediaLine, payloadTypes) {\n const typesSuffix = /[0-9 ]*$/.exec(mediaLine)[0];\n const newTypes = typesSuffix.split(' ').filter(type => type !== '' && payloadTypes.indexOf(type) === -1);\n return mediaLine.replace(typesSuffix, ` ${newTypes.join(' ')}`);\n}; // Remove all references to a particular encodingName from a particular media type\n//\n\n\nSDPHelpers.removeMediaEncoding = function removeMediaEncoding(sdp, mediaType, encodingName) {\n let payloadTypes, i, j, parts;\n let sdpLines = sdp.split('\\r\\n');\n const mLineIndex = SDPHelpers.getMLineIndex(sdpLines, mediaType);\n const mLine = mLineIndex > -1 ? sdpLines[mLineIndex] : void 0;\n const typesToRemove = [];\n\n if (mLineIndex === -1) {\n // Error, missing M line\n return sdpLines.join('\\r\\n');\n } // Get all payload types that the line supports\n\n\n payloadTypes = SDPHelpers.getMLinePayloadTypes(mLine, mediaType);\n\n if (payloadTypes.length === 0) {\n // Error, invalid M line?\n return sdpLines.join('\\r\\n');\n } // Find the payloadTypes of the codecs.\n // Allows multiple matches e.g. for CN.\n\n\n for (i = mLineIndex; i < sdpLines.length; i++) {\n const codecRegex = new RegExp(encodingName, 'i');\n\n if (sdpLines[i].indexOf('a=rtpmap:') === 0) {\n parts = sdpLines[i].split(' ');\n\n if (parts.length === 2 && codecRegex.test(parts[1])) {\n typesToRemove.push(parts[0].substr(9));\n }\n }\n }\n\n if (!typesToRemove.length) {\n // Not found.\n return sdpLines.join('\\r\\n');\n } // Also find any rtx which reference the removed codec.\n\n\n for (i = mLineIndex; i < sdpLines.length; i++) {\n if (sdpLines[i].indexOf('a=fmtp:') === 0) {\n parts = sdpLines[i].split(' ');\n\n for (j = 0; j < typesToRemove.length; j++) {\n if (parts.length === 2 && parts[1] === `apt=${typesToRemove[j]}`) {\n typesToRemove.push(parts[0].substr(7));\n }\n }\n }\n } // Remove any rtpmap, fmtp or rtcp-fb.\n\n\n sdpLines = sdpLines.filter(line => {\n for (let i = 0; i < typesToRemove.length; i++) {\n if (line.indexOf(`a=rtpmap:${typesToRemove[i]} `) === 0 || line.indexOf(`a=fmtp:${typesToRemove[i]} `) === 0 || line.indexOf(`a=rtcp-fb:${typesToRemove[i]} `) === 0) {\n return false;\n }\n }\n\n return true;\n });\n\n if (typesToRemove.length > 0 && mLineIndex > -1) {\n // Remove all the payload types and we've removed from the media line\n sdpLines[mLineIndex] = SDPHelpers.removeTypesFromMLine(mLine, typesToRemove);\n }\n\n return sdpLines.join('\\r\\n');\n};\n\nSDPHelpers.disableMediaType = function disableMediaType(sdp, mediaType) {\n const lines = sdp.split('\\r\\n');\n const blocks = [];\n let block; // Separating SDP into blocks. This usually follows the form:\n // Header block:\n // v=0\n // ...\n // Audio block:\n // m=audio\n // ...\n // Video block:\n // m=video\n // ...\n\n lines.forEach(lineParam => {\n let line = lineParam;\n\n if (/^m=/.test(line)) {\n block = undefined;\n }\n\n if (!block) {\n block = [];\n blocks.push(block);\n }\n\n block.push(line);\n }); // Now disable the block for the specified media type\n\n const mLineRegex = new RegExp(`^m=${mediaType} \\\\d+ ([^ ]+) [0-9 ]+$`);\n const fixedBlocks = blocks.map(block => {\n const match = block[0].match(mLineRegex);\n\n if (match) {\n return [`m=${mediaType} 0 ${match[1]} 0`, 'a=inactive', ...block.filter(line => /^c=/.test(line) || /^a=mid:/.test(line) || line === '' // This preserves the trailing newline\n )];\n }\n\n return block;\n });\n return [].concat(...fixedBlocks).join('\\r\\n');\n};\n\nSDPHelpers.removeVideoCodec = function removeVideoCodec(sdp, codec) {\n return SDPHelpers.removeMediaEncoding(sdp, 'video', codec);\n}; // Used to identify whether Video media (for a given set of SDP) supports\n// retransmissions.\n//\n// The algorithm to do could be summarised as:\n//\n// IF ssrc-group:FID exists AND IT HAS AT LEAST TWO IDS THEN\n// we are using RTX\n// ELSE IF \"a=rtpmap: (\\\\d+):rtxPayloadId(/\\\\d+)? rtx/90000\"\n// AND SDPHelpers.hasMLinePayloadType(sdpLines, 'Video', rtxPayloadId)\n// we are using RTX\n// ELSE\n// we are not using RTX\n//\n// The ELSE IF clause basically covers the case where ssrc-group:FID\n// is probably malformed or missing. In that case we verify whether\n// we want RTX by looking at whether it's mentioned in the video\n// media line instead.\n//\n\n\nconst isUsingRTX = function isUsingRTX(sdpLines, videoAttrs) {\n let groupFID = videoAttrs.filterByName('ssrc-group:FID');\n const missingFID = groupFID.length === 0;\n\n if (!missingFID) {\n groupFID = groupFID[0].value.split(' ');\n } else {\n groupFID = [];\n }\n\n switch (groupFID.length) {\n case 0:\n case 1:\n // possibly no RTX, double check for the RTX payload type and that\n // the Video Media line contains that payload type\n //\n // Details: Look for a rtpmap line for rtx/90000\n // If there is one, grab the payload ID for rtx\n // Look to see if that payload ID is listed under the payload types for the m=Video line\n // If it is: RTX\n // else: No RTX for you\n var rtxAttr = videoAttrs.find(attr => attr.name.indexOf('rtpmap:') === 0 && attr.value.indexOf('rtx/90000') > -1);\n\n if (!rtxAttr) {\n return false;\n }\n\n var rtxPayloadId = rtxAttr.name.split(':')[1];\n\n if (rtxPayloadId.indexOf('/') > -1) {\n rtxPayloadId = rtxPayloadId.split('/')[0];\n }\n\n return SDPHelpers.hasMLinePayloadType(sdpLines, 'video', rtxPayloadId);\n\n default:\n // two or more: definitely RTX\n logging.debug('SDP Helpers: There are more than two FIDs, RTX is definitely enabled');\n return true;\n }\n}; // This returns an Array, which is decorated with several\n// SDP specific helper methods.\n//\n\n\nSDPHelpers.getAttributesForMediaType = function getAttributesForMediaType(sdpLines, mediaType) {\n let ssrcStartIndex, ssrcEndIndex, regResult, ssrc, ssrcGroup, ssrcLine, msidMatch, msid, mid, midIndex;\n const mLineIndex = SDPHelpers.getMLineIndex(sdpLines, mediaType);\n const matchOtherMLines = new RegExp(`m=(?!${mediaType}).+ `, 'i');\n const matchSSRCLines = new RegExp('a=ssrc:\\\\d+ .*', 'i');\n const matchSSRCGroup = new RegExp('a=ssrc-group:(SIM|FID) (\\\\d+).*?', 'i');\n const matchAttrLine = new RegExp('a=([a-z0-9:/-]+) (.*)', 'i');\n const attrs = [];\n\n for (let i = mLineIndex + 1; i < sdpLines.length; i++) {\n // If we were inside an SSRC block and we've now reached an m-line, then\n // tag the previous line as the end of the SSRC block\n if (matchOtherMLines.test(sdpLines[i])) {\n if (ssrcStartIndex !== undefined) {\n ssrcEndIndex = i - 1;\n }\n\n break;\n } // Get the ssrc\n\n\n ssrcGroup = sdpLines[i].match(matchSSRCGroup);\n ssrcLine = sdpLines[i].match(matchSSRCLines);\n\n if (ssrcStartIndex === undefined && (ssrcGroup || ssrcLine)) {\n // We found the start of an SSRC block\n ssrcStartIndex = i;\n\n if (ssrcGroup) {\n ssrc = /^(FID|SIM)$/.test(ssrcGroup[1]) ? ssrcGroup[2] : ssrcGroup[1];\n }\n } // Get the msid\n\n\n msidMatch = sdpLines[i].match(`a=ssrc:${ssrc} msid:(.+)`);\n\n if (msidMatch) {\n msid = msidMatch[1];\n } // find where the ssrc lines end\n\n\n const isSSRCLine = matchSSRCLines.test(sdpLines[i]);\n const isSSRCGroup = matchSSRCGroup.test(sdpLines[i]);\n\n if (ssrcStartIndex !== undefined) {\n if (ssrcEndIndex === undefined && !isSSRCLine && !isSSRCGroup) {\n // If we were inside an SSRC block and we've now reached a non-SSRC\n // line, then tag the previous line as the end of the SSRC block\n ssrcEndIndex = i - 1;\n } else if (i === sdpLines.length - 1) {\n // If we were inside an SSRC block and we've now reached the end of the\n // offer, tag the last line as the end of the SSRC block\n ssrcEndIndex = i;\n }\n }\n\n const midMatch = sdpLines[i].match(/a=mid:(.+)/);\n\n if (midMatch) {\n mid = midMatch[1];\n midIndex = i;\n }\n\n regResult = matchAttrLine.exec(sdpLines[i]);\n\n if (regResult && regResult.length === 3) {\n attrs.push({\n lineIndex: i,\n name: regResult[1],\n value: regResult[2]\n });\n }\n } // / The next section decorates the attributes array\n // / with some useful helpers.\n // Store references to the start and end indices\n // of the media section for this mediaType\n\n\n attrs.ssrcStartIndex = ssrcStartIndex;\n attrs.ssrcEndIndex = ssrcEndIndex;\n attrs.msid = msid;\n attrs.mid = mid;\n attrs.midIndex = midIndex;\n attrs.isUsingRTX = isUsingRTX.bind(null, sdpLines, attrs);\n\n attrs.filterByName = function filterByName(name) {\n return this.filter(attr => attr.name === name);\n };\n\n attrs.getRtpNumber = mediaEncoding => {\n const namePattern = new RegExp('rtpmap:(.+)');\n return find(attrs.map(attr => {\n const nameMatch = attr.name.match(namePattern);\n\n if (nameMatch && attr.value.indexOf(mediaEncoding) >= 0) {\n return nameMatch[1];\n }\n\n return null;\n }), attr => attr !== null);\n };\n\n return attrs;\n};\n\nconst modifyStereo = (type, sdp, enable) => {\n const sdpLines = sdp.split('\\r\\n');\n\n if (!SDPHelpers.getMLine(sdpLines, 'audio')) {\n logging.debug('No audio m-line, not enabling stereo.');\n return sdp;\n }\n\n const audioAttrs = SDPHelpers.getAttributesForMediaType(sdpLines, 'audio');\n const rtpNumber = audioAttrs.getRtpNumber('opus');\n\n if (!rtpNumber) {\n logging.debug('Could not find rtp number for opus, not enabling stereo.');\n return sdp;\n }\n\n const fmtpAttr = audioAttrs.find(attr => attr.name === `fmtp:${rtpNumber}`);\n\n if (!fmtpAttr) {\n logging.debug('Could not find a=fmtp line for opus, not enabling stereo.');\n return sdp;\n }\n\n let line = sdpLines[fmtpAttr.lineIndex];\n let pattern;\n\n switch (type) {\n case 'send':\n pattern = /sprop-stereo=\\d+([\\s;]*)/;\n\n if (pattern.test(fmtpAttr.value)) {\n line = line.replace(pattern, enable ? 'sprop-stereo=1$1' : '');\n } else if (enable) {\n line += '; sprop-stereo=1';\n }\n\n break;\n\n case 'receive':\n pattern = /([^-])stereo=\\d+([\\s;]*)/;\n\n if (pattern.test(fmtpAttr.value)) {\n line = line.replace(pattern, enable ? '$1stereo=1$2' : '$1');\n } else if (enable) {\n line += '; stereo=1';\n }\n\n break;\n\n default:\n throw new Error(`Invalid type ${type} passed into enableStereo`);\n } // Trim any trailing whitespace and semicolons\n\n\n line = line.replace(/[;\\s]*$/, '');\n sdpLines[fmtpAttr.lineIndex] = line;\n return sdpLines.join('\\r\\n');\n};\n\nSDPHelpers.modifySendStereo = modifyStereo.bind(null, 'send');\nSDPHelpers.modifyReceiveStereo = modifyStereo.bind(null, 'receive');\n\nSDPHelpers.setAudioBitrate = (sdp, audioBitrate) => {\n const existingValue = SDPHelpers.getAudioBitrate(sdp);\n\n if (existingValue !== undefined) {\n logging.debug(`Audio bitrate already set to ${existingValue}, not setting audio bitrate`);\n return sdp;\n }\n\n const sdpLines = sdp.split('\\r\\n');\n\n if (!SDPHelpers.getMLine(sdpLines, 'audio')) {\n logging.debug('No audio m-line, not setting audio bitrate.');\n return sdp;\n }\n\n const audioAttrs = SDPHelpers.getAttributesForMediaType(sdpLines, 'audio');\n\n if (!audioAttrs.midIndex) {\n logging.debug('No audio mid line, not setting audio bitrate.');\n return sdp;\n } // SDP expects audio bitrate in kbit/s\n\n\n const audioBitrateKbps = Math.floor(audioBitrate / 1000);\n sdpLines.splice(audioAttrs.midIndex + 1, 0, `b=AS:${audioBitrateKbps}`);\n return sdpLines.join('\\r\\n');\n};\n\nSDPHelpers.hasSendStereo = sdp => /[\\s;]sprop-stereo=1/.test(sdp);\n\nSDPHelpers.getAudioBitrate = sdp => {\n const result = sdp.match(/\\sb=AS:(\\d+)/);\n\n if (result) {\n return Number(result[1]) * 1000;\n }\n\n return undefined;\n}; // Safety limit of unique media SSRCs allowed in an SDP offer\n\n\nSDPHelpers.MAX_SSRCS = 10;\n\nSDPHelpers.getSSRCGroupType = videoAttrs => {\n const TYPES = ['SIM', 'FID']; // Note: this returns `undefined` if SDP has neither type in `TYPES`\n\n return TYPES.find(type => {\n const _videoAttrs$filterByN = videoAttrs.filterByName(`ssrc-group:${type}`),\n exists = _videoAttrs$filterByN[0];\n\n return exists;\n });\n};\n\nSDPHelpers.getSSRCGroup = videoAttrs => {\n const ssrcGroupType = SDPHelpers.getSSRCGroupType(videoAttrs); // The first group takes precedence\n\n const _videoAttrs$filterByN2 = videoAttrs.filterByName(`ssrc-group:${ssrcGroupType}`),\n ssrcGroup = _videoAttrs$filterByN2[0];\n\n return ssrcGroup;\n};\n\nSDPHelpers.getSSRCGroupSSRCs = videoAttrs => {\n const ssrcGroup = SDPHelpers.getSSRCGroup(videoAttrs); // If no group type was found, then there's nothing to filter\n\n if (!ssrcGroup) {\n return [];\n }\n\n return ssrcGroup.value.split(' ').slice(0, SDPHelpers.MAX_SSRCS);\n};\n\nSDPHelpers.getAllowedSSRCs = (videoAttrs, ssrcGroupSSRCs) => {\n // All SSRCs in the ssrc-group are allowed\n const allowedSSRCs = ssrcGroupSSRCs.slice(); // We need to allow the SSRCs that belong to the same FID group\n\n videoAttrs.filterByName('ssrc-group:FID').forEach(fid => {\n const ssrcs = fid.value.split(' ');\n const isAllowed = intersection(allowedSSRCs, ssrcs).length;\n\n if (isAllowed) {\n Array.prototype.push.apply(allowedSSRCs, ssrcs);\n }\n });\n return uniq(allowedSSRCs);\n};\n\nSDPHelpers.filterExcessSSRCs = sdp => {\n const sdpLines = sdp.split('\\r\\n');\n const videoAttrs = SDPHelpers.getAttributesForMediaType(sdpLines, 'video');\n const ssrcGroupType = SDPHelpers.getSSRCGroupType(videoAttrs);\n const ssrcGroupIndex = videoAttrs.ssrcStartIndex; // This is only possible in an audio-only session\n\n if (ssrcGroupIndex === undefined) {\n return sdp;\n }\n\n let allowedSSRCs; // Returns the SSRC ID from the SDP line if it exists, otherwise returns null\n\n const getSSRCid = line => {\n let ssrc = null;\n\n if (line.match(/^a=ssrc:(\\d+)/)) {\n ssrc = RegExp.$1;\n }\n\n return ssrc;\n };\n\n if (ssrcGroupType) {\n // Update ssrc-group with allowed SSRCs\n const ssrcGroupSSRCs = SDPHelpers.getSSRCGroupSSRCs(videoAttrs);\n const ssrcGroupLine = [`a=ssrc-group:${ssrcGroupType}`, ...ssrcGroupSSRCs].join(' ');\n const indexToReplace = videoAttrs.find(attr => attr.name.startsWith('ssrc-group:')).lineIndex;\n sdpLines[indexToReplace] = ssrcGroupLine;\n allowedSSRCs = SDPHelpers.getAllowedSSRCs(videoAttrs, ssrcGroupSSRCs);\n } else {\n // No ssrc-group was defined. No SSRCs are allowed, so all isolated\n // (non-audio) SSRCs will be removed.\n allowedSSRCs = []; // We want to allow through a single, isolated, video ssrc which may occur in P2P sessions\n\n const hasIsolatedSSRCs = videoAttrs.ssrcStartIndex;\n\n if (hasIsolatedSSRCs) {\n // Parse the ssrc ID\n allowedSSRCs.push(getSSRCid(sdpLines[videoAttrs.ssrcStartIndex]));\n }\n } // Cleans up the SDP, by removing all SSRC-related lines that aren't allowed\n\n\n const filteredSdpLines = sdpLines.filter((line, index) => {\n // Passthrough lines outside the SSRC block\n if (index < ssrcGroupIndex || index > videoAttrs.ssrcEndIndex) {\n return true;\n }\n\n if (index === ssrcGroupIndex) {\n if (!ssrcGroupType) {\n const ssrcID = getSSRCid(line);\n\n if (ssrcID) {\n return allowedSSRCs.includes(ssrcID);\n } // Filter out isolated SSRCs, since no ssrc-group was found\n\n\n return !/^a=ssrc:(\\d+)/.test(line);\n }\n\n return true;\n } // Make sure SSRC is allowed\n\n\n const ssrcID = getSSRCid(line);\n\n if (ssrcID) {\n return allowedSSRCs.includes(ssrcID);\n } // Make sure SSRCs in FID are allowed\n\n\n if (line.match(/^a=ssrc-group:FID (\\d+)/)) {\n const ssrc = RegExp.$1;\n return allowedSSRCs.includes(ssrc);\n } // Avoid adding more than one sim group\n\n\n return !line.match(/^a=ssrc-group:SIM /);\n });\n return filteredSdpLines.join('\\r\\n');\n}; // Modifies +sdp+ to enable Simulcast for +numberOfStreams+.\n//\n// Ok, here's the plan:\n// - add the 'a=ssrc-group:SIM' line, it will have numberOfStreams ssrcs\n// - if RTX then add one 'a=ssrc-group:FID', we need to add numberOfStreams lines\n// - add numberOfStreams 'a=ssrc:...' lines for the media ssrc\n// - if RTX then add numberOfStreams 'a=ssrc:...' lines for the RTX ssrc\n//\n// Re: media and rtx ssrcs:\n// We just generate these. The Mantis folk would like us to use sequential numbers\n// here for ease of debugging. We can use the same starting number each time as well.\n// We should confirm with Oscar/Jose that whether we need to verify that the numbers\n// that we choose don't clash with any other ones in the SDP.\n//\n// I think we do need to check but I can't remember.\n//\n// Re: The format of the 'a=ssrc:' lines\n// Just use the following pattern:\n// a=ssrc: cname:localCname\n// a=ssrc: msid:\n//\n// It doesn't matter that they are all the same and are static.\n//\n//\n\n\nSDPHelpers.enableSimulcast = function enableSimulcast(sdp, numberOfStreams) {\n let linesToAdd, i;\n const sdpLines = sdp.split('\\r\\n');\n\n if (!SDPHelpers.getMLine(sdpLines, 'video')) {\n logging.debug('No video m-line, not enabling simulcast.');\n return sdp;\n }\n\n const videoAttrs = SDPHelpers.getAttributesForMediaType(sdpLines, 'video');\n\n if (videoAttrs.filterByName('ssrc-group:SIM').length > 0) {\n logging.debug('Simulcast is already enabled in this SDP, not attempting to enable again.'); // We clean the SDP in case it somehow has SSRCs unrelated to the ssrc-group\n\n return SDPHelpers.filterExcessSSRCs(sdp);\n }\n\n if (!videoAttrs.msid) {\n logging.debug('No local stream attached, not enabling simulcast.');\n return sdp;\n }\n\n const usingRTX = videoAttrs.isUsingRTX();\n const mediaSSRC = [];\n const rtxSSRC = []; // generate new media (and rtx if needed) ssrcs\n\n for (i = 0; i < numberOfStreams; ++i) {\n mediaSSRC.push(START_MEDIA_SSRC + i);\n\n if (usingRTX) {\n rtxSSRC.push(START_RTX_SSRC + i);\n }\n }\n\n linesToAdd = [`a=ssrc-group:SIM ${mediaSSRC.join(' ')}`];\n\n if (usingRTX) {\n for (i = 0; i < numberOfStreams; ++i) {\n linesToAdd.push(`a=ssrc-group:FID ${mediaSSRC[i]} ${rtxSSRC[i]}`);\n }\n }\n\n for (i = 0; i < numberOfStreams; ++i) {\n linesToAdd.push(`a=ssrc:${mediaSSRC[i]} cname:localCname`, `a=ssrc:${mediaSSRC[i]} msid:${videoAttrs.msid}`);\n }\n\n if (usingRTX) {\n for (i = 0; i < numberOfStreams; ++i) {\n linesToAdd.push(`a=ssrc:${rtxSSRC[i]} cname:localCname`, `a=ssrc:${rtxSSRC[i]} msid:${videoAttrs.msid}`);\n }\n } // Replace the previous video ssrc section with our new video ssrc section by\n // deleting the old ssrcs section and inserting the new lines\n\n\n linesToAdd.unshift(videoAttrs.ssrcStartIndex, videoAttrs.ssrcEndIndex - videoAttrs.ssrcStartIndex + 1);\n sdpLines.splice(...linesToAdd);\n return sdpLines.join('\\r\\n');\n};\n\nSDPHelpers.reprioritizeVideoCodec = function reprioritizeVideoCodec(sdp, codec, location) {\n const lines = sdp.split('\\r\\n');\n const mLineIndex = SDPHelpers.getMLineIndex(lines, 'video');\n\n if (mLineIndex === -1) {\n return sdp;\n }\n\n const payloadTypes = SDPHelpers.getMLinePayloadTypes(lines[mLineIndex], 'video');\n const regex = new RegExp(`^a=rtpmap:(\\\\d+).* ${codec}`, 'i');\n const codecMatches = lines.map(line => line.match(regex)).filter(match => match !== null);\n\n if (codecMatches.length === 0) {\n return sdp;\n }\n\n const codecTypeCodes = codecMatches.map(match => match[1]);\n let newPayloadTypes = payloadTypes.filter(t => codecTypeCodes.indexOf(t) === -1);\n\n if (location === 'top') {\n newPayloadTypes.unshift(...codecTypeCodes);\n } else if (location === 'bottom') {\n newPayloadTypes.push(...codecTypeCodes);\n } else {\n logging.error(`Unexpected location param: ${location}; not changing ${codec} priority`);\n newPayloadTypes = payloadTypes;\n }\n\n const newMLine = lines[mLineIndex].replace(payloadTypes.join(' '), newPayloadTypes.join(' '));\n lines[mLineIndex] = newMLine;\n return lines.join('\\r\\n');\n};\n\n/***/ }),\n/* 50 */\n/***/ (function(module, exports) {\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n return value === other || (value !== value && other !== other);\n}\n\nmodule.exports = eq;\n\n\n/***/ }),\n/* 51 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayLikeKeys = __webpack_require__(175),\n baseKeysIn = __webpack_require__(371),\n isArrayLike = __webpack_require__(22);\n\n/**\n * Creates an array of the own and inherited enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keysIn(new Foo);\n * // => ['a', 'b', 'c'] (iteration order is not guaranteed)\n */\nfunction keysIn(object) {\n return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);\n}\n\nmodule.exports = keysIn;\n\n\n/***/ }),\n/* 52 */\n/***/ (function(module, exports) {\n\n/**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\nfunction copyArray(source, array) {\n var index = -1,\n length = source.length;\n\n array || (array = Array(length));\n while (++index < length) {\n array[index] = source[index];\n }\n return array;\n}\n\nmodule.exports = copyArray;\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// ES3 safe\nvar _undefined = void 0;\n\nmodule.exports = function (value) { return value !== _undefined && value !== null; };\n\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseClone = __webpack_require__(77);\n\n/** Used to compose bitmasks for cloning. */\nvar CLONE_DEEP_FLAG = 1,\n CLONE_SYMBOLS_FLAG = 4;\n\n/**\n * This method is like `_.clone` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @returns {*} Returns the deep cloned value.\n * @see _.clone\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var deep = _.cloneDeep(objects);\n * console.log(deep[0] === objects[0]);\n * // => false\n */\nfunction cloneDeep(value) {\n return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);\n}\n\nmodule.exports = cloneDeep;\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseGet = __webpack_require__(98);\n\n/**\n * Gets the value at `path` of `object`. If the resolved value is\n * `undefined`, the `defaultValue` is returned in its place.\n *\n * @static\n * @memberOf _\n * @since 3.7.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n * @returns {*} Returns the resolved value.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.get(object, 'a[0].b.c');\n * // => 3\n *\n * _.get(object, ['a', '0', 'b', 'c']);\n * // => 3\n *\n * _.get(object, 'a.b.c', 'default');\n * // => 'default'\n */\nfunction get(object, path, defaultValue) {\n var result = object == null ? undefined : baseGet(object, path);\n return result === undefined ? defaultValue : result;\n}\n\nmodule.exports = get;\n\n\n/***/ }),\n/* 56 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isArray = __webpack_require__(10),\n isKey = __webpack_require__(136),\n stringToPath = __webpack_require__(233),\n toString = __webpack_require__(57);\n\n/**\n * Casts `value` to a path array if it's not one.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {Object} [object] The object to query keys on.\n * @returns {Array} Returns the cast property path array.\n */\nfunction castPath(value, object) {\n if (isArray(value)) {\n return value;\n }\n return isKey(value, object) ? [value] : stringToPath(toString(value));\n}\n\nmodule.exports = castPath;\n\n\n/***/ }),\n/* 57 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseToString = __webpack_require__(500);\n\n/**\n * Converts `value` to a string. An empty string is returned for `null`\n * and `undefined` values. The sign of `-0` is preserved.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n * @example\n *\n * _.toString(null);\n * // => ''\n *\n * _.toString(-0);\n * // => '-0'\n *\n * _.toString([1, 2, 3]);\n * // => '1,2,3'\n */\nfunction toString(value) {\n return value == null ? '' : baseToString(value);\n}\n\nmodule.exports = toString;\n\n\n/***/ }),\n/* 58 */\n/***/ (function(module, exports) {\n\nfunction _inheritsLoose(subClass, superClass) {\n subClass.prototype = Object.create(superClass.prototype);\n subClass.prototype.constructor = subClass;\n subClass.__proto__ = superClass;\n}\n\nmodule.exports = _inheritsLoose;\n\n/***/ }),\n/* 59 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/* eslint-disable global-require */\nconst once = __webpack_require__(48);\n\nconst StaticConfig = __webpack_require__(42)(); // Indicates whether this client supports WebRTC\n//\n// This is defined as: getUserMedia + PeerConnection + exceeds min browser version\n//\n\n\nexports.check = function (deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const env = deps.env || __webpack_require__(2);\n\n const hasGetUserMediaCapability = deps.hasGetUserMediaCapability || __webpack_require__(575);\n\n const hasPeerConnectionCapability = deps.hasPeerConnectionCapability || __webpack_require__(576);\n\n const logging = deps.logging || __webpack_require__(0)('hasOpenTokSupport');\n /** @type StaticConfig */\n\n\n const staticConfig = deps.staticConfig || StaticConfig.onlyLocal();\n const minimumVersions = staticConfig.minimumVersion || {};\n const minimumVersion = minimumVersions[env.name.toLowerCase()];\n\n if (minimumVersion && minimumVersion > env.version) {\n logging.debug('Support for', env.name, 'is disabled because we require', minimumVersion, 'but this is', env.version);\n return false;\n }\n\n if (env.name === 'Node') {\n // Node works, even though it doesn't have getUserMedia\n return true;\n }\n\n return hasGetUserMediaCapability() && hasPeerConnectionCapability();\n};\n\nexports.once = once(() => exports.check());\n\n/***/ }),\n/* 60 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar createFind = __webpack_require__(589),\n findIndex = __webpack_require__(270);\n\n/**\n * Iterates over elements of `collection`, returning the first element\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false },\n * { 'user': 'pebbles', 'age': 1, 'active': true }\n * ];\n *\n * _.find(users, function(o) { return o.age < 40; });\n * // => object for 'barney'\n *\n * // The `_.matches` iteratee shorthand.\n * _.find(users, { 'age': 1, 'active': true });\n * // => object for 'pebbles'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.find(users, ['active', false]);\n * // => object for 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.find(users, 'active');\n * // => object for 'barney'\n */\nvar find = createFind(findIndex);\n\nmodule.exports = find;\n\n\n/***/ }),\n/* 61 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function eventHelper(object) {\n const eventHandlers = [];\n return {\n on(eventName, handler) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n\n eventHandlers.push({\n eventName,\n handler,\n args\n });\n\n if (object.on) {\n object.on(eventName, handler);\n } else if (object.addEventListener) {\n object.addEventListener(eventName, handler, ...args);\n } else {\n throw new Error('Object does not support events', object);\n }\n },\n\n off(eventName, handler) {\n if (object.off) {\n object.off(eventName, handler);\n } else if (object.removeEventListener) {\n for (var _len2 = arguments.length, args = new Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {\n args[_key2 - 2] = arguments[_key2];\n }\n\n object.removeEventListener(eventName, handler, ...args);\n } else {\n throw new Error('Object does not support events', object);\n }\n },\n\n once(eventName, handler) {\n for (var _len3 = arguments.length, args = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {\n args[_key3 - 2] = arguments[_key3];\n }\n\n eventHandlers.push({\n eventName,\n handler,\n args\n });\n object.once(eventName, handler);\n },\n\n removeAll() {\n eventHandlers.forEach((_ref) => {\n let eventName = _ref.eventName,\n handler = _ref.handler,\n args = _ref.args;\n this.off(eventName, handler, ...args);\n });\n eventHandlers.splice(0, eventHandlers.length);\n }\n\n };\n};\n\n/***/ }),\n/* 62 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar defineProperty = __webpack_require__(173);\n\n/**\n * The base implementation of `assignValue` and `assignMergeValue` without\n * value checks.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction baseAssignValue(object, key, value) {\n if (key == '__proto__' && defineProperty) {\n defineProperty(object, key, {\n 'configurable': true,\n 'enumerable': true,\n 'value': value,\n 'writable': true\n });\n } else {\n object[key] = value;\n }\n}\n\nmodule.exports = baseAssignValue;\n\n\n/***/ }),\n/* 63 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseIsArguments = __webpack_require__(366),\n isObjectLike = __webpack_require__(14);\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/** Built-in value references. */\nvar propertyIsEnumerable = objectProto.propertyIsEnumerable;\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nvar isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {\n return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&\n !propertyIsEnumerable.call(value, 'callee');\n};\n\nmodule.exports = isArguments;\n\n\n/***/ }),\n/* 64 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(12),\n stubFalse = __webpack_require__(367);\n\n/** Detect free variable `exports`. */\nvar freeExports = true && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;\n\n/**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\nvar isBuffer = nativeIsBuffer || stubFalse;\n\nmodule.exports = isBuffer;\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(84)(module)))\n\n/***/ }),\n/* 65 */\n/***/ (function(module, exports) {\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n var type = typeof value;\n length = length == null ? MAX_SAFE_INTEGER : length;\n\n return !!length &&\n (type == 'number' ||\n (type != 'symbol' && reIsUint.test(value))) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\nmodule.exports = isIndex;\n\n\n/***/ }),\n/* 66 */\n/***/ (function(module, exports) {\n\n/**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\nfunction baseUnary(func) {\n return function(value) {\n return func(value);\n };\n}\n\nmodule.exports = baseUnary;\n\n\n/***/ }),\n/* 67 */\n/***/ (function(module, exports) {\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n}\n\nmodule.exports = isPrototype;\n\n\n/***/ }),\n/* 68 */\n/***/ (function(module, exports) {\n\n// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n\n\n/***/ }),\n/* 69 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function (fn) {\n\tif (typeof fn !== \"function\") throw new TypeError(fn + \" is not a function\");\n\treturn fn;\n};\n\n\n/***/ }),\n/* 70 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseGetTag = __webpack_require__(27),\n isObjectLike = __webpack_require__(14);\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && baseGetTag(value) == symbolTag);\n}\n\nmodule.exports = isSymbol;\n\n\n/***/ }),\n/* 71 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst OTHelpers = __webpack_require__(4);\n\nif (OTHelpers.env.name === 'Node') {\n module.exports = {\n value: ''\n };\n} else {\n var _Array$prototype$slic, _Array$prototype$slic2;\n\n // Script embed\n const scriptSrc = (_Array$prototype$slic = Array.prototype.slice.call(document.getElementsByTagName('script'))) == null ? void 0 : (_Array$prototype$slic2 = _Array$prototype$slic.pop()) == null ? void 0 : _Array$prototype$slic2.getAttribute('src');\n\n const _ref = scriptSrc && scriptSrc.match(/[?&]apikey=([^&]+)/i) || [],\n apiKey = _ref[1]; // TODO: The indirection here is due to the need to set APIKEY in testing. We should find a better\n // solution.\n\n\n module.exports = {\n value: apiKey || ''\n };\n}\n\n/***/ }),\n/* 72 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2);\n\nconst enforcesUnifiedPlan = () => // Chromium-based browsers version M90 logs a warning that Plan B is being removed\n(env.isChrome || env.isElectron) && env.version >= 90 || env.isOpera && env.version >= 76;\n\nmodule.exports = () => !enforcesUnifiedPlan() && ( // For legacy browsers, we still enforce Plan B\nenv.isChrome && env.version > 71 || env.isChromiumEdge && env.version < 90 || env.isOpera && env.version > 58);\n\n/***/ }),\n/* 73 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nlet userAgent = 'Node';\n\nif (typeof navigator !== 'undefined') {\n userAgent = navigator.userAgent;\n}\n\nmodule.exports = function isiOSFactory(deps) {\n if (deps === void 0) {\n deps = {\n userAgent\n };\n }\n\n return /iPhone|iPad/.test(deps.userAgent) // This line is for detecting iPad on iOS 13+. See https://developer.apple.com/forums/thread/119186\n || userAgent.includes('Mac') && 'ontouchend' in document;\n};\n\n/***/ }),\n/* 74 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/*\n * Executes the provided callback thanks to global.setTimeout
.\n *\n * @param {function()} callback\n * @param {number} frequency how many times per second we want to execute the callback\n * @param {number} maxIterations the maximum amount of iterations for this runner\n * @constructor\n */\nfunction IntervalRunner(callback, desiredFrequency, maxIterations) {\n if (maxIterations === void 0) {\n maxIterations = Infinity;\n }\n\n let timeoutId = null;\n let running = false;\n let iteration = 1;\n const timeBetween = 1000 / desiredFrequency;\n\n const loop = () => {\n if (!running || iteration > maxIterations) {\n return;\n }\n\n iteration += 1;\n callback();\n timeoutId = setTimeout(loop, timeBetween);\n };\n\n this.start = () => {\n if (running) {\n return;\n }\n\n running = true;\n setTimeout(loop, timeBetween);\n };\n\n this.stop = () => {\n (typeof window !== undefined ? window : global).clearTimeout(timeoutId);\n running = false;\n };\n}\n\nmodule.exports = IntervalRunner;\n\n/***/ }),\n/* 75 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst pick = __webpack_require__(45);\n\nconst mapValues = __webpack_require__(632);\n\nconst isObject = __webpack_require__(8);\n\nconst frameRateTrackers = {};\n\nfunction getFrameRate(stat) {\n return Number(stat.framerateMean || stat.googFrameRateSent || stat.googFrameRateReceived || stat.googFrameRateInput || stat.googFrameRateOutput || 0);\n}\n\nfunction getFrames(stat) {\n return Number(stat.framesEncoded || stat.framesDecoded);\n}\n\nfunction calcFrameRate(stat, startTime) {\n if (getFrameRate(stat)) {\n return getFrameRate(stat);\n }\n\n if (!getFrames(stat)) {\n return undefined;\n }\n\n let frameRate = 0;\n\n if (frameRateTrackers[stat.id] !== undefined) {\n frameRate = (getFrames(stat) - frameRateTrackers[stat.id].frames) / ((stat.timestamp - frameRateTrackers[stat.id].timestamp) / 1000);\n } else {\n frameRate = getFrames(stat) / ((stat.timestamp - startTime) / 1000);\n }\n\n frameRateTrackers[stat.id] = {\n frames: getFrames(stat),\n timestamp: stat.timestamp\n };\n return Math.round(frameRate * 100) / 100;\n}\n\nfunction getLinkedTrack(stat, allStats) {\n if (!allStats) {\n return {};\n }\n\n const linkedTrack = allStats.filter(x => x.id === stat.mediaTrackId);\n return linkedTrack.length ? linkedTrack[0] : {};\n}\n\nfunction totalPacketsLostReducer(totalPacketsLost, stat) {\n if ('packetsLost' in stat) {\n return totalPacketsLost + stat.packetsLost;\n }\n\n return totalPacketsLost;\n}\n\nconst getStatsHelpers = {\n isVideoStat(stat, allStats) {\n const linkedTrack = getLinkedTrack(stat, allStats); // When kind is 'video', we need to check the type since there are other\n // stats with kind 'video' without the information we need.\n\n const isVideoKind = stat.kind === 'video' && ['outbound-rtp', 'inbound-rtp'].includes(stat.type);\n return isVideoKind || stat.mediaType === 'video' || 'googFrameWidthReceived' in stat || // Old Chrome\n 'googFrameWidthInput' in stat || // Old Chrome\n stat.type === 'inbound-rtp' && stat.id.indexOf('Video') !== -1 || // Safari\n stat.type === 'inboundrtp' && linkedTrack.frameWidth !== 0; // Edge\n },\n\n isAudioStat(stat, allStats) {\n const linkedTrack = getLinkedTrack(stat, allStats); // When kind is 'audio', we need to check the type since there are other\n // stats with kind 'audio' without the information we need.\n\n const isAudioKind = stat.kind === 'audio' && ['outbound-rtp', 'inbound-rtp'].includes(stat.type);\n return isAudioKind || stat.mediaType === 'audio' || 'audioInputLevel' in stat || // Old Chrome\n 'audioOutputLevel' in stat || // Old Chrome\n stat.type === 'inbound-rtp' && stat.id.indexOf('Audio') !== -1 || // Safari\n stat.type === 'inboundrtp' && linkedTrack.frameWidth === 0; // Edge;\n },\n\n isInboundStat(stat) {\n return 'bytesReceived' in stat || 'packetsReceived' in stat;\n },\n\n isOutboundStat(stat) {\n return 'bytesSent' in stat || 'packetsSent' in stat;\n },\n\n isVideoTrackStat(stat) {\n 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);\n },\n\n isVideoRemoteInboundRtpStat(stat) {\n return stat.type === 'remote-inbound-rtp' && stat.kind === 'video';\n },\n\n isAudioRemoteInboundRtpStat(stat) {\n return stat.type === 'remote-inbound-rtp' && stat.kind === 'audio';\n },\n\n parseStatCategory(stat) {\n const statCategory = {\n packetsLost: 0,\n packetsReceived: 0,\n bytesReceived: 0 // frameRate is only set for video stat\n\n };\n\n if ('packetsReceived' in stat) {\n statCategory.packetsReceived = parseInt(stat.packetsReceived, 10);\n }\n\n if ('packetsLost' in stat) {\n statCategory.packetsLost = parseInt(stat.packetsLost, 10);\n }\n\n if ('bytesReceived' in stat) {\n statCategory.bytesReceived = parseInt(stat.bytesReceived, 10);\n }\n\n if (this.isVideoStat(stat)) {\n if ('framerateMean' in stat) {\n statCategory.frameRate = Number(stat.framerateMean);\n } else if ('googFrameRateOutput' in stat) {\n statCategory.frameRate = Number(stat.googFrameRateOutput);\n } else if ('googFrameRateInput' in stat) {\n statCategory.frameRate = Number(stat.googFrameRateInput);\n }\n }\n\n return statCategory;\n },\n\n getVideoPacketsLost(stats) {\n return stats.filter(stat => this.isVideoRemoteInboundRtpStat(stat, stats)).reduce(totalPacketsLostReducer, 0);\n },\n\n getAudioPacketsLost(stats) {\n return stats.filter(stat => this.isAudioRemoteInboundRtpStat(stat, stats)).reduce(totalPacketsLostReducer, 0);\n },\n\n normalizeTimestamp(timestamp, now) {\n if (now === void 0) {\n now = Date.now();\n }\n\n if (isObject(timestamp) && 'getTime' in timestamp) {\n // Chrome as of 39 delivers a \"kind of Date\" object for timestamps\n // we duck check it and get the timestamp\n return timestamp.getTime();\n }\n\n if (Math.abs(timestamp / (1000 * now) - 1) < 0.05) {\n // If the timestamp is within 5% of 1000x now, we assume its unit is microseconds and\n // divide by 1000 to correct for this.\n return timestamp / 1000;\n }\n\n return timestamp;\n },\n\n normalizeStats(stats, inbound, startTime) {\n if (inbound === void 0) {\n inbound = true;\n }\n\n const videos = stats.filter(stat => getStatsHelpers.isVideoStat(stat, stats)).filter(stat => getStatsHelpers.isInboundStat(stat) === inbound);\n const audio = stats.filter(stat => this.isAudioStat(stat, stats)).filter(stat => this.isInboundStat(stat) === inbound)[0]; // There may be more than one video stream (e.g., when simulcast is enabled)\n // We just need to grab one of them for its id and timestamp\n\n const video = videos[0];\n\n if (!audio && !video) {\n throw new Error('could not find stats for either audio or video');\n }\n\n const timestamp = getStatsHelpers.normalizeTimestamp(video ? video.timestamp : audio.timestamp);\n const otStats = {\n timestamp\n };\n\n if (video && !inbound && video.packetsLost === undefined) {\n video.packetsLost = this.getVideoPacketsLost(stats);\n }\n\n if (audio && !inbound && audio.packetsLost === undefined) {\n audio.packetsLost = this.getAudioPacketsLost(stats);\n }\n\n const outboundStatNames = ['packetsSent', 'packetsLost', 'bytesSent'];\n\n const getOutboundStats = stat => pick(stat, outboundStatNames);\n\n const getInboundStats = stat => pick(stat, ['packetsReceived', 'packetsLost', 'bytesReceived']);\n\n const getCommonStats = inbound ? getInboundStats : getOutboundStats;\n\n const accumulateVideoStats = videoArray => {\n // We want to accumulate all these stats, if they exist.\n const frameStats = ['framerateMean', 'googFrameRateSent', 'googFrameRateReceived', 'googFrameRateInput', 'googFrameRateOutput', 'framesEncoded', 'framesDecoded'];\n const outboundStatNamesWithFrames = outboundStatNames.concat(frameStats);\n const accumulatedVideo = {\n timestamp,\n id: video.id\n };\n outboundStatNamesWithFrames.forEach(statName => {\n accumulatedVideo[statName] = videoArray.map(x => Number(x[statName] || 0)).reduce((accumulator, stat) => accumulator + stat, 0);\n });\n return accumulatedVideo;\n };\n\n if (videos && videos.length > 0) {\n const accumulatedVideo = accumulateVideoStats(videos);\n otStats.video = Object.assign({\n frameRate: calcFrameRate(accumulatedVideo, startTime)\n }, mapValues(getCommonStats(accumulatedVideo), Number));\n }\n\n if (audio) {\n otStats.audio = mapValues(getCommonStats(audio), Number);\n }\n\n return otStats;\n }\n\n};\nmodule.exports = getStatsHelpers;\n\n/***/ }),\n/* 76 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\nexports.codes = exports.messages = void 0;\n// https://tools.ietf.org/html/rfc6455#section-7.4.1\n// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Close_codes\nconst messages = {\n 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.',\n 1002: 'The endpoint is terminating the connection due to a protocol error. ' + '(CLOSE_PROTOCOL_ERROR)',\n 1003: 'The connection is being terminated because the endpoint has indicated ' + 'that reconnections are not available. (CLOSE_UNSUPPORTED)',\n 1005: 'Indicates that no status code was provided even though one was expected. ' + '(CLOSE_NO_STATUS)',\n 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)',\n 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)',\n 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',\n 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)',\n 1011: 'Indicates that a server is terminating the connection because it encountered an ' + 'unexpected condition that prevented it from fulfilling the request',\n // ... codes in the 4000-4999 range are available for use by applications.\n 4001: 'Connectivity loss was detected as it was too long since the socket received the ' + 'last PONG message',\n 4010: 'Timed out while waiting for the Rumor socket to connect.',\n 4020: 'Error code unavailable.',\n 4030: 'Exception was thrown during Rumor connection, possibly because of a blocked port.'\n};\nexports.messages = messages;\nconst codes = {\n CLOSE_NORMAL: 1000,\n CLOSE_GOING_AWAY: 1001,\n CLOSE_PROTOCOL_ERROR: 1002,\n CLOSE_UNSUPPORTED: 1003,\n CLOSE_NO_STATUS: 1005,\n CLOSE_ABNORMAL: 1006,\n CLOSE_INCONSISTENT_DATA: 1007,\n CLOSE_POLICY_VIOLATION: 1008,\n CLOSE_TOO_LARGE: 1009,\n CLOSE_UNEXPECTED_CONDITION: 1011,\n CLOSE_CONNECTIVITY_LOSS: 4001,\n CLOSE_TIMEOUT: 4010,\n CLOSE_FALLBACK_CODE: 4020,\n CLOSE_CONNECT_EXCEPTION: 4030,\n ALREADY_CONNECTED_CONNECTING: null\n};\nexports.codes = codes;\n\n/***/ }),\n/* 77 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Stack = __webpack_require__(78),\n arrayEach = __webpack_require__(111),\n assignValue = __webpack_require__(83),\n baseAssign = __webpack_require__(174),\n baseAssignIn = __webpack_require__(370),\n cloneBuffer = __webpack_require__(177),\n copyArray = __webpack_require__(52),\n copySymbols = __webpack_require__(373),\n copySymbolsIn = __webpack_require__(375),\n getAllKeys = __webpack_require__(180),\n getAllKeysIn = __webpack_require__(182),\n getTag = __webpack_require__(38),\n initCloneArray = __webpack_require__(378),\n initCloneByTag = __webpack_require__(379),\n initCloneObject = __webpack_require__(187),\n isArray = __webpack_require__(10),\n isBuffer = __webpack_require__(64),\n isMap = __webpack_require__(383),\n isObject = __webpack_require__(8),\n isSet = __webpack_require__(385),\n keys = __webpack_require__(28),\n keysIn = __webpack_require__(51);\n\n/** Used to compose bitmasks for cloning. */\nvar CLONE_DEEP_FLAG = 1,\n CLONE_FLAT_FLAG = 2,\n CLONE_SYMBOLS_FLAG = 4;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n objectTag = '[object Object]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n/** Used to identify `toStringTag` values supported by `_.clone`. */\nvar cloneableTags = {};\ncloneableTags[argsTag] = cloneableTags[arrayTag] =\ncloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\ncloneableTags[boolTag] = cloneableTags[dateTag] =\ncloneableTags[float32Tag] = cloneableTags[float64Tag] =\ncloneableTags[int8Tag] = cloneableTags[int16Tag] =\ncloneableTags[int32Tag] = cloneableTags[mapTag] =\ncloneableTags[numberTag] = cloneableTags[objectTag] =\ncloneableTags[regexpTag] = cloneableTags[setTag] =\ncloneableTags[stringTag] = cloneableTags[symbolTag] =\ncloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\ncloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\ncloneableTags[errorTag] = cloneableTags[funcTag] =\ncloneableTags[weakMapTag] = false;\n\n/**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Deep clone\n * 2 - Flatten inherited properties\n * 4 - Clone symbols\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\nfunction baseClone(value, bitmask, customizer, key, object, stack) {\n var result,\n isDeep = bitmask & CLONE_DEEP_FLAG,\n isFlat = bitmask & CLONE_FLAT_FLAG,\n isFull = bitmask & CLONE_SYMBOLS_FLAG;\n\n if (customizer) {\n result = object ? customizer(value, key, object, stack) : customizer(value);\n }\n if (result !== undefined) {\n return result;\n }\n if (!isObject(value)) {\n return value;\n }\n var isArr = isArray(value);\n if (isArr) {\n result = initCloneArray(value);\n if (!isDeep) {\n return copyArray(value, result);\n }\n } else {\n var tag = getTag(value),\n isFunc = tag == funcTag || tag == genTag;\n\n if (isBuffer(value)) {\n return cloneBuffer(value, isDeep);\n }\n if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n result = (isFlat || isFunc) ? {} : initCloneObject(value);\n if (!isDeep) {\n return isFlat\n ? copySymbolsIn(value, baseAssignIn(result, value))\n : copySymbols(value, baseAssign(result, value));\n }\n } else {\n if (!cloneableTags[tag]) {\n return object ? value : {};\n }\n result = initCloneByTag(value, tag, isDeep);\n }\n }\n // Check for circular references and return its corresponding clone.\n stack || (stack = new Stack);\n var stacked = stack.get(value);\n if (stacked) {\n return stacked;\n }\n stack.set(value, result);\n\n if (isSet(value)) {\n value.forEach(function(subValue) {\n result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));\n });\n } else if (isMap(value)) {\n value.forEach(function(subValue, key) {\n result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n }\n\n var keysFunc = isFull\n ? (isFlat ? getAllKeysIn : getAllKeys)\n : (isFlat ? keysIn : keys);\n\n var props = isArr ? undefined : keysFunc(value);\n arrayEach(props || value, function(subValue, key) {\n if (props) {\n key = subValue;\n subValue = value[key];\n }\n // Recursively populate clone (susceptible to call stack limits).\n assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n return result;\n}\n\nmodule.exports = baseClone;\n\n\n/***/ }),\n/* 78 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar ListCache = __webpack_require__(79),\n stackClear = __webpack_require__(342),\n stackDelete = __webpack_require__(343),\n stackGet = __webpack_require__(344),\n stackHas = __webpack_require__(345),\n stackSet = __webpack_require__(346);\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n var data = this.__data__ = new ListCache(entries);\n this.size = data.size;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\nmodule.exports = Stack;\n\n\n/***/ }),\n/* 79 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar listCacheClear = __webpack_require__(337),\n listCacheDelete = __webpack_require__(338),\n listCacheGet = __webpack_require__(339),\n listCacheHas = __webpack_require__(340),\n listCacheSet = __webpack_require__(341);\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\nmodule.exports = ListCache;\n\n\n/***/ }),\n/* 80 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar eq = __webpack_require__(50);\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n}\n\nmodule.exports = assocIndexOf;\n\n\n/***/ }),\n/* 81 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar getNative = __webpack_require__(35);\n\n/* Built-in method references that are verified to be native. */\nvar nativeCreate = getNative(Object, 'create');\n\nmodule.exports = nativeCreate;\n\n\n/***/ }),\n/* 82 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isKeyable = __webpack_require__(361);\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n}\n\nmodule.exports = getMapData;\n\n\n/***/ }),\n/* 83 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseAssignValue = __webpack_require__(62),\n eq = __webpack_require__(50);\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction assignValue(object, key, value) {\n var objValue = object[key];\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n (value === undefined && !(key in object))) {\n baseAssignValue(object, key, value);\n }\n}\n\nmodule.exports = assignValue;\n\n\n/***/ }),\n/* 84 */\n/***/ (function(module, exports) {\n\nmodule.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\tmodule.deprecate = function() {};\n\t\tmodule.paths = [];\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\n\n\n/***/ }),\n/* 85 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseIsTypedArray = __webpack_require__(368),\n baseUnary = __webpack_require__(66),\n nodeUtil = __webpack_require__(113);\n\n/* Node.js helper references. */\nvar nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n/**\n * Checks if `value` is classified as a typed array.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n * @example\n *\n * _.isTypedArray(new Uint8Array);\n * // => true\n *\n * _.isTypedArray([]);\n * // => false\n */\nvar isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;\n\nmodule.exports = isTypedArray;\n\n\n/***/ }),\n/* 86 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isObject = __webpack_require__(8);\n\n/** Built-in value references. */\nvar objectCreate = Object.create;\n\n/**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} proto The object to inherit from.\n * @returns {Object} Returns the new object.\n */\nvar baseCreate = (function() {\n function object() {}\n return function(proto) {\n if (!isObject(proto)) {\n return {};\n }\n if (objectCreate) {\n return objectCreate(proto);\n }\n object.prototype = proto;\n var result = new object;\n object.prototype = undefined;\n return result;\n };\n}());\n\nmodule.exports = baseCreate;\n\n\n/***/ }),\n/* 87 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar identity = __webpack_require__(88),\n overRest = __webpack_require__(190),\n setToString = __webpack_require__(121);\n\n/**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\nfunction baseRest(func, start) {\n return setToString(overRest(func, start, identity), func + '');\n}\n\nmodule.exports = baseRest;\n\n\n/***/ }),\n/* 88 */\n/***/ (function(module, exports) {\n\n/**\n * This method returns the first argument it receives.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Util\n * @param {*} value Any value.\n * @returns {*} Returns `value`.\n * @example\n *\n * var object = { 'a': 1 };\n *\n * console.log(_.identity(object) === object);\n * // => true\n */\nfunction identity(value) {\n return value;\n}\n\nmodule.exports = identity;\n\n\n/***/ }),\n/* 89 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar objToString = Object.prototype.toString\n , id = objToString.call((function () { return arguments; })());\n\nmodule.exports = function (value) { return objToString.call(value) === id; };\n\n\n/***/ }),\n/* 90 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar objToString = Object.prototype.toString, id = objToString.call(\"\");\n\nmodule.exports = function (value) {\n\treturn (\n\t\ttypeof value === \"string\" ||\n\t\t(value &&\n\t\t\ttypeof value === \"object\" &&\n\t\t\t(value instanceof String || objToString.call(value) === id)) ||\n\t\tfalse\n\t);\n};\n\n\n/***/ }),\n/* 91 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = __webpack_require__(410)() ? globalThis : __webpack_require__(411);\n\n\n/***/ }),\n/* 92 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar MapCache = __webpack_require__(110),\n setCacheAdd = __webpack_require__(443),\n setCacheHas = __webpack_require__(444);\n\n/**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\nfunction SetCache(values) {\n var index = -1,\n length = values == null ? 0 : values.length;\n\n this.__data__ = new MapCache;\n while (++index < length) {\n this.add(values[index]);\n }\n}\n\n// Add methods to `SetCache`.\nSetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\nSetCache.prototype.has = setCacheHas;\n\nmodule.exports = SetCache;\n\n\n/***/ }),\n/* 93 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseIndexOf = __webpack_require__(445);\n\n/**\n * A specialized version of `_.includes` for arrays without support for\n * specifying an index to search from.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\nfunction arrayIncludes(array, value) {\n var length = array == null ? 0 : array.length;\n return !!length && baseIndexOf(array, value, 0) > -1;\n}\n\nmodule.exports = arrayIncludes;\n\n\n/***/ }),\n/* 94 */\n/***/ (function(module, exports) {\n\n/**\n * Checks if a `cache` value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction cacheHas(cache, key) {\n return cache.has(key);\n}\n\nmodule.exports = cacheHas;\n\n\n/***/ }),\n/* 95 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseCreate = __webpack_require__(86),\n isObject = __webpack_require__(8);\n\n/**\n * Creates a function that produces an instance of `Ctor` regardless of\n * whether it was invoked as part of a `new` expression or by `call` or `apply`.\n *\n * @private\n * @param {Function} Ctor The constructor to wrap.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createCtor(Ctor) {\n return function() {\n // Use a `switch` statement to work with class constructors. See\n // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n // for more details.\n var args = arguments;\n switch (args.length) {\n case 0: return new Ctor;\n case 1: return new Ctor(args[0]);\n case 2: return new Ctor(args[0], args[1]);\n case 3: return new Ctor(args[0], args[1], args[2]);\n case 4: return new Ctor(args[0], args[1], args[2], args[3]);\n case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);\n case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);\n case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);\n }\n var thisBinding = baseCreate(Ctor.prototype),\n result = Ctor.apply(thisBinding, args);\n\n // Mimic the constructor's `return` behavior.\n // See https://es5.github.io/#x13.2.2 for more details.\n return isObject(result) ? result : thisBinding;\n };\n}\n\nmodule.exports = createCtor;\n\n\n/***/ }),\n/* 96 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar toFinite = __webpack_require__(485);\n\n/**\n * Converts `value` to an integer.\n *\n * **Note:** This method is loosely based on\n * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toInteger(3.2);\n * // => 3\n *\n * _.toInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toInteger(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toInteger('3.2');\n * // => 3\n */\nfunction toInteger(value) {\n var result = toFinite(value),\n remainder = result % 1;\n\n return result === result ? (remainder ? result - remainder : result) : 0;\n}\n\nmodule.exports = toInteger;\n\n\n/***/ }),\n/* 97 */\n/***/ (function(module, exports) {\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = value;\n });\n return result;\n}\n\nmodule.exports = setToArray;\n\n\n/***/ }),\n/* 98 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar castPath = __webpack_require__(56),\n toKey = __webpack_require__(44);\n\n/**\n * The base implementation of `_.get` without support for default values.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @returns {*} Returns the resolved value.\n */\nfunction baseGet(object, path) {\n path = castPath(path, object);\n\n var index = 0,\n length = path.length;\n\n while (object != null && index < length) {\n object = object[toKey(path[index++])];\n }\n return (index && index == length) ? object : undefined;\n}\n\nmodule.exports = baseGet;\n\n\n/***/ }),\n/* 99 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseFor = __webpack_require__(236),\n keys = __webpack_require__(28);\n\n/**\n * The base implementation of `_.forOwn` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\nfunction baseForOwn(object, iteratee) {\n return object && baseFor(object, iteratee, keys);\n}\n\nmodule.exports = baseForOwn;\n\n\n/***/ }),\n/* 100 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.adaptIceServers = function adaptIceServers(iceServers) {\n return iceServers.map(iceServer => ({\n url: iceServer.url,\n // deprecated\n urls: iceServer.urls || [iceServer.url],\n username: iceServer.username,\n credential: iceServer.credential\n }));\n};\n\nexports.parseIceServers = function parseIceServers(message) {\n let iceServers;\n\n try {\n iceServers = JSON.parse(message.data).content.iceServers;\n } catch (e) {\n return [];\n }\n\n return exports.adaptIceServers(iceServers);\n};\n\n/***/ }),\n/* 101 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable one-var, no-underscore-dangle, no-use-before-define, no-param-reassign */\n\n/* eslint-disable max-len, no-var, vars-on-top, global-require */\nconst promiseTimeout = __webpack_require__(153);\n\nconst now = __webpack_require__(47);\n\nconst assign = __webpack_require__(7);\n\nconst cloneDeep = __webpack_require__(54);\n\nconst intersection = __webpack_require__(127);\n\nconst merge = __webpack_require__(147);\n\nconst omit = __webpack_require__(269);\n\nconst uuid = __webpack_require__(17);\n\nconst eventing = __webpack_require__(5);\n\nconst OTHelpersError = __webpack_require__(248);\n\nconst createCleanupJobs = __webpack_require__(154);\n\nconst SDPHelpers = __webpack_require__(49);\n\nconst shouldUsePlanBSDP = __webpack_require__(72);\n\nmodule.exports = function PeerConnectionFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const hasValidPeerConnection = deps.hasValidPeerConnection || __webpack_require__(592);\n\n const applySdpTransform = deps.applySdpTransform || __webpack_require__(155);\n\n const createPeerConnection = deps.createPeerConnection || __webpack_require__(595);\n\n const env = deps.env || __webpack_require__(2);\n\n const sdpTransformDefaults = deps.sdpTransformDefaults || __webpack_require__(597);\n\n const getStatsAdapter = deps.getStatsAdapter || __webpack_require__(272);\n\n const getRtcStatsReportAdapter = deps.getRtcStatsReportAdapter || __webpack_require__(275);\n\n const getSynchronizationSources = deps.getSynchronizationSources || __webpack_require__(601);\n\n const IceCandidateProcessor = deps.IceCandidateProcessor || __webpack_require__(602);\n\n const createLog = deps.logging || __webpack_require__(0);\n\n const offerProcessor = deps.offerProcessor || __webpack_require__(604);\n\n const PeerConnectionChannels = deps.PeerConnectionChannels || __webpack_require__(606);\n\n const subscribeProcessor = deps.subscribeProcessor || __webpack_require__(608);\n\n const _ref = deps.changeMediaDirection || __webpack_require__(609),\n changeMediaDirectionToInactive = _ref.changeMediaDirectionToInactive,\n changeMediaDirectionToRecvOnly = _ref.changeMediaDirectionToRecvOnly;\n\n const Qos = deps.Qos || __webpack_require__(276).default;\n\n const windowMock = deps.global || (typeof window !== undefined ? window : global);\n\n const debounce = deps.debounce || __webpack_require__(613);\n\n const needsToSwapH264Profiles = deps.needsToSwapH264Profiles || __webpack_require__(615).once;\n\n const futureIsPeerConnectionValid = () => hasValidPeerConnection(windowMock.RTCPeerConnection);\n\n const NativeRTCIceCandidate = deps.NativeRTCIceCandidate || windowMock.RTCIceCandidate;\n const NativeRTCSessionDescription = deps.NativeRTCSessionDescription || windowMock.RTCSessionDescription;\n\n const Errors = deps.Errors || __webpack_require__(6);\n\n const OTErrorClass = deps.OTErrorClass || __webpack_require__(20);\n\n const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(9); // Unified-plan does not transition to failed. Plan-b used to transition after 10 secs (empirically measured)\n\n\n const DISCONNECT_TO_FAILED_TIMEOUT = 10000; // Helper function to forward Ice Candidates via +sendMessage+\n\n const iceCandidateForwarder = (_ref2) => {\n let sendMessage = _ref2.sendMessage,\n logging = _ref2.logging;\n const sdpMids = {};\n return event => {\n if (event.candidate) {\n // It would be better to read the mids from the SDP\n sdpMids[event.candidate.sdpMid] = event.candidate.sdpMLineIndex;\n sendMessage('candidate', {\n candidate: event.candidate.candidate,\n sdpMid: event.candidate.sdpMid || '',\n sdpMLineIndex: event.candidate.sdpMLineIndex || 0\n });\n } else {\n logging.debug('IceCandidateForwarder: No more ICE candidates.');\n }\n };\n };\n\n const noop = () => {};\n /*\n * Negotiates a WebRTC PeerConnection.\n *\n * Responsible for:\n * * offer-answer exchange\n * * iceCandidates\n * * notification of remote streams being added/removed\n *\n */\n\n\n return function PeerConnection(options) {\n if (options === void 0) {\n options = {};\n }\n\n let hasRelayCandidates = false;\n const _options = options,\n _options$iceConfig = _options.iceConfig,\n iceConfig = _options$iceConfig === void 0 ? {\n servers: []\n } : _options$iceConfig,\n isPublisher = _options.isPublisher,\n _options$logAnalytics = _options.logAnalyticsEvent,\n logAnalyticsEvent = _options$logAnalytics === void 0 ? noop : _options$logAnalytics,\n offerOverrides = _options.offerOverrides,\n answerOverrides = _options.answerOverrides,\n sdpTransformOptions = _options.sdpTransformOptions,\n originalSendMessage = _options.sendMessage,\n p2p = _options.p2p,\n codecFlags = _options.codecFlags,\n sourceStreamId = _options.sourceStreamId;\n let offerAnswerPending = false; // Whether we have an offer out and are waiting for an answer\n\n let regenerateOffer = false;\n let processingOffer = false; // Whether we are currently processing an offer\n\n let pendingOfferMessage; // An offer we received that is pending waiting on a previous offer\n\n const replaceBaselineProfile = needsToSwapH264Profiles();\n let offerMessagesReceived = 0; // number of offer or generateoffer messages received\n\n let renegotiationInProgress = false;\n const cleanupJobs = createCleanupJobs();\n\n function sendMessage(type, payload) {\n if (!hasRelayCandidates) {\n const shouldCheckForRelayCandidates = ['candidate', 'offer', 'answer', 'pranswer'].indexOf(type) > -1;\n\n if (shouldCheckForRelayCandidates) {\n const message = type === 'candidate' ? payload.candidate : payload.sdp;\n hasRelayCandidates = message.indexOf('typ relay') !== -1;\n }\n }\n\n originalSendMessage(type, payload);\n }\n\n const startConnectingTime = now();\n logAnalyticsEvent('createPeerConnection', 'Attempt');\n const api = {};\n api.id = uuid();\n const logging = createLog(`PeerConnection:${api.id}`);\n logging.debug('construct', {\n id: api.id,\n options\n });\n const sdpTransforms = merge({}, sdpTransformDefaults, sdpTransformOptions);\n\n const shouldFilterCandidate = candidate => iceConfig.transportPolicy === 'relay' && candidate != null && candidate.candidate.indexOf('typ relay') === -1;\n\n const config = omit(options, ['isPublisher', 'logAnalyticsEvent', 'offerOverrides', 'answerOverrides', 'sendMessage', 'sdpTransformOptions']);\n\n let _peerConnection, _channels, _offer, _answer, _transitionToFailedTimeOut;\n\n let _peerConnectionCompletionHandlers = [];\n\n const _simulcastEnabled = (() => {\n let value = config.overrideSimulcastEnabled || false;\n return {\n get() {\n return value;\n },\n\n set(newValueParam) {\n const newValue = Boolean(newValueParam) && config.capableSimulcastStreams > 1;\n\n if (newValue !== value && config.overrideSimulcastEnabled === undefined) {\n value = newValue;\n api.generateOffer();\n }\n }\n\n };\n })();\n\n const _iceRestartNeeded = (() => {\n let value = false;\n return {\n get() {\n return value;\n },\n\n set(newValueParam) {\n const newValue = Boolean(newValueParam);\n\n if (newValue !== value) {\n value = newValue;\n\n if (value && !api.iceConnectionStateIsConnected()) {\n api.clearFailedTimeout();\n api.generateOffer();\n } else {\n logging.debug('iceRestart is going to wait until we disconnect and then restart ice');\n }\n }\n }\n\n };\n })(); // OPENTOK-27106: This _readyToSendOffer mechanism is a workaround for a P2P IE->FF issue where\n // ice candidates sometimes take an excessive amount of time (~10 seconds) to be generated by the\n // IE plugin. FF will time out if there is a delay like this between receiving the offer and\n // receiving ice candidates, so the workaround holds back sending the offer until an ice candidate\n // appears.\n\n\n const _readyToSendOffer = {\n clean() {\n delete this.promise;\n delete this.resolve;\n delete this.reject;\n }\n\n };\n _readyToSendOffer.promise = new Promise((resolve, reject) => {\n _readyToSendOffer.resolve = resolve;\n _readyToSendOffer.reject = reject;\n });\n\n let _iceProcessor = new IceCandidateProcessor();\n\n let _state = 'new';\n Object.defineProperty(api, 'signalingState', {\n get() {\n return _peerConnection.signalingState;\n },\n\n set(val) {\n // obviously they should not be doing this, but we'll mimic what the browser does.\n _peerConnection.signalingState = val;\n return val;\n }\n\n });\n eventing(api);\n api.once('iceConnected', () => {\n const proxyInfo = '';\n const payload = {\n pcc: parseInt(now() - startConnectingTime, 10),\n hasRelayCandidates,\n proxyInfo\n };\n if (_peerConnection && _peerConnection.proxyInfo) payload.proxyInfo = _peerConnection.proxyInfo;\n logAnalyticsEvent('createPeerConnection', 'Success', payload);\n });\n\n _readyToSendOffer.resolve(); // Create and initialise the PeerConnection object. This deals with\n // any differences between the various browser implementations and\n // our own OTPlugin version.\n //\n // +completion+ is the function is call once we've either successfully\n // created the PeerConnection or on failure.\n //\n\n\n const internalCreatePeerConnection = completion => {\n logging.debug('internalCreatePeerConnection: called');\n\n if (_peerConnection) {\n logging.debug('internalCreatePeerConnection: resolving synchronously');\n completion.call(null, null, _peerConnection);\n return;\n }\n\n _peerConnectionCompletionHandlers.push(completion);\n\n if (_peerConnectionCompletionHandlers.length > 1) {\n // The PeerConnection is already being setup, just wait for\n // it to be ready.\n return;\n }\n\n const pcConstraints = {\n optional: [// This should be unnecessary, but the plugin has issues if we remove it. This needs\n // to be investigated.\n // @todo now plugin is gone, can we remove it?\n {\n DtlsSrtpKeyAgreement: true\n }]\n };\n logging.debug(`Creating peer connection config \"${JSON.stringify(config)}\".`);\n\n if (iceConfig.servers.length === 0) {\n // This should never happen unless something is misconfigured\n logging.error('No ice servers present');\n logAnalyticsEvent('Error', 'noIceServers');\n }\n\n if (iceConfig.transportPolicy === 'relay') {\n const isTurnUrl = url => url && url.indexOf('turn') === 0;\n\n iceConfig.servers = iceConfig.servers.map(providedServer => {\n const server = cloneDeep(providedServer);\n\n if (!Array.isArray(server.urls)) {\n server.urls = [server.urls];\n }\n\n server.urls = server.urls.filter(isTurnUrl);\n return server.urls.length === 0 ? undefined : server;\n }).filter(server => server !== undefined);\n }\n\n config.iceTransportPolicy = iceConfig.transportPolicy;\n config.iceServers = iceConfig.servers;\n futureIsPeerConnectionValid().then(isValid => {\n if (!isValid) {\n logging.error('createPeerConnection: Invalid RTCPeerConnection object');\n throw new Error('Failed to create valid RTCPeerConnection object');\n }\n\n return createPeerConnection({\n window: windowMock,\n config,\n constraints: pcConstraints\n });\n }).then(pc => attachEventsToPeerConnection(null, pc), err => attachEventsToPeerConnection(err));\n }; // An auxiliary function to internalCreatePeerConnection. This binds the various event\n // callbacks once the peer connection is created.\n //\n // +err+ will be non-null if an err occured while creating the PeerConnection\n // +pc+ will be the PeerConnection object itself.\n //\n // @todo if anything called in attachEventsToPeerConnection throws it will be\n // silent\n\n\n const attachEventsToPeerConnection = (err, pc) => {\n if (err) {\n triggerError({\n reason: `Failed to create PeerConnection, exception: ${err}`,\n prefix: 'NewPeerConnection'\n });\n _peerConnectionCompletionHandlers = [];\n return;\n }\n\n logging.debug('OT attachEventsToPeerConnection');\n _peerConnection = pc;\n\n if (_iceProcessor) {\n _iceProcessor.setPeerConnection(pc);\n }\n\n _channels = new PeerConnectionChannels(_peerConnection);\n\n if (config.channels) {\n _channels.addMany(config.channels);\n }\n\n const forwarder = iceCandidateForwarder({\n sendMessage,\n logging\n });\n\n const onIceCandidate = event => {\n _readyToSendOffer.resolve();\n\n if (shouldFilterCandidate(event.candidate)) {\n logging.debug('Ignore candidate', event.candidate.candidate);\n return;\n }\n\n forwarder(event);\n };\n\n let _previousIceState = _peerConnection.iceConnectionState;\n\n const onIceConnectionStateChanged = event => {\n if (_peerConnection) {\n logging.debug('iceconnectionstatechange', _peerConnection.iceConnectionState);\n\n if (_peerConnection.iceConnectionState === 'connected') {\n api.emit('iceConnected');\n\n _iceRestartNeeded.set(false);\n } else if (_peerConnection.iceConnectionState === 'completed' && env.isLegacyEdge) {\n // Start collecting stats later in Edge because it fails if you call it sooner\n // This can probably be fixed better in Adapter.js\n setTimeout(() => qos.startCollecting(_peerConnection), 1000);\n }\n } else {\n logging.debug('iceconnectionstatechange on peerConnection that no longer exists', api);\n }\n\n const newIceState = event.target.iceConnectionState;\n api.trigger('iceConnectionStateChange', newIceState);\n logAnalyticsEvent('attachEventsToPeerConnection', 'iceconnectionstatechange', newIceState);\n\n if (newIceState === 'disconnected') {\n if (_iceRestartNeeded.get()) {\n logging.debug('Restarting ice!');\n api.generateOffer();\n } else if (!shouldUsePlanBSDP()) {\n // We only transition to failed in unified-plan\n // Plan-b will transition natively\n _transitionToFailedTimeOut = setTimeout(() => {\n const iceState = 'failed';\n logAnalyticsEvent('attachEventsToPeerConnection', 'iceconnectionstatechange', iceState);\n _previousIceState = iceState;\n api.trigger('iceConnectionStateChange', iceState);\n }, DISCONNECT_TO_FAILED_TIMEOUT);\n }\n }\n\n if (_previousIceState !== 'disconnected' && newIceState === 'failed') {\n // the sequence disconnected => failure would indicate an abrupt disconnection (e.g. remote\n // peer closed the browser) or a network problem. We don't want to log that has a connection\n // establishment failure. This behavior is seen only in Chrome 47+\n triggerError({\n reason: 'The stream was unable to connect due to a network error.' + ' Make sure your connection isn\\'t blocked by a firewall.',\n prefix: 'ICEWorkflow'\n });\n }\n\n _previousIceState = newIceState;\n };\n\n const onNegotiationNeeded = () => {\n logAnalyticsEvent('peerConnection:negotiationNeeded', 'Event');\n\n if (isPublisher) {\n api.generateOffer();\n }\n }; // Work around the fact that the safari adapter shim only\n // fires 'addstream' event if onaddstream has been set\n // https://github.com/opentok/webrtc-js/pull/2266#issuecomment-305647862\n\n\n _peerConnection.onaddstream = noop;\n\n _peerConnection.addEventListener('icecandidate', onIceCandidate);\n\n _peerConnection.addEventListener('addstream', onRemoteStreamAdded);\n\n _peerConnection.addEventListener('removestream', onRemoteStreamRemoved);\n\n _peerConnection.addEventListener('signalingstatechange', routeStateChanged);\n\n _peerConnection.addEventListener('negotiationneeded', onNegotiationNeeded);\n\n _peerConnection.addEventListener('iceconnectionstatechange', onIceConnectionStateChanged);\n\n cleanupJobs.add(() => {\n if (!_peerConnection) {\n return;\n }\n\n _peerConnection.onaddstream = null;\n\n _peerConnection.removeEventListener('icecandidate', onIceCandidate);\n\n _peerConnection.removeEventListener('addstream', onRemoteStreamAdded);\n\n _peerConnection.removeEventListener('removestream', onRemoteStreamRemoved);\n\n _peerConnection.removeEventListener('signalingstatechange', routeStateChanged);\n\n _peerConnection.removeEventListener('negotiationneeded', onNegotiationNeeded);\n\n _peerConnection.removeEventListener('iceconnectionstatechange', onIceConnectionStateChanged);\n });\n triggerPeerConnectionCompletion(null);\n };\n\n const triggerPeerConnectionCompletion = () => {\n while (_peerConnectionCompletionHandlers.length) {\n _peerConnectionCompletionHandlers.shift().call(null);\n }\n }; // Clean up the Peer Connection and trigger the close event.\n // This function can be called safely multiple times, it will\n // only trigger the close event once (per PeerConnection object)\n\n\n const tearDownPeerConnection = () => {\n // Our connection is dead, stop processing ICE candidates\n if (_iceProcessor) {\n _iceProcessor.destroy();\n\n _iceProcessor = null;\n }\n\n _peerConnectionCompletionHandlers = [];\n qos.stopCollecting();\n cleanupJobs.releaseAll();\n\n _readyToSendOffer.clean();\n\n if (_peerConnection !== null) {\n if (_peerConnection.destroy) {\n // OTPlugin defines a destroy method on PCs. This allows\n // the plugin to release any resources that it's holding.\n _peerConnection.destroy();\n }\n\n _peerConnection = null;\n api.trigger('close');\n }\n\n api.off();\n };\n\n const routeStateChanged = event => {\n const newState = event.target.signalingState;\n api.emit('signalingStateChange', newState);\n\n if (newState === 'stable') {\n api.emit('signalingStateStable');\n }\n\n if (newState && newState !== _state) {\n _state = newState;\n logging.debug(`stateChange: ${_state}`);\n\n switch (_state) {\n case 'closed':\n tearDownPeerConnection();\n break;\n\n default:\n }\n }\n };\n\n const qosCallback = parsedStats => {\n parsedStats.dataChannels = _channels.sampleQos();\n api.trigger('qos', {\n parsedStats,\n simulcastEnabled: _simulcastEnabled.get()\n });\n };\n\n const getRemoteStreams = () => {\n let streams;\n\n if (_peerConnection.getRemoteStreams) {\n streams = _peerConnection.getRemoteStreams();\n } else if (_peerConnection.remoteStreams) {\n streams = _peerConnection.remoteStreams;\n } else {\n throw new Error('Invalid Peer Connection object implements no ' + 'method for retrieving remote streams');\n } // Force streams to be an Array, rather than a 'Sequence' object,\n // which is browser dependent and does not behaviour like an Array\n // in every case.\n\n\n return Array.prototype.slice.call(streams);\n }; // / PeerConnection signaling\n\n\n const onRemoteStreamAdded = event => {\n logAnalyticsEvent('createPeerConnection', 'StreamAdded');\n api.trigger('streamAdded', event.stream);\n };\n\n const onRemoteStreamRemoved = event => {\n logAnalyticsEvent('peerConnection:streamRemoved', 'Event');\n api.trigger('streamRemoved', event.stream);\n }; // ICE Negotiation messages\n\n\n const reportError = (message, reason, prefix) => {\n processingOffer = false;\n triggerError({\n reason: `PeerConnection.offerProcessor ${message}: ${reason}`,\n prefix\n });\n }; // Process an offer that\n\n\n const processOffer = message => {\n if (processingOffer) {\n // If we get multiple pending offer messages we just handle the most recent one\n pendingOfferMessage = message;\n return;\n }\n\n processingOffer = true;\n logAnalyticsEvent('peerConnection:processOffer', 'Event');\n const offer = new NativeRTCSessionDescription({\n type: 'offer',\n sdp: message.content.sdp\n }); // Relays +answer+ Answer\n\n const relayAnswer = answer => {\n processingOffer = false;\n\n if (_iceProcessor) {\n _iceProcessor.process();\n }\n\n sendMessage('answer', answer);\n\n if (!env.isLegacyEdge) {\n qos.startCollecting(_peerConnection, isPublisher);\n }\n\n if (pendingOfferMessage) {\n processOffer(pendingOfferMessage);\n pendingOfferMessage = null;\n }\n };\n\n const onRemoteVideoSupported = supported => api.trigger('remoteVideoSupported', supported);\n\n internalCreatePeerConnection(() => {\n offerProcessor(_peerConnection, windowMock.RTCPeerConnection, windowMock.RTCSessionDescription, NativeRTCSessionDescription, sdpTransforms, offer, codecFlags, p2p, relayAnswer, reportError, onRemoteVideoSupported, replaceBaselineProfile);\n });\n };\n\n const processAnswer = message => {\n logAnalyticsEvent('peerConnection:processAnswer', 'Event');\n\n const failure = errorReason => {\n triggerError({\n reason: `Error while setting RemoteDescription ${errorReason}`,\n prefix: 'SetRemoteDescription'\n });\n };\n\n if (!message.content.sdp) {\n failure('Weird answer message, no SDP.');\n return;\n }\n\n _answer = new NativeRTCSessionDescription({\n type: 'answer',\n sdp: applySdpTransform(sdpTransforms, 'remote', 'answer', assign({\n replaceBaselineProfile\n }, answerOverrides), message.content.sdp).local\n });\n\n (() => {\n const success = () => {\n logging.debug('processAnswer: setRemoteDescription Success');\n\n if (_iceProcessor) {\n _iceProcessor.process();\n }\n };\n\n _peerConnection.setRemoteDescription(_answer).then(success).catch(failure);\n\n offerAnswerPending = false;\n\n if (regenerateOffer) {\n regenerateOffer = false;\n api.generateOffer();\n }\n })();\n\n if (!env.isLegacyEdge) {\n qos.startCollecting(_peerConnection, isPublisher);\n }\n };\n\n const processSubscribe = () => {\n var _peerConnection2, _peerConnection2$sign;\n\n if (((_peerConnection2 = _peerConnection) == null ? void 0 : (_peerConnection2$sign = _peerConnection2.signalingState) == null ? void 0 : _peerConnection2$sign.toLowerCase()) === 'closed') {\n return;\n }\n\n offerAnswerPending = true;\n logAnalyticsEvent('peerConnection:processSubscribe', 'Event');\n logging.debug('processSubscribe: Sending offer to subscriber.');\n let numSimulcastStreams = _simulcastEnabled.get() ? config.capableSimulcastStreams : 1; // When transitioning from Mantis to P2P, in order to ensure the video quality we must\n // send only one video layer, regardless that simulcast is enabled.\n\n const isAdaptiveP2pSourceStreamId = sourceStreamId === 'P2P' && !p2p;\n\n if (isAdaptiveP2pSourceStreamId) {\n numSimulcastStreams = 1;\n }\n\n internalCreatePeerConnection(() => {\n subscribeProcessor({\n peerConnection: _peerConnection,\n NativeRTCSessionDescription,\n sdpTransforms,\n numSimulcastStreams,\n offerOverrides,\n offerConstraints: {\n iceRestart: _iceRestartNeeded.get()\n },\n replaceBaselineProfile\n }).then(offer => {\n logging.debug('processSubscribe: got offer, waiting for ' + '_readyToSendOffer');\n _offer = offer;\n\n _readyToSendOffer.promise.then(() => {\n logging.debug('processSubscribe: sending offer');\n sendMessage('offer', _offer);\n });\n }, error => {\n triggerError({\n reason: `subscribeProcessor ${error.message}: ${error.reason}`,\n prefix: error.prefix\n });\n });\n\n _iceRestartNeeded.set(false);\n });\n };\n\n api.generateOffer = debounce(() => {\n if (!offerAnswerPending) {\n processSubscribe();\n } else {\n regenerateOffer = true;\n }\n }, 100);\n\n const triggerError = (_ref3) => {\n let reason = _ref3.reason,\n prefix = _ref3.prefix;\n logging.error(reason, 'in state', !_peerConnection ? '(none)' : {\n connectionState: _peerConnection.connectionState,\n iceConnectionState: _peerConnection.iceConnectionState,\n iceGatheringState: _peerConnection.iceGatheringState,\n signalingState: _peerConnection.signalingState\n });\n api.trigger('error', {\n reason,\n prefix\n });\n };\n /*\n * Add a track to the underlying PeerConnection\n *\n * @param {object} track - the track to add\n * @param {object} stream - the stream to add it to\n * @return {RTCRtpSender}\n */\n\n\n api.addTrack = (track, stream) => {\n const promise = new Promise((resolve, reject) => {\n internalCreatePeerConnection(err => {\n if (err) {\n return reject(err);\n }\n\n resolve();\n return undefined;\n });\n }).then(() => {\n if (_peerConnection.addTrack) {\n return _peerConnection.addTrack(track, stream);\n }\n\n const pcStream = _peerConnection.getLocalStreams()[0];\n\n if (pcStream === undefined) {\n throw new Error('PeerConnection has no existing streams, cannot addTrack');\n }\n\n pcStream.addTrack(track);\n api.generateOffer();\n return undefined;\n }).then(() => new Promise(resolve => {\n api.once('signalingStateStable', () => {\n resolve();\n });\n }));\n return promiseTimeout(promise, 15000, 'Renegotiation timed out');\n };\n\n function FakeRTCRtpSender(track) {\n this.track = track;\n }\n /**\n * Remove a track from the underlying PeerConnection\n *\n * @param {RTCRtpSender} RTCRtpSender - the RTCRtpSender to remove\n */\n\n\n api.removeTrack = RTCRtpSender => {\n const promise = Promise.resolve().then(() => {\n if (RTCRtpSender instanceof FakeRTCRtpSender) {\n _peerConnection.getLocalStreams()[0].removeTrack(RTCRtpSender.track);\n\n api.generateOffer();\n return undefined;\n }\n\n return _peerConnection.removeTrack(RTCRtpSender);\n }).then(() => new Promise(resolve => {\n api.once('signalingStateStable', () => {\n resolve();\n });\n }));\n return promiseTimeout(promise, 15000, 'Renegotiation timed out');\n };\n\n api.addLocalStream = webRTCStream => new Promise((resolve, reject) => {\n internalCreatePeerConnection(err => {\n if (err) {\n reject(err);\n return;\n }\n\n try {\n _peerConnection.addStream(webRTCStream);\n } catch (addStreamErr) {\n reject(addStreamErr);\n return;\n }\n\n resolve();\n }, webRTCStream);\n });\n\n api.getLocalStreams = () => _peerConnection.getLocalStreams();\n\n api.getSenders = () => {\n if (_peerConnection.getSenders) {\n return _peerConnection.getSenders();\n }\n\n return _peerConnection.getLocalStreams()[0].getTracks().map(track => new FakeRTCRtpSender(track));\n };\n\n api.disconnect = () => {\n var _peerConnection3;\n\n if (_iceProcessor) {\n _iceProcessor.destroy();\n\n _iceProcessor = null;\n }\n\n if (((_peerConnection3 = _peerConnection) == null ? void 0 : _peerConnection3.signalingState) && _peerConnection.signalingState.toLowerCase() !== 'closed') {\n _peerConnection.close();\n\n setTimeout(tearDownPeerConnection);\n }\n };\n\n api.iceRestart = () => _iceRestartNeeded.set(true);\n\n api.clearFailedTimeout = () => clearTimeout(_transitionToFailedTimeOut);\n\n api.changeMediaDirectionToRecvOnly = () => changeMediaDirectionToRecvOnly(_peerConnection);\n\n api.changeMediaDirectionToInactive = () => changeMediaDirectionToInactive(_peerConnection);\n\n api.processMessage = (type, message) => {\n logging.debug(`processMessage: Received ${type} from ${message.fromAddress}`, message);\n logging.debug(message);\n\n switch (type) {\n case 'generateoffer':\n if (message.content && message.content.simulcastEnabled !== undefined) {\n _simulcastEnabled.set(message.content.simulcastEnabled);\n }\n\n api.generateOffer();\n trackRenegotiationAttempts();\n break;\n\n case 'offer':\n processOffer(message);\n trackRenegotiationAttempts();\n break;\n\n case 'answer':\n case 'pranswer':\n processAnswer(message);\n break;\n\n case 'candidate':\n var iceCandidate = new NativeRTCIceCandidate(message.content);\n\n if (shouldFilterCandidate(iceCandidate)) {\n logging.debug('Ignore candidate', iceCandidate.candidate);\n return api;\n }\n\n if (_iceProcessor) {\n _iceProcessor.addIceCandidate(iceCandidate).catch(err => {\n // Sometimes you might get an error adding an iceCandidate\n // this does not mean we should fail, if we get a working candidate\n // later then we should let it work\n logging.warn(`Error while adding ICE candidate: ${JSON.stringify(iceCandidate)}: ${err.toString()}`);\n });\n }\n\n break;\n\n default:\n logging.debug(`processMessage: Received an unexpected message of type ${type} from ${message.fromAddress}: ${JSON.stringify(message)}`);\n }\n\n function trackRenegotiationAttempts() {\n offerMessagesReceived += 1;\n qos.handleOfferMessageReceived();\n\n if (offerMessagesReceived > 1) {\n logAnalyticsEvent('Renegotiation', 'Attempt');\n renegotiationInProgress = true;\n }\n }\n\n return api;\n };\n\n api.remoteStreams = () => _peerConnection ? getRemoteStreams() : [];\n\n api.remoteTracks = () => {\n if (!_peerConnection) {\n return [];\n }\n\n if (_peerConnection.getReceivers) {\n return Array.prototype.slice.apply(_peerConnection.getReceivers()).map(receiver => receiver.track);\n }\n\n return Array.prototype.concat.apply([], getRemoteStreams().map(stream => stream.getTracks()));\n };\n\n api.remoteDescription = () => _peerConnection.remoteDescription;\n /**\n * @param {function(DOMError, Array)} callback\n */\n\n\n api.getStats = callback => {\n if (!_peerConnection) {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(new OTHelpersError(OTErrorClass.getTitleByCode(errorCode), Errors.PEER_CONNECTION_NOT_CONNECTED, {\n code: errorCode\n }));\n return;\n }\n\n getStatsAdapter(_peerConnection, callback);\n };\n /**\n * @param {function(DOMError, RTCStatsReport)} callback\n */\n\n\n api.getRtcStatsReport = callback => {\n if (!_peerConnection) {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(new OTHelpersError(OTErrorClass.getTitleByCode(errorCode), Errors.PEER_CONNECTION_NOT_CONNECTED, {\n code: errorCode\n }));\n return;\n }\n\n getRtcStatsReportAdapter(_peerConnection, callback);\n };\n\n api.getSynchronizationSources = callback => {\n if (!isPublisher) {\n if (!_peerConnection) {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(new OTHelpersError(OTErrorClass.getTitleByCode(errorCode), Errors.PEER_CONNECTION_NOT_CONNECTED, {\n code: errorCode\n }));\n return;\n }\n\n getSynchronizationSources(_peerConnection, callback);\n }\n };\n\n const waitForChannel = function waitForChannel(timesToWait, label, channelOptions, completion) {\n let err;\n\n const channel = _channels.get(label, channelOptions);\n\n if (!channel) {\n if (timesToWait > 0) {\n setTimeout(waitForChannel.bind(null, timesToWait - 1, label, channelOptions, completion), 200);\n return;\n }\n\n err = new OTHelpersError(`${'A channel with that label and options could not be found. ' + 'Label:'}${label}. Options: ${JSON.stringify(channelOptions)}`);\n }\n\n completion(err, channel);\n };\n\n api.getDataChannel = (label, channelOptions, completion) => {\n if (!_peerConnection) {\n completion(new OTHelpersError('Cannot create a DataChannel before there is a connection.'));\n return;\n } // Wait up to 20 sec for the channel to appear, then fail\n\n\n waitForChannel(100, label, channelOptions, completion);\n };\n\n api.getSourceStreamId = () => sourceStreamId;\n\n api.iceConnectionStateIsConnected = () => _peerConnection && ['connected', 'completed'].indexOf(_peerConnection.iceConnectionState) > -1;\n\n api.findAndReplaceTrack = (oldTrack, newTrack) => Promise.resolve().then(() => {\n const sender = _peerConnection.getSenders().filter(s => s.track === oldTrack)[0];\n\n if (!sender) {\n // There is no video track to replace but this is OK\n // they probably called cycleVideo on an audio only Publisher\n // or on a Publisher that does not support the videoCodec\n return Promise.resolve();\n }\n\n if (typeof sender.replaceTrack !== 'function') {\n throw new Error('Sender does not support replaceTrack');\n }\n\n return sender.replaceTrack(newTrack);\n });\n\n api.hasRelayCandidates = () => hasRelayCandidates;\n\n api.getNegotiatedCodecs = () => {\n if (!_peerConnection.localDescription || !_peerConnection.remoteDescription) {\n return null;\n }\n\n const getForMediaType = mediaType => intersection(SDPHelpers.getCodecs(_peerConnection.localDescription.sdp, mediaType), SDPHelpers.getCodecs(_peerConnection.remoteDescription.sdp, mediaType));\n\n return {\n audio: getForMediaType('audio'),\n video: getForMediaType('video')\n };\n };\n\n api.on('signalingStateStable', () => {\n if (renegotiationInProgress) {\n renegotiationInProgress = false;\n logAnalyticsEvent('Renegotiation', 'Success', api.getNegotiatedCodecs());\n }\n });\n api.on('error', (_ref4) => {\n let prefix = _ref4.prefix;\n\n if (renegotiationInProgress && ['CreateOffer', 'SetRemoteDescription'].indexOf(prefix) !== -1) {\n renegotiationInProgress = false;\n logAnalyticsEvent('Renegotiation', 'Failure');\n }\n });\n api.on('close', () => {\n if (renegotiationInProgress) {\n renegotiationInProgress = false;\n logAnalyticsEvent('Renegotiation', 'Cancel');\n }\n });\n const qos = new Qos();\n qos.on('stats', qosCallback);\n return api;\n };\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 102 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2);\n/**\n * Firefox, Edge, Safari and Chrome v58+ will use the standard version\n * of getStats API.\n * https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats#Browser_compatibility\n */\n\n\nmodule.exports = () => env.name === 'Chrome' && env.version >= 58 || ['Firefox', 'Edge', 'Safari', 'Opera'].indexOf(env.name) !== -1;\n\n/***/ }),\n/* 103 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/*\n * Executes the provided callback before each paint (frequency is usually the monitors refresh\n * rate which is typically 60 times per second)\n *\n * @param {function()} callback\n * @constructor\n */\nmodule.exports = function RafRunner(callback) {\n let cancelId = null;\n let running = false;\n\n const loop = () => {\n if (!running) {\n return;\n }\n\n callback();\n cancelId = window.requestAnimationFrame(loop);\n };\n\n this.start = () => {\n if (running) {\n return;\n }\n\n running = true;\n window.requestAnimationFrame(loop);\n };\n\n this.stop = () => {\n window.cancelAnimationFrame(cancelId);\n running = false;\n };\n};\n\n/***/ }),\n/* 104 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst assign = __webpack_require__(7);\n\nconst generatePropertyFunction = (object, getter, setter) => {\n if (getter && !setter) {\n return () => getter.call(object);\n }\n\n if (getter && setter) {\n return value => {\n if (value !== undefined) {\n setter.call(object, value);\n }\n\n return getter.call(object);\n };\n }\n\n return value => {\n if (value !== undefined) {\n setter.call(object, value);\n }\n };\n};\n/**\n * @typedef getterSetterDefinition\n * @property {function(): any} get the function called when accessing the value\n * @property {function(any): void} set the function called when setting the value\n */\n\n/**\n * Creates primitive getters/ setters on objects\n *\n * For every key in gettersSetters, a method will be added to object.\n *\n * When the method is called with no value, it will call the getter\n * When the method is called with a value, it will call the setter with the value\n *\n * @deprecated Prefer using real getters and setters\n *\n * @param {any} object\n * @param {Object.} getterSetters\n */\n\n\nmodule.exports = (object, getterSetters) => {\n const mixin = {};\n Object.keys(getterSetters).forEach(key => {\n mixin[key] = generatePropertyFunction(object, getterSetters[key].get, getterSetters[key].set);\n });\n assign(object, mixin);\n};\n\n/***/ }),\n/* 105 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\n/* eslint-disable func-names */\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require */\nmodule.exports = function deviceHelpersFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const getNativeEnumerateDevices = deps.getNativeEnumerateDevices || __webpack_require__(284)();\n\n const env = deps.env || __webpack_require__(2);\n\n const deviceHelpers = {}; // /\n // Device Helpers\n //\n // Support functions to enumerating and guerying device info\n //\n\n const deviceKindsMap = {\n audio: 'audioInput',\n video: 'videoInput',\n audioinput: 'audioInput',\n videoinput: 'videoInput',\n audioInput: 'audioInput',\n videoInput: 'videoInput'\n };\n\n const enumerateDevices = function enumerateDevices() {\n // Our mocking currently requires that this be re-evaluated each time.\n const fn = getNativeEnumerateDevices();\n return fn();\n }; // Indicates whether this browser supports the enumerateDevices (getSources) API.\n //\n\n\n deviceHelpers.hasEnumerateDevicesCapability = function () {\n return typeof getNativeEnumerateDevices() === 'function';\n }; // OPENTOK-40733, see https://bugs.webkit.org/show_bug.cgi?id=209739#c2\n // multiple calls to enumerateDevices fail to return video devices\n // note that this causes any devices without a camera (mac minis) to make a video gum call\n // we believe this is worth it to prevent safari video breaking\n\n\n deviceHelpers.hasEnumerateDevicesBug = () => env.isSafari && env.version === 13.1;\n\n deviceHelpers.getMediaDevices = function () {\n if (!deviceHelpers.hasEnumerateDevicesCapability()) {\n return Promise.reject(new Error('This browser does not support enumerateDevices APIs'));\n }\n\n return enumerateDevices().then(devices => // Normalise the device kinds\n devices.map(device => ({\n deviceId: device.deviceId || device.id,\n label: device.label,\n kind: deviceKindsMap[device.kind]\n })).filter(device => device.kind === 'audioInput' || device.kind === 'videoInput'));\n };\n\n deviceHelpers.shouldAskForDevices = function () {\n if (!deviceHelpers.hasEnumerateDevicesCapability()) {\n // Assume video and audio exists if enumerateDevices is not available\n return Promise.resolve({\n video: true,\n audio: true\n });\n }\n\n return deviceHelpers.getMediaDevices().then(devices => {\n if (devices.length === 0) {\n // If no devices reported, might as well try anyway, maybe it was just an issue with\n // device enumeration.\n return {\n video: true,\n audio: true\n };\n }\n\n const audioDevices = devices.filter(device => device.kind === 'audioInput');\n const videoDevices = devices.filter(device => device.kind === 'videoInput');\n\n if (deviceHelpers.hasEnumerateDevicesBug() && videoDevices.length === 0) {\n return {\n audio: true,\n video: true\n };\n }\n\n return {\n video: videoDevices.length > 0,\n audio: audioDevices.length > 0,\n videoDevices,\n audioDevices\n };\n }).catch(() => ( // There was an error. It may be temporally. Just assume\n // all devices exist for now.\n {\n video: true,\n audio: true\n }));\n };\n\n return deviceHelpers;\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 106 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nmodule.exports = function promiseDelay(timeoutMs) {\n return new Promise(resolve => setTimeout(resolve, timeoutMs));\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 107 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst DefaultNativeVideoElementWrapperFactory = __webpack_require__(291);\n/**\n * VideoElementFacadeFactory DI Container for VideoElementFacade\n *\n * @package\n * @param {Object} [dependencies]\n * @param {typeof NativeVideoElementWrapper} [dependencies.NativeVideoElementWrapper]\n * @return {typeof VideoElementFacade}\n */\n\n\nfunction VideoElementFacadeFactory(_temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n _ref$NativeVideoEleme = _ref.NativeVideoElementWrapper,\n NativeVideoElementWrapper = _ref$NativeVideoEleme === void 0 ? DefaultNativeVideoElementWrapperFactory() : _ref$NativeVideoEleme;\n\n const VideoElementWrapper = NativeVideoElementWrapper;\n const defaultAudioVolume = 50;\n /**\n * Create a new VideoElementFacade\n *\n * Was used to simplify the strategy between Plugin and Native.\n *\n * @todo This needs to go.\n *\n * Note: Could VideoElementFacade not exist? Could it just be an expected interface\n * for VideoElementWrapper which could be either PluginVideoElementWrapper or\n * NativeVideoElementWrapper\n *\n * @package\n * @class\n * @param {Object} [config]\n * @param {String} [config.fallbackText] displayed when WebRTC is not supported\n * @param {Boolean} [config.muted] initial mute setting\n * @param {String} [config.fitMode] fitMode a parameter passed to VideoElementWrapper\n * @param {Object} [config._inject] @todo use the DI system\n * @param {Function} [errorHandler=() => {}] error callback function\n */\n\n function VideoElementFacade(_temp2) {\n let _ref2 = _temp2 === void 0 ? {} : _temp2,\n _ref2$fallbackText = _ref2.fallbackText,\n fallbackText = _ref2$fallbackText === void 0 ? 'Sorry, Web RTC is not available in your browser' : _ref2$fallbackText,\n fitMode = _ref2.fitMode,\n muted = _ref2.muted,\n _inject = _ref2._inject,\n widgetType = _ref2.widgetType;\n\n return new VideoElementWrapper({\n fallbackText,\n fitMode,\n muted,\n defaultAudioVolume,\n _inject,\n widgetType\n });\n }\n\n return VideoElementFacade;\n}\n\nmodule.exports = VideoElementFacadeFactory;\n\n/***/ }),\n/* 108 */\n/***/ (function(module, exports) {\n\nfunction _interopRequireWildcard(obj) {\n if (obj && obj.__esModule) {\n return obj;\n } else {\n var newObj = {};\n\n if (obj != null) {\n for (var key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {};\n\n if (desc.get || desc.set) {\n Object.defineProperty(newObj, key, desc);\n } else {\n newObj[key] = obj[key];\n }\n }\n }\n }\n\n newObj.default = obj;\n return newObj;\n }\n}\n\nmodule.exports = _interopRequireWildcard;\n\n/***/ }),\n/* 109 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar getNative = __webpack_require__(35),\n root = __webpack_require__(12);\n\n/* Built-in method references that are verified to be native. */\nvar Map = getNative(root, 'Map');\n\nmodule.exports = Map;\n\n\n/***/ }),\n/* 110 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar mapCacheClear = __webpack_require__(353),\n mapCacheDelete = __webpack_require__(360),\n mapCacheGet = __webpack_require__(362),\n mapCacheHas = __webpack_require__(363),\n mapCacheSet = __webpack_require__(364);\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\nmodule.exports = MapCache;\n\n\n/***/ }),\n/* 111 */\n/***/ (function(module, exports) {\n\n/**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\nfunction arrayEach(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (iteratee(array[index], index, array) === false) {\n break;\n }\n }\n return array;\n}\n\nmodule.exports = arrayEach;\n\n\n/***/ }),\n/* 112 */\n/***/ (function(module, exports) {\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\nmodule.exports = isLength;\n\n\n/***/ }),\n/* 113 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* WEBPACK VAR INJECTION */(function(module) {var freeGlobal = __webpack_require__(171);\n\n/** Detect free variable `exports`. */\nvar freeExports = true && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/** Detect free variable `process` from Node.js. */\nvar freeProcess = moduleExports && freeGlobal.process;\n\n/** Used to access faster Node.js helpers. */\nvar nodeUtil = (function() {\n try {\n // Use `util.types` for Node.js 10+.\n var types = freeModule && freeModule.require && freeModule.require('util').types;\n\n if (types) {\n return types;\n }\n\n // Legacy `process.binding('util')` for Node.js < 10.\n return freeProcess && freeProcess.binding && freeProcess.binding('util');\n } catch (e) {}\n}());\n\nmodule.exports = nodeUtil;\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(84)(module)))\n\n/***/ }),\n/* 114 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isPrototype = __webpack_require__(67),\n nativeKeys = __webpack_require__(369);\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n}\n\nmodule.exports = baseKeys;\n\n\n/***/ }),\n/* 115 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayFilter = __webpack_require__(374),\n stubArray = __webpack_require__(178);\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Built-in value references. */\nvar propertyIsEnumerable = objectProto.propertyIsEnumerable;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols;\n\n/**\n * Creates an array of the own enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = !nativeGetSymbols ? stubArray : function(object) {\n if (object == null) {\n return [];\n }\n object = Object(object);\n return arrayFilter(nativeGetSymbols(object), function(symbol) {\n return propertyIsEnumerable.call(object, symbol);\n });\n};\n\nmodule.exports = getSymbols;\n\n\n/***/ }),\n/* 116 */\n/***/ (function(module, exports) {\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n}\n\nmodule.exports = arrayPush;\n\n\n/***/ }),\n/* 117 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar overArg = __webpack_require__(176);\n\n/** Built-in value references. */\nvar getPrototype = overArg(Object.getPrototypeOf, Object);\n\nmodule.exports = getPrototype;\n\n\n/***/ }),\n/* 118 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Uint8Array = __webpack_require__(185);\n\n/**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\nfunction cloneArrayBuffer(arrayBuffer) {\n var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n return result;\n}\n\nmodule.exports = cloneArrayBuffer;\n\n\n/***/ }),\n/* 119 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = {\n error: {\n priority: 1,\n method: 'error'\n },\n warn: {\n priority: 2,\n method: 'warn'\n },\n info: {\n priority: 3,\n method: 'info'\n },\n log: {\n priority: 4,\n method: 'log'\n },\n debug: {\n priority: 5,\n method: 'debug'\n },\n spam: {\n priority: 6,\n method: 'log'\n }\n};\n\n/***/ }),\n/* 120 */\n/***/ (function(module, exports) {\n\n/**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\nfunction apply(func, thisArg, args) {\n switch (args.length) {\n case 0: return func.call(thisArg);\n case 1: return func.call(thisArg, args[0]);\n case 2: return func.call(thisArg, args[0], args[1]);\n case 3: return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n}\n\nmodule.exports = apply;\n\n\n/***/ }),\n/* 121 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseSetToString = __webpack_require__(390),\n shortOut = __webpack_require__(191);\n\n/**\n * Sets the `toString` method of `func` to return `string`.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\nvar setToString = shortOut(baseSetToString);\n\nmodule.exports = setToString;\n\n\n/***/ }),\n/* 122 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar eq = __webpack_require__(50),\n isArrayLike = __webpack_require__(22),\n isIndex = __webpack_require__(65),\n isObject = __webpack_require__(8);\n\n/**\n * Checks if the given arguments are from an iteratee call.\n *\n * @private\n * @param {*} value The potential iteratee value argument.\n * @param {*} index The potential iteratee index or key argument.\n * @param {*} object The potential iteratee object argument.\n * @returns {boolean} Returns `true` if the arguments are from an iteratee call,\n * else `false`.\n */\nfunction isIterateeCall(value, index, object) {\n if (!isObject(object)) {\n return false;\n }\n var type = typeof index;\n if (type == 'number'\n ? (isArrayLike(object) && isIndex(index, object.length))\n : (type == 'string' && index in object)\n ) {\n return eq(object[index], value);\n }\n return false;\n}\n\nmodule.exports = isIterateeCall;\n\n\n/***/ }),\n/* 123 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = __webpack_require__(193)() ? Object.setPrototypeOf : __webpack_require__(194);\n\n\n/***/ }),\n/* 124 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isValue = __webpack_require__(53);\n\n// prettier-ignore\nvar possibleTypes = { \"object\": true, \"function\": true, \"undefined\": true /* document.all */ };\n\nmodule.exports = function (value) {\n\tif (!isValue(value)) return false;\n\treturn hasOwnProperty.call(possibleTypes, typeof value);\n};\n\n\n/***/ }),\n/* 125 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = __webpack_require__(401)() ? Object.assign : __webpack_require__(402);\n\n\n/***/ }),\n/* 126 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/* eslint-disable no-underscore-dangle */\nconst logging = __webpack_require__(0)('Event');\n\nconst assign = __webpack_require__(7);\n\nconst intersection = __webpack_require__(127);\n\nmodule.exports = /*#__PURE__*/function () {\n function Event(type, cancelable, props) {\n if (cancelable === void 0) {\n cancelable = true;\n }\n\n if (props === void 0) {\n props = {};\n }\n\n this.type = type;\n this.cancelable = cancelable;\n this._defaultPrevented = false;\n const reservedKeys = intersection(Object.keys(this), Object.keys(props));\n\n if (reservedKeys.length > 0) {\n throw new Error(`Cannot used reserved property names: ${reservedKeys.join(',')}`);\n }\n\n assign(this, props);\n }\n\n var _proto = Event.prototype;\n\n _proto.preventDefault = function preventDefault() {\n if (this.cancelable) {\n this._defaultPrevented = true;\n } else {\n logging.warn('Event.preventDefault :: Trying to preventDefault on an ' + 'event that isn\\'t cancelable');\n }\n };\n\n _proto.isDefaultPrevented = function isDefaultPrevented() {\n return this._defaultPrevented;\n };\n\n return Event;\n}();\n\n/***/ }),\n/* 127 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayMap = __webpack_require__(31),\n baseIntersection = __webpack_require__(442),\n baseRest = __webpack_require__(87),\n castArrayLikeObject = __webpack_require__(448);\n\n/**\n * Creates an array of unique values that are included in all given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersection([2, 1], [2, 3]);\n * // => [2]\n */\nvar intersection = baseRest(function(arrays) {\n var mapped = arrayMap(arrays, castArrayLikeObject);\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped)\n : [];\n});\n\nmodule.exports = intersection;\n\n\n/***/ }),\n/* 128 */\n/***/ (function(module, exports) {\n\n/**\n * This function is like `arrayIncludes` except that it accepts a comparator.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\nfunction arrayIncludesWith(array, value, comparator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (comparator(value, array[index])) {\n return true;\n }\n }\n return false;\n}\n\nmodule.exports = arrayIncludesWith;\n\n\n/***/ }),\n/* 129 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isArrayLike = __webpack_require__(22),\n isObjectLike = __webpack_require__(14);\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n}\n\nmodule.exports = isArrayLikeObject;\n\n\n/***/ }),\n/* 130 */\n/***/ (function(module, exports) {\n\n/**\n * The default argument placeholder value for methods.\n *\n * @type {Object}\n */\nmodule.exports = {};\n\n\n/***/ }),\n/* 131 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseSetData = __webpack_require__(214),\n createBind = __webpack_require__(471),\n createCurry = __webpack_require__(472),\n createHybrid = __webpack_require__(216),\n createPartial = __webpack_require__(483),\n getData = __webpack_require__(220),\n mergeData = __webpack_require__(484),\n setData = __webpack_require__(223),\n setWrapToString = __webpack_require__(224),\n toInteger = __webpack_require__(96);\n\n/** Error message constants. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used to compose bitmasks for function metadata. */\nvar WRAP_BIND_FLAG = 1,\n WRAP_BIND_KEY_FLAG = 2,\n WRAP_CURRY_FLAG = 8,\n WRAP_CURRY_RIGHT_FLAG = 16,\n WRAP_PARTIAL_FLAG = 32,\n WRAP_PARTIAL_RIGHT_FLAG = 64;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max;\n\n/**\n * Creates a function that either curries or invokes `func` with optional\n * `this` binding and partially applied arguments.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags.\n * 1 - `_.bind`\n * 2 - `_.bindKey`\n * 4 - `_.curry` or `_.curryRight` of a bound function\n * 8 - `_.curry`\n * 16 - `_.curryRight`\n * 32 - `_.partial`\n * 64 - `_.partialRight`\n * 128 - `_.rearg`\n * 256 - `_.ary`\n * 512 - `_.flip`\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to be partially applied.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {\n var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;\n if (!isBindKey && typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var length = partials ? partials.length : 0;\n if (!length) {\n bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);\n partials = holders = undefined;\n }\n ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);\n arity = arity === undefined ? arity : toInteger(arity);\n length -= holders ? holders.length : 0;\n\n if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {\n var partialsRight = partials,\n holdersRight = holders;\n\n partials = holders = undefined;\n }\n var data = isBindKey ? undefined : getData(func);\n\n var newData = [\n func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,\n argPos, ary, arity\n ];\n\n if (data) {\n mergeData(newData, data);\n }\n func = newData[0];\n bitmask = newData[1];\n thisArg = newData[2];\n partials = newData[3];\n holders = newData[4];\n arity = newData[9] = newData[9] === undefined\n ? (isBindKey ? 0 : func.length)\n : nativeMax(newData[9] - length, 0);\n\n if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {\n bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);\n }\n if (!bitmask || bitmask == WRAP_BIND_FLAG) {\n var result = createBind(func, bitmask, thisArg);\n } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {\n result = createCurry(func, bitmask, arity);\n } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {\n result = createPartial(func, bitmask, thisArg, partials);\n } else {\n result = createHybrid.apply(undefined, newData);\n }\n var setter = data ? baseSetData : setData;\n return setWrapToString(setter(result, newData), func, bitmask);\n}\n\nmodule.exports = createWrap;\n\n\n/***/ }),\n/* 132 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseCreate = __webpack_require__(86),\n baseLodash = __webpack_require__(133);\n\n/** Used as references for the maximum length and index of an array. */\nvar MAX_ARRAY_LENGTH = 4294967295;\n\n/**\n * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.\n *\n * @private\n * @constructor\n * @param {*} value The value to wrap.\n */\nfunction LazyWrapper(value) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__dir__ = 1;\n this.__filtered__ = false;\n this.__iteratees__ = [];\n this.__takeCount__ = MAX_ARRAY_LENGTH;\n this.__views__ = [];\n}\n\n// Ensure `LazyWrapper` is an instance of `baseLodash`.\nLazyWrapper.prototype = baseCreate(baseLodash.prototype);\nLazyWrapper.prototype.constructor = LazyWrapper;\n\nmodule.exports = LazyWrapper;\n\n\n/***/ }),\n/* 133 */\n/***/ (function(module, exports) {\n\n/**\n * The function whose prototype chain sequence wrappers inherit from.\n *\n * @private\n */\nfunction baseLodash() {\n // No operation performed.\n}\n\nmodule.exports = baseLodash;\n\n\n/***/ }),\n/* 134 */\n/***/ (function(module, exports) {\n\n/** Used as the internal argument placeholder. */\nvar PLACEHOLDER = '__lodash_placeholder__';\n\n/**\n * Replaces all `placeholder` elements in `array` with an internal placeholder\n * and returns an array of their indexes.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {*} placeholder The placeholder to replace.\n * @returns {Array} Returns the new array of placeholder indexes.\n */\nfunction replaceHolders(array, placeholder) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value === placeholder || value === PLACEHOLDER) {\n array[index] = PLACEHOLDER;\n result[resIndex++] = index;\n }\n }\n return result;\n}\n\nmodule.exports = replaceHolders;\n\n\n/***/ }),\n/* 135 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseGetTag = __webpack_require__(27),\n getPrototype = __webpack_require__(117),\n isObjectLike = __webpack_require__(14);\n\n/** `Object#toString` result references. */\nvar objectTag = '[object Object]';\n\n/** Used for built-in method references. */\nvar funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/** Used to infer the `Object` constructor. */\nvar objectCtorString = funcToString.call(Object);\n\n/**\n * Checks if `value` is a plain object, that is, an object created by the\n * `Object` constructor or one with a `[[Prototype]]` of `null`.\n *\n * @static\n * @memberOf _\n * @since 0.8.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * _.isPlainObject(new Foo);\n * // => false\n *\n * _.isPlainObject([1, 2, 3]);\n * // => false\n *\n * _.isPlainObject({ 'x': 0, 'y': 0 });\n * // => true\n *\n * _.isPlainObject(Object.create(null));\n * // => true\n */\nfunction isPlainObject(value) {\n if (!isObjectLike(value) || baseGetTag(value) != objectTag) {\n return false;\n }\n var proto = getPrototype(value);\n if (proto === null) {\n return true;\n }\n var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;\n return typeof Ctor == 'function' && Ctor instanceof Ctor &&\n funcToString.call(Ctor) == objectCtorString;\n}\n\nmodule.exports = isPlainObject;\n\n\n/***/ }),\n/* 136 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isArray = __webpack_require__(10),\n isSymbol = __webpack_require__(70);\n\n/** Used to match property names within property paths. */\nvar reIsDeepProp = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n reIsPlainProp = /^\\w*$/;\n\n/**\n * Checks if `value` is a property name and not a property path.\n *\n * @private\n * @param {*} value The value to check.\n * @param {Object} [object] The object to query keys on.\n * @returns {boolean} Returns `true` if `value` is a property name, else `false`.\n */\nfunction isKey(value, object) {\n if (isArray(value)) {\n return false;\n }\n var type = typeof value;\n if (type == 'number' || type == 'symbol' || type == 'boolean' ||\n value == null || isSymbol(value)) {\n return true;\n }\n return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||\n (object != null && value in Object(object));\n}\n\nmodule.exports = isKey;\n\n\n/***/ }),\n/* 137 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar flatten = __webpack_require__(507),\n overRest = __webpack_require__(190),\n setToString = __webpack_require__(121);\n\n/**\n * A specialized version of `baseRest` which flattens the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\nfunction flatRest(func) {\n return setToString(overRest(func, undefined, flatten), func + '');\n}\n\nmodule.exports = flatRest;\n\n\n/***/ }),\n/* 138 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst urlUtil = __webpack_require__(237);\n\nconst stripTrailingSlash = url => url.replace(/\\/$/, '');\n\nconst prependProxyToUrlIfNeeded = (url, proxyUrl) => {\n if (!proxyUrl) return url;\n const proxyUrlParsed = urlUtil.parse(proxyUrl);\n const urlParsed = urlUtil.parse(url);\n if (!proxyUrlParsed.protocol || !urlParsed.protocol) return url;\n const isSsl = proxyUrlParsed.protocol.match(/^https/);\n const isWs = urlParsed.protocol.match(/^ws/i);\n const protocol = (isWs ? 'ws' : 'http') + (isSsl ? 's' : '');\n const targetUrl = stripTrailingSlash(`${protocol}://${proxyUrlParsed.host}${proxyUrlParsed.pathname}`);\n const targetPath = stripTrailingSlash(`${urlParsed.host}${urlParsed.pathname}${urlParsed.search || ''}`);\n return `${targetUrl}/${targetPath}`;\n};\n\nmodule.exports = {\n prependProxyToUrlIfNeeded\n};\n\n/***/ }),\n/* 139 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/* eslint-disable global-require */\nconst sessionObjects = __webpack_require__(23);\n\nconst otError = __webpack_require__(11);\n\nconst Errors = __webpack_require__(6);\n\nconst ExceptionCodes = __webpack_require__(9);\n\nconst OTErrorClass = __webpack_require__(20);\n\nlet proxyUrl;\nconst errorMessages = {\n SET_PROXY_URL_TIMING_ERROR: 'The proxyUrl was set after a session or publisher was created.',\n PROXY_URL_ALREADY_SET_ERROR: 'The proxyUrl has already been set. Setting it again will not have any effect.'\n};\n/**\n * Sets the URL of the IP proxy server. With the IP proxy feature, the client\n * routes all internet traffic (except for media streams) via your proxy server.\n * \n * You must call this method before calling any other OpenTok methods.\n * This ensures that the proxy server is used for OpenTok traffic. Otherwise,\n * the call fails, and the OT
object dispatches an\n * exception
event. The OT
object also dispatches an\n * exception
event if you call this method after having\n * previously called it.\n *
\n * This is available as an add-on feature. For more information, see the\n * IP proxy developer\n * guide.\n *\n * @method OT.setProxyUrl\n * @memberof OT\n */\n\nmodule.exports = {\n errorMessages,\n setProxyUrl: url => {\n // This is necessary to break a circular dependancy, but seems hacky and should be cleaned up\n const AnalyticsHelper = __webpack_require__(46);\n\n const apiKey = __webpack_require__(71);\n\n const analytics = new AnalyticsHelper();\n const hasSessionInitalized = sessionObjects.sessions.length() > 0;\n const hasPublisherInitalized = sessionObjects.publishers.length() > 0;\n let sessionId;\n\n if (hasSessionInitalized) {\n const connectedSession = sessionObjects.sessions.find(session => session.currentState === 'connected');\n sessionId = connectedSession ? connectedSession.sessionId : sessionObjects.sessions.find().sessionId;\n } else {\n sessionId = '';\n }\n\n const target = {\n sessionId,\n apiKey: apiKey.value\n };\n\n if (hasPublisherInitalized || hasSessionInitalized) {\n const error = otError()(Errors.SET_PROXY_URL_TIMING_ERROR, new Error(errorMessages.SET_PROXY_URL_TIMING_ERROR), ExceptionCodes.SET_PROXY_URL_TIMING_ERROR);\n OTErrorClass.handleJsException({\n error,\n target,\n analytics\n });\n } else if (proxyUrl) {\n const error = otError()(Errors.PROXY_URL_ALREADY_SET_ERROR, new Error(errorMessages.PROXY_URL_ALREADY_SET_ERROR), ExceptionCodes.PROXY_URL_ALREADY_SET_ERROR);\n OTErrorClass.handleJsException({\n error,\n target,\n analytics\n });\n } else {\n // eslint-disable-next-line no-param-reassign\n proxyUrl = url;\n }\n },\n getProxyUrl: () => proxyUrl,\n // this method should only be exposed to tests\n clearProxyUrl: () => {\n proxyUrl = null;\n }\n};\n\n/***/ }),\n/* 140 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseRest = __webpack_require__(87),\n eq = __webpack_require__(50),\n isIterateeCall = __webpack_require__(122),\n keysIn = __webpack_require__(51);\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Assigns own and inherited enumerable string keyed properties of source\n * objects to the destination object for all destination properties that\n * resolve to `undefined`. Source objects are applied from left to right.\n * Once a property is set, additional values of the same property are ignored.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.defaultsDeep\n * @example\n *\n * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\nvar defaults = baseRest(function(object, sources) {\n object = Object(object);\n\n var index = -1;\n var length = sources.length;\n var guard = length > 2 ? sources[2] : undefined;\n\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n length = 1;\n }\n\n while (++index < length) {\n var source = sources[index];\n var props = keysIn(source);\n var propsIndex = -1;\n var propsLength = props.length;\n\n while (++propsIndex < propsLength) {\n var key = props[propsIndex];\n var value = object[key];\n\n if (value === undefined ||\n (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) {\n object[key] = source[key];\n }\n }\n }\n\n return object;\n});\n\nmodule.exports = defaults;\n\n\n/***/ }),\n/* 141 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _createClass2 = _interopRequireDefault(__webpack_require__(43));\n\n/* eslint-disable no-underscore-dangle */\nconst _get = __webpack_require__(55);\n\nconst cloneDeep = __webpack_require__(54);\n/**\n * Provides an interface around the raw session info from Anvil\n *\n * @class SessionInfo\n * @param {Object} rawSessionInfo The raw session info from Anvil\n *\n */\n\n\nlet SessionInfo = /*#__PURE__*/function () {\n function SessionInfo(rawSessionInfo) {\n if (rawSessionInfo === void 0) {\n rawSessionInfo = {};\n }\n\n Object.defineProperty(this, '_rawSessionInfo', {\n value: cloneDeep(rawSessionInfo)\n });\n }\n /**\n * @type {String}\n * @readonly\n */\n\n\n (0, _createClass2.default)(SessionInfo, [{\n key: \"sessionId\",\n get: function get() {\n return this._rawSessionInfo.session_id;\n }\n /**\n * @type {String}\n * @readonly\n */\n\n }, {\n key: \"partnerId\",\n get: function get() {\n return this._rawSessionInfo.partner_id;\n }\n /**\n * @type {String}\n * @readonly\n */\n\n }, {\n key: \"messagingServer\",\n get: function get() {\n return this._rawSessionInfo.messaging_server_url;\n }\n /**\n * @type {String}\n * @readonly\n */\n\n }, {\n key: \"mediaServerName\",\n get: function get() {\n return this._rawSessionInfo.media_server_hostname;\n }\n /**\n * @type {String}\n * @readonly\n */\n\n }, {\n key: \"messagingURL\",\n get: function get() {\n return this._rawSessionInfo.messaging_url;\n }\n /**\n * @type {String}\n * @readonly\n */\n\n }, {\n key: \"symphonyAddress\",\n get: function get() {\n return this._rawSessionInfo.symphony_address;\n }\n /**\n * @type {Array}\n * @readonly\n */\n\n }, {\n key: \"iceServers\",\n get: function get() {\n return this._rawSessionInfo.ice_servers;\n }\n /**\n * Simulcast is tri-state:\n * * true - simulcast is on for this session\n * * false - simulcast is off for this session\n * * undefined - the developer can choose\n * @type {Boolean|Undefined}\n * @readonly\n */\n\n }, {\n key: \"simulcast\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.simulcast');\n }\n /**\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"reconnection\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.reconnection', false);\n }\n /**\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"renegotiation\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.renegotiation', false);\n }\n /**\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"p2pEnabled\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.p2p.preference.value') === 'enabled';\n }\n /**\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"hybridSession\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.hybridSession', false);\n }\n /**\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"isAdaptiveEnabled\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.p2p.preference.value') === 'adaptive';\n }\n /**\n * If this contains a valid codec, it will be prioritized\n *\n * @type {String}\n * @readonly\n */\n\n }, {\n key: \"priorityVideoCodec\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.priorityVideoCodec', '');\n }\n /**\n * Is true if H264 codec is enabled\n *\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"h264\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.h264', true);\n }\n /**\n * Is true if VP8 codec is enabled\n *\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"vp8\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.vp8', true);\n }\n /**\n * Is true if VP9 codec is enabled\n *\n * @type {Boolean}\n * @readonly\n */\n\n }, {\n key: \"vp9\",\n get: function get() {\n return _get(this._rawSessionInfo, 'properties.vp9', true);\n }\n /**\n * clientCandidates can be \"relay\". Other values are unknown.\n *\n * @type {String|Undefined}\n * @readonly\n */\n\n }, {\n key: \"clientCandidates\",\n get: function get() {\n const rawValue = _get(this._rawSessionInfo, 'properties.clientCandidates');\n\n return rawValue === 'relayed' ? 'relay' : rawValue;\n }\n }]);\n return SessionInfo;\n}();\n\nmodule.exports = SessionInfo;\n\n/***/ }),\n/* 142 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst extendES5Native = __webpack_require__(539);\n\nconst webrtcAdapter = __webpack_require__(540);\n\nconst logging = __webpack_require__(0)('createWindowMock');\n\nconst env = __webpack_require__(2);\n\nconst windowKeys = [{\n key: 'location',\n type: 'object'\n}, {\n key: 'setTimeout',\n type: 'function'\n}, {\n key: 'requestAnimationFrame',\n type: 'function'\n}, {\n key: 'URL',\n type: 'function'\n}, {\n key: 'MediaStream',\n type: 'class'\n}, {\n key: 'webkitMediaStream',\n type: 'class'\n}, {\n key: 'RTCIceCandidate',\n type: 'class'\n}, {\n key: 'mozRTCIceCandidate',\n type: 'class'\n}, {\n key: 'RTCSessionDescription',\n type: 'class'\n}, {\n key: 'mozRTCSessionDescription',\n type: 'class'\n}, {\n key: 'RTCIceGatherer',\n type: 'class'\n}, {\n key: 'RTCIceTransport',\n type: 'class'\n}, {\n key: 'RTCDtlsTransport',\n type: 'class'\n}, {\n key: 'RTCSctpTransport',\n type: 'class'\n}, {\n key: 'RTCRtpReceiver',\n type: 'class'\n}, {\n key: 'HTMLMediaElement',\n type: 'class'\n}, {\n key: 'RTCPeerConnection',\n type: 'class'\n}, {\n key: 'webkitRTCPeerConnection',\n type: 'class'\n}, {\n key: 'mozRTCPeerConnection',\n type: 'class'\n}, {\n key: 'MediaStreamTrack',\n type: 'class'\n}, {\n key: 'RTCRtpSender',\n type: 'class'\n}, {\n key: 'RTCTrackEvent',\n type: 'class'\n}, {\n key: 'RTCTransceiver',\n type: 'class'\n}, {\n key: 'RTCDtmfSender',\n type: 'class'\n}, {\n key: 'RTCDTMFSender',\n type: 'class'\n}, {\n key: 'MediaStreamTrackEvent',\n type: 'class'\n}];\nconst navigatorKeys = [{\n key: 'userAgent',\n type: 'string'\n}, {\n key: 'getUserMedia',\n type: 'function'\n}, {\n key: 'getDisplayMedia',\n type: 'function'\n}, {\n key: 'webkitGetUserMedia',\n type: 'function'\n}, {\n key: 'mozGetUserMedia',\n type: 'function'\n}];\nconst mediaDevicesKeys = [{\n key: 'getUserMedia',\n type: 'function'\n}, {\n key: 'getDisplayMedia',\n type: 'function'\n}, {\n key: 'enumerateDevices',\n type: 'function'\n}, {\n key: 'getSupportedConstraints',\n type: 'function'\n}, {\n key: 'addEventListener',\n type: 'function'\n}, {\n key: 'removeEventListener',\n type: 'function'\n}];\n\nconst bindWithStaticProperties = (context, func) => {\n if (typeof func !== 'function') {\n logging.warn('Non-function passed into bindWithStaticProperties()');\n return func;\n }\n\n const bound = func.bind(context);\n Object.keys(func).forEach(key => {\n bound[key] = func[key];\n });\n return bound;\n};\n\nconst extendParentClass = ParentClass => {\n try {\n // The below __proto__ hack is so we can extend RTCPeerConnection in Firefox and Safari\n // See: https://bugs.webkit.org/show_bug.cgi?id=172867#c6\n // eslint-disable-next-line no-eval\n const ChildClass = eval(`\n \"use strict\"; // Chrome<49 requires strict mode\n\n const getOwnProperties = (target) => {\n const properties = {};\n Object.getOwnPropertyNames(target).forEach((key) => {\n properties[key] = Object.getOwnPropertyDescriptor(target, key);\n });\n return properties;\n };\n\n class ChildClass extends ParentClass {\n constructor(...args) {\n super(...args);\n try {\n this.__proto__ = ChildClass.prototype;\n } catch (e) {}\n }\n };\n\n Object.defineProperties(ChildClass.prototype, getOwnProperties(ParentClass.prototype));\n Object.keys(ParentClass)\n .forEach((staticKey) => {\n ChildClass[staticKey] = ParentClass[staticKey];\n });\n\n ChildClass; // Ensure the final statement is returned in FF\n `);\n\n if (ChildClass && ChildClass.prototype instanceof ParentClass) {\n return ChildClass;\n }\n } catch (e) {// Failed to use ES6 class or eval under CSP\n }\n\n return null;\n};\n\nconst canUseES6Class = window => {\n const RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;\n const PC = extendParentClass(RTCPeerConnection);\n\n if (PC) {\n try {\n PC.prototype.foo = 'bar';\n const instance = new PC({\n iceServers: []\n });\n const result = instance.foo === 'bar';\n\n try {\n instance.close();\n } catch (e) {} // eslint-disable-line no-empty\n\n\n return result;\n } catch (e) {// Failed to instantiate the subclass\n }\n }\n\n return false;\n};\n\nconst extendClass = (ParentClass, label, canUseClass) => {\n if (typeof ParentClass !== 'function') {\n logging.warn('Non-function passed into extendClass()');\n return ParentClass;\n }\n\n let result = canUseClass && extendParentClass(ParentClass);\n\n if (!result) {\n result = extendES5Native(ParentClass);\n }\n\n return result;\n};\n\nconst getCopyProperties = canUseClass => (target, source, keys) => {\n keys.forEach((_ref) => {\n let key = _ref.key,\n type = _ref.type;\n let value = source[key];\n\n if (value !== undefined) {\n switch (type) {\n case 'function':\n value = bindWithStaticProperties(source, value);\n break;\n\n case 'class':\n value = extendClass(value, key, canUseClass);\n break;\n\n case 'string':\n case 'object':\n break;\n\n default:\n throw new Error(`Invalid type of window key: ${type}`);\n }\n\n target[key] = value; // eslint-disable-line no-param-reassign\n }\n });\n};\n\nconst cloneWindow = window => {\n const windowMock = {};\n const copyProperties = getCopyProperties(canUseES6Class(window));\n copyProperties(windowMock, window, windowKeys);\n\n if (window.navigator !== undefined) {\n windowMock.navigator = {};\n copyProperties(windowMock.navigator, window.navigator, navigatorKeys);\n\n if (window.navigator.mediaDevices !== undefined) {\n windowMock.navigator.mediaDevices = {};\n copyProperties(windowMock.navigator.mediaDevices, window.navigator.mediaDevices, mediaDevicesKeys);\n }\n }\n\n return windowMock;\n};\n\nmodule.exports = function createWindowMock(window) {\n // We avoid shimming twice because adapter is not idempotent.\n // Unfortunately checking for window.adapter can be used to detect\n // adapter.js but not adapter_no_global.js, there is no reliable\n // way of detecting it.\n if (window.adapter !== undefined || env.isLegacyEdge) {\n return window;\n }\n\n const windowMock = cloneWindow(window);\n webrtcAdapter({\n window: windowMock\n });\n return windowMock;\n};\n\n/***/ }),\n/* 143 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/*\n * Transforms a raw audio level to produce a \"smoother\" animation when using displaying the\n * audio level. This transformer is state-full because it needs to keep the previous average\n * value of the signal for filtering.\n *\n * It applies a low pass filter to get rid of level jumps and apply a log scale.\n *\n * @constructor\n */\nmodule.exports = function AudioLevelTransformer() {\n let averageAudioLevel = null;\n /*\n *\n * @param {number} audioLevel a level in the [0,1] range\n * @returns {number} a level in the [0,1] range transformed\n */\n\n this.transform = audioLevel => {\n if (averageAudioLevel === null || audioLevel >= averageAudioLevel) {\n averageAudioLevel = audioLevel;\n } else {\n // a simple low pass filter with a smoothing of 70\n averageAudioLevel = audioLevel * 0.3 + averageAudioLevel * 0.7;\n } // 1.5 scaling to map -30-0 dBm range to [0,1]\n\n\n const logScaled = Math.log(averageAudioLevel) / Math.LN10 / 1.5 + 1;\n return Math.min(Math.max(logScaled, 0), 1);\n };\n};\n\n/***/ }),\n/* 144 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/* eslint-disable no-underscore-dangle */\nconst eventing = __webpack_require__(5);\n\nconst values = obj => Object.keys(obj).map(key => obj[key]);\n\nconst assign = __webpack_require__(7); // Manages N Chrome elements\n\n\nmodule.exports = function Chrome(properties) {\n const _widgets = {}; // Private helper function\n\n const _set = (name, widget) => {\n assign(widget, {\n parent: this\n });\n widget.appendTo(properties.parent);\n _widgets[name] = widget;\n this[name] = widget;\n };\n\n if (!properties.parent) {\n // @todo raise an exception\n return;\n }\n\n eventing(this);\n\n this.destroy = () => {\n this.off();\n this.hideWhileLoading();\n values(_widgets).forEach(widget => widget.destroy());\n };\n\n this.showAfterLoading = () => {\n values(_widgets).forEach(widget => widget.showAfterLoading());\n };\n\n this.hideWhileLoading = () => {\n values(_widgets).forEach(widget => widget.hideWhileLoading());\n }; // Adds the widget to the chrome and to the DOM. Also creates a accessor\n // property for it on the chrome.\n //\n // @example\n // chrome.set('foo', new FooWidget());\n // chrome.foo.setDisplayMode('on');\n //\n // @example\n // chrome.set({\n // foo: new FooWidget(),\n // bar: new BarWidget()\n // });\n // chrome.foo.setDisplayMode('on');\n //\n\n\n this.set = (widgetName, widget) => {\n if (typeof widgetName === 'string' && widget) {\n _set(widgetName, widget);\n } else {\n Object.keys(widgetName).forEach(name => {\n _set(name, widgetName[name]);\n });\n }\n\n return this;\n };\n};\n\n/***/ }),\n/* 145 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _defineProperty2 = _interopRequireDefault(__webpack_require__(16));\n\nvar _temp;\n\n/* eslint-disable no-underscore-dangle */\n\n/*\n * An AudioContext
based audio level sampler. It returns the maximum value in the\n * last 1024 samples.\n *\n * It is worth noting that the remote MediaStream
audio analysis is currently only\n * available in FF.\n *\n * This implementation gracefully handles the case where the MediaStream
has not\n * been set yet by returning a null
value until the stream is set. It is up to the\n * call site to decide what to do with this value (most likely ignore it and retry later).\n *\n * @constructor\n * @param {AudioContext} audioContext an audio context instance to get an analyser node\n */\nmodule.exports = (_temp = /*#__PURE__*/function () {\n function WebAudioAudioLevelSampler(audioContext) {\n (0, _defineProperty2.default)(this, \"_analyser\", null);\n (0, _defineProperty2.default)(this, \"_timeDomainData\", null);\n (0, _defineProperty2.default)(this, \"_sourceNode\", null);\n this._audioContext = audioContext;\n }\n\n var _proto = WebAudioAudioLevelSampler.prototype;\n\n _proto._initAudioAnalyser = function _initAudioAnalyser(mediaStream) {\n this._analyser = this._audioContext.createAnalyser();\n this._sourceNode = this._audioContext.createMediaStreamSource(mediaStream);\n\n this._sourceNode.connect(this._analyser);\n\n this._timeDomainData = new Uint8Array(this._analyser.frequencyBinCount);\n };\n\n _proto.webRTCStream = function webRTCStream(mediaStream) {\n if (this._sourceNode) {\n this._sourceNode.disconnect(this._analyser);\n }\n\n if (this._audioContext.state === 'suspended') {\n this._audioContext.resume();\n }\n\n this._initAudioAnalyser(mediaStream);\n };\n\n _proto.destroy = function destroy() {\n if (this._sourceNode) {\n this._sourceNode.disconnect(this._analyser);\n }\n\n this._timeDomainData = null;\n };\n\n _proto.sample = function sample(done) {\n if (typeof done === 'function') {\n throw new Error('sample no longer takes a callback');\n }\n\n if (this._analyser && this._timeDomainData) {\n this._analyser.getByteTimeDomainData(this._timeDomainData); // varies from 0 to 255\n\n\n let max = 0;\n\n for (let idx = this._timeDomainData.length - 1; idx >= 0; idx -= 1) {\n max = Math.max(max, Math.abs(this._timeDomainData[idx] - 128));\n } // normalize the collected level to match the range delivered by\n // the getStats' audioOutputLevel\n\n\n return max / 128;\n }\n\n return null;\n };\n\n return WebAudioAudioLevelSampler;\n}(), _temp);\n\n/***/ }),\n/* 146 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-use-before-define */\nconst domState = __webpack_require__(551);\n\nconst eventNames = __webpack_require__(24);\n\nconst eventing = __webpack_require__(5);\n\nconst Events = __webpack_require__(21)(); // Helper to synchronise several startup tasks and then dispatch a unified\n// 'envLoaded' event.\n\n\nfunction EnvironmentLoader() {\n const environmentLoader = this;\n eventing(environmentLoader);\n\n function isReady() {\n return domState.isDomLoaded() && !domState.isDomUnloaded();\n }\n\n function onLoaded() {\n if (isReady()) {\n environmentLoader.dispatchEvent(new Events.EnvLoadedEvent(eventNames.ENV_LOADED));\n }\n }\n\n function onDomReady() {\n domState.whenUnloaded.then(onDomUnload);\n onLoaded();\n }\n\n function onDomUnload() {\n environmentLoader.dispatchEvent(new Events.EnvLoadedEvent(eventNames.ENV_UNLOADED));\n }\n\n domState.whenLoaded.then(onDomReady);\n\n this.onLoad = function (cb, context) {\n if (isReady()) {\n cb.call(context);\n return;\n }\n\n environmentLoader.on(eventNames.ENV_LOADED, cb, context);\n };\n\n this.onUnload = function (cb, context) {\n if (this.isUnloaded()) {\n cb.call(context);\n return;\n }\n\n environmentLoader.on(eventNames.ENV_UNLOADED, cb, context);\n };\n\n this.isUnloaded = function () {\n return domState.isDomUnloaded();\n };\n}\n\nmodule.exports = new EnvironmentLoader();\n\n/***/ }),\n/* 147 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseMerge = __webpack_require__(552),\n createAssigner = __webpack_require__(189);\n\n/**\n * This method is like `_.assign` except that it recursively merges own and\n * inherited enumerable string keyed properties of source objects into the\n * destination object. Source properties that resolve to `undefined` are\n * skipped if a destination value exists. Array and plain object properties\n * are merged recursively. Other objects and value types are overridden by\n * assignment. Source objects are applied from left to right. Subsequent\n * sources overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {\n * 'a': [{ 'b': 2 }, { 'd': 4 }]\n * };\n *\n * var other = {\n * 'a': [{ 'c': 3 }, { 'e': 5 }]\n * };\n *\n * _.merge(object, other);\n * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }\n */\nvar merge = createAssigner(function(object, source, srcIndex) {\n baseMerge(object, source, srcIndex);\n});\n\nmodule.exports = merge;\n\n\n/***/ }),\n/* 148 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar objectAssign = __webpack_require__(556);\n\n// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js\n// original notice:\n\n/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\nfunction compare(a, b) {\n if (a === b) {\n return 0;\n }\n\n var x = a.length;\n var y = b.length;\n\n for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i];\n y = b[i];\n break;\n }\n }\n\n if (x < y) {\n return -1;\n }\n if (y < x) {\n return 1;\n }\n return 0;\n}\nfunction isBuffer(b) {\n if ((typeof window !== undefined ? window : global).Buffer && typeof (typeof window !== undefined ? window : global).Buffer.isBuffer === 'function') {\n return (typeof window !== undefined ? window : global).Buffer.isBuffer(b);\n }\n return !!(b != null && b._isBuffer);\n}\n\n// based on node assert, original notice:\n// NB: The URL to the CommonJS spec is kept just for tradition.\n// node-assert has evolved a lot since then, both in API and behavior.\n\n// http://wiki.commonjs.org/wiki/Unit_Testing/1.0\n//\n// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!\n//\n// Originally from narwhal.js (http://narwhaljs.org)\n// Copyright (c) 2009 Thomas Robinson <280north.com>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the 'Software'), to\n// deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n// sell copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nvar util = __webpack_require__(557);\nvar hasOwn = Object.prototype.hasOwnProperty;\nvar pSlice = Array.prototype.slice;\nvar functionsHaveNames = (function () {\n return function foo() {}.name === 'foo';\n}());\nfunction pToString (obj) {\n return Object.prototype.toString.call(obj);\n}\nfunction isView(arrbuf) {\n if (isBuffer(arrbuf)) {\n return false;\n }\n if (typeof (typeof window !== undefined ? window : global).ArrayBuffer !== 'function') {\n return false;\n }\n if (typeof ArrayBuffer.isView === 'function') {\n return ArrayBuffer.isView(arrbuf);\n }\n if (!arrbuf) {\n return false;\n }\n if (arrbuf instanceof DataView) {\n return true;\n }\n if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) {\n return true;\n }\n return false;\n}\n// 1. The assert module provides functions that throw\n// AssertionError's when particular conditions are not met. The\n// assert module must conform to the following interface.\n\nvar assert = module.exports = ok;\n\n// 2. The AssertionError is defined in assert.\n// new assert.AssertionError({ message: message,\n// actual: actual,\n// expected: expected })\n\nvar regex = /\\s*function\\s+([^\\(\\s]*)\\s*/;\n// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js\nfunction getName(func) {\n if (!util.isFunction(func)) {\n return;\n }\n if (functionsHaveNames) {\n return func.name;\n }\n var str = func.toString();\n var match = str.match(regex);\n return match && match[1];\n}\nassert.AssertionError = function AssertionError(options) {\n this.name = 'AssertionError';\n this.actual = options.actual;\n this.expected = options.expected;\n this.operator = options.operator;\n if (options.message) {\n this.message = options.message;\n this.generatedMessage = false;\n } else {\n this.message = getMessage(this);\n this.generatedMessage = true;\n }\n var stackStartFunction = options.stackStartFunction || fail;\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, stackStartFunction);\n } else {\n // non v8 browsers so we can have a stacktrace\n var err = new Error();\n if (err.stack) {\n var out = err.stack;\n\n // try to strip useless frames\n var fn_name = getName(stackStartFunction);\n var idx = out.indexOf('\\n' + fn_name);\n if (idx >= 0) {\n // once we have located the function frame\n // we need to strip out everything before it (and its line)\n var next_line = out.indexOf('\\n', idx + 1);\n out = out.substring(next_line + 1);\n }\n\n this.stack = out;\n }\n }\n};\n\n// assert.AssertionError instanceof Error\nutil.inherits(assert.AssertionError, Error);\n\nfunction truncate(s, n) {\n if (typeof s === 'string') {\n return s.length < n ? s : s.slice(0, n);\n } else {\n return s;\n }\n}\nfunction inspect(something) {\n if (functionsHaveNames || !util.isFunction(something)) {\n return util.inspect(something);\n }\n var rawname = getName(something);\n var name = rawname ? ': ' + rawname : '';\n return '[Function' + name + ']';\n}\nfunction getMessage(self) {\n return truncate(inspect(self.actual), 128) + ' ' +\n self.operator + ' ' +\n truncate(inspect(self.expected), 128);\n}\n\n// At present only the three keys mentioned above are used and\n// understood by the spec. Implementations or sub modules can pass\n// other keys to the AssertionError's constructor - they will be\n// ignored.\n\n// 3. All of the following functions must throw an AssertionError\n// when a corresponding condition is not met, with a message that\n// may be undefined if not provided. All assertion methods provide\n// both the actual and expected values to the assertion error for\n// display purposes.\n\nfunction fail(actual, expected, message, operator, stackStartFunction) {\n throw new assert.AssertionError({\n message: message,\n actual: actual,\n expected: expected,\n operator: operator,\n stackStartFunction: stackStartFunction\n });\n}\n\n// EXTENSION! allows for well behaved errors defined elsewhere.\nassert.fail = fail;\n\n// 4. Pure assertion tests whether a value is truthy, as determined\n// by !!guard.\n// assert.ok(guard, message_opt);\n// This statement is equivalent to assert.equal(true, !!guard,\n// message_opt);. To test strictly for the value true, use\n// assert.strictEqual(true, guard, message_opt);.\n\nfunction ok(value, message) {\n if (!value) fail(value, true, message, '==', assert.ok);\n}\nassert.ok = ok;\n\n// 5. The equality assertion tests shallow, coercive equality with\n// ==.\n// assert.equal(actual, expected, message_opt);\n\nassert.equal = function equal(actual, expected, message) {\n if (actual != expected) fail(actual, expected, message, '==', assert.equal);\n};\n\n// 6. The non-equality assertion tests for whether two objects are not equal\n// with != assert.notEqual(actual, expected, message_opt);\n\nassert.notEqual = function notEqual(actual, expected, message) {\n if (actual == expected) {\n fail(actual, expected, message, '!=', assert.notEqual);\n }\n};\n\n// 7. The equivalence assertion tests a deep equality relation.\n// assert.deepEqual(actual, expected, message_opt);\n\nassert.deepEqual = function deepEqual(actual, expected, message) {\n if (!_deepEqual(actual, expected, false)) {\n fail(actual, expected, message, 'deepEqual', assert.deepEqual);\n }\n};\n\nassert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {\n if (!_deepEqual(actual, expected, true)) {\n fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);\n }\n};\n\nfunction _deepEqual(actual, expected, strict, memos) {\n // 7.1. All identical values are equivalent, as determined by ===.\n if (actual === expected) {\n return true;\n } else if (isBuffer(actual) && isBuffer(expected)) {\n return compare(actual, expected) === 0;\n\n // 7.2. If the expected value is a Date object, the actual value is\n // equivalent if it is also a Date object that refers to the same time.\n } else if (util.isDate(actual) && util.isDate(expected)) {\n return actual.getTime() === expected.getTime();\n\n // 7.3 If the expected value is a RegExp object, the actual value is\n // equivalent if it is also a RegExp object with the same source and\n // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).\n } else if (util.isRegExp(actual) && util.isRegExp(expected)) {\n return actual.source === expected.source &&\n actual.global === expected.global &&\n actual.multiline === expected.multiline &&\n actual.lastIndex === expected.lastIndex &&\n actual.ignoreCase === expected.ignoreCase;\n\n // 7.4. Other pairs that do not both pass typeof value == 'object',\n // equivalence is determined by ==.\n } else if ((actual === null || typeof actual !== 'object') &&\n (expected === null || typeof expected !== 'object')) {\n return strict ? actual === expected : actual == expected;\n\n // If both values are instances of typed arrays, wrap their underlying\n // ArrayBuffers in a Buffer each to increase performance\n // This optimization requires the arrays to have the same type as checked by\n // Object.prototype.toString (aka pToString). Never perform binary\n // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their\n // bit patterns are not identical.\n } else if (isView(actual) && isView(expected) &&\n pToString(actual) === pToString(expected) &&\n !(actual instanceof Float32Array ||\n actual instanceof Float64Array)) {\n return compare(new Uint8Array(actual.buffer),\n new Uint8Array(expected.buffer)) === 0;\n\n // 7.5 For all other Object pairs, including Array objects, equivalence is\n // determined by having the same number of owned properties (as verified\n // with Object.prototype.hasOwnProperty.call), the same set of keys\n // (although not necessarily the same order), equivalent values for every\n // corresponding key, and an identical 'prototype' property. Note: this\n // accounts for both named and indexed properties on Arrays.\n } else if (isBuffer(actual) !== isBuffer(expected)) {\n return false;\n } else {\n memos = memos || {actual: [], expected: []};\n\n var actualIndex = memos.actual.indexOf(actual);\n if (actualIndex !== -1) {\n if (actualIndex === memos.expected.indexOf(expected)) {\n return true;\n }\n }\n\n memos.actual.push(actual);\n memos.expected.push(expected);\n\n return objEquiv(actual, expected, strict, memos);\n }\n}\n\nfunction isArguments(object) {\n return Object.prototype.toString.call(object) == '[object Arguments]';\n}\n\nfunction objEquiv(a, b, strict, actualVisitedObjects) {\n if (a === null || a === undefined || b === null || b === undefined)\n return false;\n // if one is a primitive, the other must be same\n if (util.isPrimitive(a) || util.isPrimitive(b))\n return a === b;\n if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))\n return false;\n var aIsArgs = isArguments(a);\n var bIsArgs = isArguments(b);\n if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))\n return false;\n if (aIsArgs) {\n a = pSlice.call(a);\n b = pSlice.call(b);\n return _deepEqual(a, b, strict);\n }\n var ka = objectKeys(a);\n var kb = objectKeys(b);\n var key, i;\n // having the same number of owned properties (keys incorporates\n // hasOwnProperty)\n if (ka.length !== kb.length)\n return false;\n //the same set of keys (although not necessarily the same order),\n ka.sort();\n kb.sort();\n //~~~cheap key test\n for (i = ka.length - 1; i >= 0; i--) {\n if (ka[i] !== kb[i])\n return false;\n }\n //equivalent values for every corresponding key, and\n //~~~possibly expensive deep test\n for (i = ka.length - 1; i >= 0; i--) {\n key = ka[i];\n if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))\n return false;\n }\n return true;\n}\n\n// 8. The non-equivalence assertion tests for any deep inequality.\n// assert.notDeepEqual(actual, expected, message_opt);\n\nassert.notDeepEqual = function notDeepEqual(actual, expected, message) {\n if (_deepEqual(actual, expected, false)) {\n fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);\n }\n};\n\nassert.notDeepStrictEqual = notDeepStrictEqual;\nfunction notDeepStrictEqual(actual, expected, message) {\n if (_deepEqual(actual, expected, true)) {\n fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);\n }\n}\n\n\n// 9. The strict equality assertion tests strict equality, as determined by ===.\n// assert.strictEqual(actual, expected, message_opt);\n\nassert.strictEqual = function strictEqual(actual, expected, message) {\n if (actual !== expected) {\n fail(actual, expected, message, '===', assert.strictEqual);\n }\n};\n\n// 10. The strict non-equality assertion tests for strict inequality, as\n// determined by !==. assert.notStrictEqual(actual, expected, message_opt);\n\nassert.notStrictEqual = function notStrictEqual(actual, expected, message) {\n if (actual === expected) {\n fail(actual, expected, message, '!==', assert.notStrictEqual);\n }\n};\n\nfunction expectedException(actual, expected) {\n if (!actual || !expected) {\n return false;\n }\n\n if (Object.prototype.toString.call(expected) == '[object RegExp]') {\n return expected.test(actual);\n }\n\n try {\n if (actual instanceof expected) {\n return true;\n }\n } catch (e) {\n // Ignore. The instanceof check doesn't work for arrow functions.\n }\n\n if (Error.isPrototypeOf(expected)) {\n return false;\n }\n\n return expected.call({}, actual) === true;\n}\n\nfunction _tryBlock(block) {\n var error;\n try {\n block();\n } catch (e) {\n error = e;\n }\n return error;\n}\n\nfunction _throws(shouldThrow, block, expected, message) {\n var actual;\n\n if (typeof block !== 'function') {\n throw new TypeError('\"block\" argument must be a function');\n }\n\n if (typeof expected === 'string') {\n message = expected;\n expected = null;\n }\n\n actual = _tryBlock(block);\n\n message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +\n (message ? ' ' + message : '.');\n\n if (shouldThrow && !actual) {\n fail(actual, expected, 'Missing expected exception' + message);\n }\n\n var userProvidedMessage = typeof message === 'string';\n var isUnwantedException = !shouldThrow && util.isError(actual);\n var isUnexpectedException = !shouldThrow && actual && !expected;\n\n if ((isUnwantedException &&\n userProvidedMessage &&\n expectedException(actual, expected)) ||\n isUnexpectedException) {\n fail(actual, expected, 'Got unwanted exception' + message);\n }\n\n if ((shouldThrow && actual && expected &&\n !expectedException(actual, expected)) || (!shouldThrow && actual)) {\n throw actual;\n }\n}\n\n// 11. Expected to throw an error:\n// assert.throws(block, Error_opt, message_opt);\n\nassert.throws = function(block, /*optional*/error, /*optional*/message) {\n _throws(true, block, error, message);\n};\n\n// EXTENSION! This is annoying to write outside this module.\nassert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {\n _throws(false, block, error, message);\n};\n\nassert.ifError = function(err) { if (err) throw err; };\n\n// Expose a strict only variant of assert\nfunction strict(value, message) {\n if (!value) fail(value, true, message, '==', strict);\n}\nassert.strict = objectAssign(strict, assert, {\n equal: assert.strictEqual,\n deepEqual: assert.deepStrictEqual,\n notEqual: assert.notStrictEqual,\n notDeepEqual: assert.notDeepStrictEqual\n});\nassert.strict.strict = assert.strict;\n\nvar objectKeys = Object.keys || function (obj) {\n var keys = [];\n for (var key in obj) {\n if (hasOwn.call(obj, key)) keys.push(key);\n }\n return keys;\n};\n\n\n/***/ }),\n/* 149 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable max-len, no-restricted-syntax, no-prototype-builtins, no-continue */\nconst castToBoolean = __webpack_require__(266);\n\nconst eventing = __webpack_require__(5);\n\nconst logging = __webpack_require__(0)('StreamChannel');\n\nconst VideoOrientation = __webpack_require__(150)(); // id: String | mandatory | immutable\n// type: String {video/audio/data/...} | mandatory | immutable\n// active: Boolean | mandatory | mutable\n// orientation: Integer? | optional | mutable\n// frameRate: Float | optional | mutable\n// height: Integer | optional | mutable\n// width: Integer | optional | mutable\n// preferredFrameRate: Float | optional | mutable\n// preferredHeight: Integer | optional | mutable\n// preferredWidth: Integer | optional | mutable\n//\n\n\nmodule.exports = function StreamChannel(options) {\n this.id = options.id;\n this.type = options.type;\n this.active = castToBoolean(options.active);\n this.orientation = options.orientation || VideoOrientation.ROTATED_NORMAL;\n\n if (options.frameRate) {\n this.frameRate = parseFloat(options.frameRate);\n }\n\n if (options.preferredFrameRate) {\n this.preferredFrameRate = parseFloat(options.preferredFrameRate);\n }\n\n if (options.preferredWidth) {\n this.preferredWidth = parseInt(options.preferredWidth, 10);\n }\n\n if (options.preferredHeight) {\n this.preferredHeight = parseInt(options.preferredHeight, 10);\n }\n\n this.width = parseInt(options.width, 10);\n this.height = parseInt(options.height, 10); // The defaults are used for incoming streams from pre 2015Q1 release clients.\n\n this.source = options.source || 'camera';\n this.fitMode = options.fitMode || 'cover';\n eventing(this); // Returns true if a property was updated.\n\n this.update = function (attributes) {\n const videoDimensions = {};\n const oldVideoDimensions = {};\n\n for (const key in attributes) {\n if (!attributes.hasOwnProperty(key)) {\n continue;\n } // we shouldn't really read this before we know the key is valid\n\n\n const oldValue = this[key];\n\n switch (key) {\n case 'active':\n this.active = castToBoolean(attributes[key]);\n break;\n\n case 'disableWarning':\n this.disableWarning = castToBoolean(attributes[key]);\n break;\n\n case 'frameRate':\n this.frameRate = parseFloat(attributes[key], 10);\n break;\n\n case 'width':\n case 'height':\n this[key] = parseInt(attributes[key], 10);\n videoDimensions[key] = this[key];\n oldVideoDimensions[key] = oldValue;\n break;\n\n case 'orientation':\n this[key] = attributes[key];\n videoDimensions[key] = this[key];\n oldVideoDimensions[key] = oldValue;\n break;\n\n case 'fitMode':\n this[key] = attributes[key];\n break;\n\n case 'source':\n this[key] = attributes[key];\n break;\n\n default:\n logging.warn(`Tried to update unknown key ${key} on ${this.type} channel ${this.id}`);\n return false;\n }\n\n this.trigger('update', this, key, oldValue, this[key]);\n }\n\n if (Object.keys(videoDimensions).length) {\n // To make things easier for the public API, we broadcast videoDimensions changes,\n // which is an aggregate of width, height, and orientation changes.\n this.trigger('update', this, 'videoDimensions', oldVideoDimensions, videoDimensions);\n }\n\n return true;\n };\n};\n\n/***/ }),\n/* 150 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function videoOrientationFactory() {\n return {\n ROTATED_NORMAL: 0,\n ROTATED_LEFT: 270,\n ROTATED_RIGHT: 90,\n ROTATED_UPSIDE_DOWN: 180\n };\n};\n\n/***/ }),\n/* 151 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, no-underscore-dangle */\nconst fixBackgroundImageURI = __webpack_require__(572);\n\nconst logging = __webpack_require__(0)('StylableComponent');\n\nconst Style = __webpack_require__(573);\n\nconst styleHashLogFilter = __webpack_require__(574);\n/* Stylable Notes\n * Some bits are controlled by multiple flags, i.e. buttonDisplayMode and nameDisplayMode.\n * When there are multiple flags how is the final setting chosen?\n * When some style bits are set updates will need to be pushed through to the Chrome\n */\n// Mixes the StylableComponent behaviour into the +self+ object. It will\n// also set the default styles to +initialStyles+.\n//\n// @note This Mixin is dependent on OT.Eventing.\n//\n//\n// @example\n//\n// function SomeObject {\n// StylableComponent(this, {\n// name: 'SomeObject',\n// foo: 'bar'\n// });\n// }\n//\n// var obj = new SomeObject();\n// obj.getStyle('foo'); // => 'bar'\n// obj.setStyle('foo', 'baz')\n// obj.getStyle('foo'); // => 'baz'\n// obj.getStyle(); // => {name: 'SomeObject', foo: 'baz'}\n//\n\n\nmodule.exports = function StylableComponent(self, initialStyles, showControls, logSetStyleWithPayload) {\n if (!self.trigger) {\n throw new Error('OT.StylableComponent is dependent on the eventing mixin. ' + 'Ensure that this is included in the object before StylableComponent.');\n }\n\n logSetStyleWithPayload = logSetStyleWithPayload || function () {};\n\n let _readOnly = false; // Broadcast style changes as the styleValueChanged event\n\n const onStyleChange = function onStyleChange(key, value, oldValue) {\n if (oldValue) {\n self.trigger('styleValueChanged', key, value, oldValue);\n } else {\n self.trigger('styleValueChanged', key, value);\n }\n };\n\n if (showControls === false) {\n initialStyles = {\n buttonDisplayMode: 'off',\n nameDisplayMode: 'off',\n audioLevelDisplayMode: 'off',\n videoDisabledDisplayMode: 'off',\n audioBlockedDisplayMode: 'off'\n };\n _readOnly = true;\n logSetStyleWithPayload({\n showControls: false\n });\n }\n\n const _style = new Style(initialStyles, onStyleChange);\n /**\n * Returns an object that has the properties that define the current user interface controls of\n * the Publisher. You can modify the properties of this object and pass the object to the\n * setStyle()
method of thePublisher object. (See the documentation for\n * setStyle() to see the styles that define this object.)\n * @return {Object} The object that defines the styles of the Publisher.\n * @see setStyle()\n * @method #getStyle\n * @memberOf Publisher\n */\n\n /**\n * Returns an object that has the properties that define the current user interface controls of\n * the Subscriber. You can modify the properties of this object and pass the object to the\n * setStyle()
method of the Subscriber object. (See the documentation for\n * setStyle() to see the styles that define this object.)\n * @return {Object} The object that defines the styles of the Subscriber.\n * @see setStyle()\n * @method #getStyle\n * @memberOf Subscriber\n */\n // If +key+ is falsly then all styles will be returned.\n\n\n self.getStyle = function (key) {\n return _style.get(key);\n };\n /**\n * Sets properties that define the appearance of some user interface controls of the Publisher.\n *\n * You can either pass one parameter or two parameters to this method.
\n *\n * If you pass one parameter, style
, it is an object that has the following\n * properties:\n *\n *
\n * audioLevelDisplayMode
(String) — How to display the audio level\n * indicator. Possible values are: \"auto\"
(the indicator is displayed when the\n * video is disabled), \"off\"
(the indicator is not displayed), and\n * \"on\"
(the indicator is always displayed). \n *\n * archiveStatusDisplayMode
(String) — How to display the archive\n * status indicator. Possible values are: \"auto\"
(the indicator is displayed\n * when the session is being recorded), \"off\"
(the indicator is not displayed).\n * If you disable the archive status display indicator, you can display your own user\n * interface notifications based on the archiveStarted
and\n * archiveStopped
events dispatched by the Session object. \n *\n * backgroundImageURI
(String) — A URI for an image to display as\n * the background image when a video is not displayed. (A video may not be displayed if\n * you call publishVideo(false)
on the Publisher object). You can pass an http\n * or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the\n * data
URI scheme (instead of http or https) and pass in base-64-encrypted\n * PNG data, such as that obtained from the \n * Publisher.getImgData() method (for example, you could call\n * myPublisher.setStyle(\"backgroundImageURI\", myPublisher.getImgData())
).\n * If the URL or the image data is invalid, the property is ignored (the attempt to set\n * the image fails silently). \n *\n * buttonDisplayMode
(String) — How to display the microphone\n * controls. Possible values are: \"auto\"
(controls are displayed when the\n * stream is first displayed and when the user mouses over the display), \"off\"
\n * (controls are not displayed), and \"on\"
(controls are always displayed). \n *\n * nameDisplayMode
(String) Whether to display the stream name.\n * Possible values are: \"auto\"
(the name is displayed when the stream is first\n * displayed and when the user mouses over the display), \"off\"
(the name is not\n * displayed), and \"on\"
(the name is always displayed). \n *
\n *
\n *\n * For example, the following code passes one parameter to the method:
\n *\n * myPublisher.setStyle({nameDisplayMode: \"off\"});
\n *\n * If you pass two parameters, style
and value
, they are\n * key-value pair that define one property of the display style. For example, the following\n * code passes two parameter values to the method:
\n *\n * myPublisher.setStyle(\"nameDisplayMode\", \"off\");
\n *\n * You can set the initial settings when you call the Session.publish()
\n * or OT.initPublisher()
method. Pass a style
property as part of the\n * properties
parameter of the method.
\n *\n * The OT object dispatches an exception
event if you pass in an invalid style\n * to the method. The code
property of the ExceptionEvent object is set to 1011.
\n *\n * @param {Object} style Either an object containing properties that define the style, or a\n * String defining this single style property to set.\n * @param {String} value The value to set for the style
passed in. Pass a value\n * for this parameter only if the value of the style
parameter is a String.
\n *\n * @see getStyle()\n * @return {Publisher} The Publisher object\n * @see setStyle()\n *\n * @see Session.publish()\n * @see OT.initPublisher()\n * @method #setStyle\n * @memberOf Publisher\n */\n\n /**\n * Sets properties that define the appearance of some user interface controls of the Subscriber.\n *\n * You can either pass one parameter or two parameters to this method.
\n *\n * If you pass one parameter, style
, it is an object that has the following\n * properties:\n *\n *
\n * audioBlockedDisplayMode
(String) — Whether to display\n * the default audio blocked icon in Subscribers (in browsers where audio\n * autoplay is blocked). Possible values are: \"auto\"
(the default,\n * icon is displayed when the audio is disabled) and \"off\"
(the icon\n * is not displayed). Set this to \"off\"
if you want to display\n * your own UI element showing that the audio is blocked. In response to an\n * HTML element dispatching a click
event, you can call the\n * OT.unblockAudio() method to start audio\n * playback in this and all other blocked subscribers. \n *\n * audioLevelDisplayMode
(String) — How to display the audio level\n * indicator. Possible values are: \"auto\"
(the indicator is displayed when the\n * video is disabled), \"off\"
(the indicator is not displayed), and\n * \"on\"
(the indicator is always displayed). \n *\n * backgroundImageURI
(String) — A URI for an image to display as\n * the background image when a video is not displayed. (A video may not be displayed if\n * you call subscribeToVideo(false)
on the Publisher object). You can pass an\n * http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the\n * data
URI scheme (instead of http or https) and pass in base-64-encrypted\n * PNG data, such as that obtained from the \n * Subscriber.getImgData() method (for example, you could call\n * mySubscriber.setStyle(\"backgroundImageURI\", mySubscriber.getImgData())
).\n * If the URL or the image data is invalid, the property is ignored (the attempt to set\n * the image fails silently). \n *\n * buttonDisplayMode
(String) — How to display the speaker\n * controls. Possible values are: \"auto\"
(controls are displayed when the\n * stream is first displayed and when the user mouses over the display), \"off\"
\n * (controls are not displayed), and \"on\"
(controls are always displayed). \n *\n * nameDisplayMode
(String) Whether to display the stream name.\n * Possible values are: \"auto\"
(the name is displayed when the stream is first\n * displayed and when the user mouses over the display), \"off\"
(the name is not\n * displayed), and \"on\"
(the name is always displayed). \n *\n * videoDisabledDisplayMode
(String) Whether to display the video\n * disabled indicator and video disabled warning icons for a Subscriber. These icons\n * indicate that the video has been disabled (or is in risk of being disabled for\n * the warning icon) due to poor stream quality. Possible values are: \"auto\"
\n * (the icons are automatically when the displayed video is disabled or in risk of being\n * disabled due to poor stream quality), \"off\"
(do not display the icons), and\n * \"on\"
(display the icons). \n *
\n * \n *\n * For example, the following code passes one parameter to the method:
\n *\n *
\n * Chrome 72+, Firefox 52+, Safari 13+, Edge 79+, and Opera 59+ have screen-sharing\n * support built-in, with no extension required. (Note that support for the OpenTok\n * plugin for Internet Explorer is removed in OpenTok 2.17.) Screen sharing is\n * not supported in mobile browsers. In Electron, screen sharing is supported if\n * the webPreferences.contextIsolation
option of the Electron BrowserWindow is\n * set to false
or if the app uses a preload script to access the desktop capturer.\n * (The default value of webPreferences.contextIsolation
option is true
\n * in Electron 12 and false
in previous versions). To publish a screen-sharing video\n * in older versions of Chrome or Opera, the client adds an extension that enables publishing a\n * screen-sharing video stream on your domain. The OpenTok\n * screensharing-extensions\n * sample includes code for creating an extension for screen-sharing support in\n * older versions of Chrome and Opera.\n *
The options parameter also includes the following properties, which apply to screen-sharing\n * support in older versions of Chrome and Opera (in all other browsers these properties are\n * undefined):\n *
}\n */\n ;\n\n _proto.bindVideo =\n /*#__PURE__*/\n function () {\n var _bindVideo = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(webRTCStream, _ref3) {\n var audioVolume, muted, fallbackText, _inject, oldVideoFacade, tempContainer, cancellation, videoFacadeEvents, whenVideoPlaying, subscribingToAudioOnlyStream;\n\n return _regenerator.default.wrap(function _callee$(_context) {\n while (1) switch (_context.prev = _context.next) {\n case 0:\n audioVolume = _ref3.audioVolume, muted = _ref3.muted, fallbackText = _ref3.fallbackText, _inject = _ref3._inject;\n logging.debug('bindVideo ', {\n webRTCStream\n });\n oldVideoFacade = this._videoElementFacade;\n tempContainer = document.createElement('div');\n\n if (this._cancelBind) {\n logging.debug('Cancelling last bindVideo request');\n\n this._cancelBind.cancel();\n }\n\n this._cancelBind = new Cancellation();\n cancellation = this._cancelBind;\n this.once('destroy', () => cancellation.cancel());\n this._videoElementFacade = new WidgetView.VideoElementFacade({\n defaultAudioVolume: parseFloat(audioVolume, 100),\n fallbackText,\n fitMode: this._fitMode,\n _inject,\n muted,\n widgetType: this._widgetType\n });\n videoFacadeEvents = eventHelper(this._videoElementFacade);\n\n if (this._videoFacadeEvents) {\n logging.debug('Remove event listeners from old video facade');\n\n this._videoFacadeEvents.removeAll();\n }\n\n this._videoFacadeEvents = videoFacadeEvents;\n videoFacadeEvents.on('error', () => {\n this.trigger('error');\n });\n videoFacadeEvents.on('videoDimensionsChanged', (oldValue, newValue) => {\n this.trigger('videoDimensionsChanged', oldValue, newValue);\n });\n videoFacadeEvents.on('mediaStopped', track => {\n this.trigger('mediaStopped', track);\n });\n videoFacadeEvents.on('audioBlocked', () => this.trigger('audioBlocked'));\n videoFacadeEvents.on('audioUnblocked', () => this.trigger('audioUnblocked')); // Initialize the audio volume\n\n if (typeof audioVolume !== 'undefined') {\n try {\n this._videoElementFacade.setAudioVolume(audioVolume);\n } catch (e) {\n logging.warn(`bindVideo ${e}`);\n }\n } // makes the incoming audio streams take priority (will impact only FF OS for now)\n\n\n this._videoElementFacade.audioChannelType('telephony');\n\n if (!oldVideoFacade) {\n logging.debug('Appending the video facade');\n\n this._videoElementFacade.appendTo(this._widgetContainer);\n } else {\n this._videoElementFacade.appendTo(tempContainer);\n }\n\n _context.prev = 20;\n _context.next = 23;\n return this._videoElementFacade.bindToStream(webRTCStream);\n\n case 23:\n _context.next = 34;\n break;\n\n case 25:\n _context.prev = 25;\n _context.t0 = _context[\"catch\"](20);\n\n if (!cancellation.isCanceled()) {\n _context.next = 32;\n break;\n }\n\n logging.debug('Refusing to destroyVideo as bindVideo was cancelled');\n throw new CancellationError('CANCEL');\n\n case 32:\n this.destroyVideo();\n throw _context.t0;\n\n case 34:\n if (!oldVideoFacade) {\n if (this._videoElementFacade.domElement()) {\n this.trigger('videoElementCreated', this._videoElementFacade.domElement());\n }\n\n videoFacadeEvents.on('videoElementCreated', element => {\n this.trigger('videoElementCreated', element);\n });\n }\n\n if (!cancellation.isCanceled()) {\n _context.next = 38;\n break;\n }\n\n logging.debug('bindVideo bailing due to cancellation');\n throw new CancellationError('CANCEL');\n\n case 38:\n whenVideoPlaying = waitForVideoToBePlaying(this._videoElementFacade, 5000);\n subscribingToAudioOnlyStream = this.audioOnly() && this._widgetType === 'subscriber';\n\n if (!(!subscribingToAudioOnlyStream && webRTCStream.getVideoTracks().length > 0)) {\n _context.next = 45;\n break;\n }\n\n logging.debug('Waiting for correct resolution');\n _context.next = 44;\n return waitForVideoResolution(this._videoElementFacade, 5000);\n\n case 44:\n logging.debug(`Resolution: ${this._videoElementFacade.videoWidth()}x${this._videoElementFacade.videoHeight()}`);\n\n case 45:\n logging.debug('Waiting for video to be playing');\n _context.next = 48;\n return whenVideoPlaying;\n\n case 48:\n logging.debug('Video is playing');\n\n if (!cancellation.isCanceled()) {\n _context.next = 52;\n break;\n }\n\n logging.debug('bindVideo bailing due to cancellation');\n throw new CancellationError('CANCEL');\n\n case 52:\n if (oldVideoFacade) {\n // overview of the transition\n // add new one to dom, give it a second\n // remove old one\n if (this._videoElementFacade.domElement()) {\n this.trigger('videoElementCreated', this._videoElementFacade.domElement());\n }\n\n videoFacadeEvents.on('videoElementCreated', element => {\n this.trigger('videoElementCreated', element);\n });\n logging.debug('Destroy the old video facade');\n oldVideoFacade.destroy();\n logging.debug('Insert the new video facade');\n\n this._videoElementFacade.appendTo(this._widgetContainer);\n }\n\n if (this._insertDefaultUI !== false) {\n OTHelpers.addClass(this._videoElementFacade.domElement(), 'OT_video-element');\n }\n\n case 54:\n case \"end\":\n return _context.stop();\n }\n }, _callee, this, [[20, 25]]);\n }));\n\n function bindVideo(_x, _x2) {\n return _bindVideo.apply(this, arguments);\n }\n\n return bindVideo;\n }();\n\n _proto.bindAudioTrackOnly = function bindAudioTrackOnly() {\n if (this._videoElementFacade) {\n this._videoElementFacade.bindAudioTrackOnly();\n }\n };\n\n _proto.destroyVideo = function destroyVideo() {\n if (this._videoElementFacade) {\n this._videoElementFacade.destroy();\n\n this._videoElementFacade = null;\n }\n };\n\n _proto.video = function video() {\n return this._videoElementFacade;\n };\n\n _proto.showPoster = function showPoster(_showPoster) {\n if (_showPoster === undefined) {\n return !OTHelpers.isDisplayNone(this._posterContainer);\n }\n\n this._showPoster = _showPoster;\n OTHelpers[_showPoster ? 'show' : 'hide'](this._posterContainer);\n return this.showPoster();\n };\n\n _proto.poster = function poster(src) {\n if (src === undefined) {\n return OTHelpers.css(this._posterContainer, 'backgroundImage');\n }\n\n this._poster = src;\n OTHelpers.css(this._posterContainer, 'backgroundImage', `url(${src})`);\n return this.poster();\n };\n\n _proto.loading = function loading(isLoading) {\n if (isLoading === undefined) {\n return this._loading;\n }\n\n this._loading = Boolean(isLoading);\n\n if (this._container) {\n this._container.classList[isLoading ? 'add' : 'remove']('OT_loading');\n }\n\n return this.loading();\n };\n\n _proto.audioOnly = function audioOnly(isAudioOnly) {\n if (isAudioOnly === undefined) {\n return this._audioOnly;\n }\n\n this._audioOnly = isAudioOnly;\n\n if (this._container) {\n this._container.classList[isAudioOnly ? 'add' : 'remove']('OT_audio-only');\n }\n\n return this.audioOnly();\n };\n\n _proto.domId = function domId() {\n return this._container && this._container.getAttribute('id');\n }\n /** @return {HTMLVideoElement} */\n ;\n\n (0, _createClass2.default)(WidgetView, [{\n key: \"domElement\",\n get: function get() {\n return this._container;\n }\n }, {\n key: \"videoElement\",\n get: function get() {\n return this._videoElementFacade && this._videoElementFacade.domElement() || undefined;\n }\n /**\n * The width of the video element in pixels\n * @return {Number}\n */\n\n }, {\n key: \"width\",\n get: function get() {\n return this.videoElement && this.videoElement.offsetWidth;\n }\n /**\n * The height of the video element in pixels\n * @return {Number}\n */\n\n }, {\n key: \"height\",\n get: function get() {\n return this.videoElement && this.videoElement.offsetHeight;\n }\n }]);\n return WidgetView;\n }(); // This is bound here so that it can be mocked in testing. Feels like a smell that's a symptom of\n // larger problems to me, but I'm just maintaining existing behaviour right now.\n\n\n WidgetView.VideoElementFacade = VideoElementFacade;\n return WidgetView;\n}\n\nmodule.exports = WidgetViewFactory;\n\n/***/ }),\n/* 164 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.CancellationError = exports.default = undefined;\n\nvar _Cancellation = __webpack_require__(638);\n\nvar _Cancellation2 = _interopRequireDefault(_Cancellation);\n\nvar _CancellationError2 = __webpack_require__(296);\n\nvar _CancellationError3 = _interopRequireDefault(_CancellationError2);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = _Cancellation2.default;\nexports.CancellationError = _CancellationError3.default;\n\n/***/ }),\n/* 165 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nconst sessionObjects = __webpack_require__(23);\n\nmodule.exports = () => Promise.all(sessionObjects.subscribers.where().map(sub => {\n if (!sub.isAudioBlocked()) {\n return undefined;\n }\n /**\n * Causes subscribers' audio to play back in browsers where audio is blocked.\n * You must call this method in response to an HTML element dispaching\n * a click
event.\n * \n * You only need to call this method if you disable display of\n * the Subscriber's default audio playback icon (by setting the\n * style.audioBlockedDisplayMode
property of the\n * options
parameter of the\n * Session.subscribe() method).\n * You may call this method in response to the user clicking an HTML element\n * that you display on the page.\n *
\n * The Subscriber dispatches an\n * audioBlocked event\n * if audio is blocked. In response to that event, you can display a UI\n * element to notify the end user to respond to playback subscriber audio.\n * Call the OT.unblockAudio()
method in response to the user\n * clicking a DOM element.\n *
\n * Note: Subscriber audio is also unblocked automatically if\n * the local client gains access to the camera or microphone (for instance,\n * in response to a successful call to OT.initPublisher()
).\n *\n * @returns Promise The promise resolves if audio playback succeeds\n * on all blocked subscribers. The promise is rejected if playback fails.\n * See Error for details.\n *\n * @see Subscriber.isAudioBlocked()\n * @see The audioBlocked\n * and audioUnblocked\n * Subscriber events\n *\n * @method OT.unblockAudio\n * @memberof OT\n */\n\n\n return sub._.unblockAudio();\n}));\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 166 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _regenerator = _interopRequireDefault(__webpack_require__(18));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\n/* eslint-disable global-require, func-names */\n\n/* eslint-disable no-use-before-define, no-prototype-builtins, no-underscore-dangle */\n\n/* global MediaStreamTrack */\n// @todo need to ensure logging for peer disconnected, and peer failures is intact\nconst get = __webpack_require__(55);\n\nconst assert = __webpack_require__(148);\n\nconst assign = __webpack_require__(7);\n\nconst cloneDeep = __webpack_require__(54);\n\nconst find = __webpack_require__(60);\n\nconst isString = __webpack_require__(273);\n\nconst pick = __webpack_require__(45);\n\nconst once = __webpack_require__(48);\n\nconst startCase = __webpack_require__(657);\n\nconst uuid = __webpack_require__(17);\n\nconst capitalize = __webpack_require__(256);\n\nconst WeakMap = __webpack_require__(192);\n\nconst _require = __webpack_require__(164),\n CancellationError = _require.CancellationError,\n Cancellation = _require.default;\n\nconst env = __webpack_require__(2);\n\nconst setEncodersActiveStateDefault = __webpack_require__(667);\n\nconst promisify = __webpack_require__(305);\n\nconst getStatsHelpers = __webpack_require__(75);\n\nconst eventNames = __webpack_require__(24);\n\nconst eventing = __webpack_require__(5);\n\nconst promiseDelay = __webpack_require__(106);\n\nconst Event = __webpack_require__(126);\n\nconst AnalyticsHelperDefault = __webpack_require__(46);\n\nconst IntervalRunnerDefault = __webpack_require__(74);\n\nconst createCleanupJobs = __webpack_require__(154);\n\nconst whitelistPublisherProperties = __webpack_require__(668);\n\nconst defaultWidgetView = __webpack_require__(163)();\n\nconst audioLevelBehaviour = __webpack_require__(669);\n\nconst blockCallsUntilComplete = __webpack_require__(670);\n\nconst unblockAudio = __webpack_require__(165);\n\nconst _require2 = __webpack_require__(105)(),\n getMediaDevices = _require2.getMediaDevices;\n\nconst createCanvasVideoTrack = __webpack_require__(671);\n\nconst isSecureContextRequired = __webpack_require__(267);\n\nconst isGetRtcStatsReportSupported = __webpack_require__(304);\n\nconst getDeviceIdFromStream = __webpack_require__(672);\n\nconst createStreamErrorMap = __webpack_require__(673);\n\nconst isiOS = __webpack_require__(73);\n\nconst _require3 = __webpack_require__(288),\n setVideoContentHint = _require3.setVideoContentHint,\n getVideoContentHint = _require3.getVideoContentHint;\n\nconst STOP_SENDING_MEDIA_DELAY = 3 * 1000;\nconst KEEP_SENDING_RTCP_DELAY = 30 * 1000;\n\nmodule.exports = function PublisherFactory(_ref) {\n if (_ref === void 0) {\n _ref = {};\n }\n\n let _ref2 = _ref,\n deps = (0, _extends2.default)({}, _ref2);\n ['processPubOptions'].forEach(key => {\n assert(deps[key], `${key} dependency must be injected into Publisher`);\n });\n const AnalyticsHelper = deps.AnalyticsHelper || AnalyticsHelperDefault;\n\n const calculateCapableSimulcastStreams = deps.calculateCapableSimulcastStreams || __webpack_require__(250);\n\n const createChromeMixin = deps.createChromeMixin || __webpack_require__(279)();\n\n const deviceHelpers = deps.deviceHelpers || __webpack_require__(105)();\n\n const EnvironmentLoader = deps.EnvironmentLoader || __webpack_require__(146);\n\n const Errors = deps.Errors || __webpack_require__(6);\n\n const Events = deps.Events || __webpack_require__(21)();\n\n const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(9);\n\n const interpretPeerConnectionError = deps.interpretPeerConnectionError || __webpack_require__(161)();\n\n const IntervalRunner = deps.IntervalRunner || IntervalRunnerDefault;\n\n const logging = deps.logging || __webpack_require__(0)('Publisher');\n\n const Microphone = deps.Microphone || __webpack_require__(289)();\n\n const otError = deps.otError || __webpack_require__(11)();\n\n const OTErrorClass = deps.OTErrorClass || __webpack_require__(20);\n\n const OTHelpers = deps.OTHelpers || __webpack_require__(4);\n\n const parseIceServers = deps.parseIceServers || __webpack_require__(100).parseIceServers;\n\n const PUBLISH_MAX_DELAY = deps.PUBLISH_MAX_DELAY || __webpack_require__(265);\n\n const PublisherPeerConnection = deps.PublisherPeerConnection || __webpack_require__(293)();\n\n const PublishingState = deps.PublishingState || __webpack_require__(295)();\n\n const StreamChannel = deps.StreamChannel || __webpack_require__(149);\n\n const systemRequirements = deps.systemRequirements || __webpack_require__(152);\n\n const VideoOrientation = deps.VideoOrientation || __webpack_require__(150)();\n\n const WidgetView = deps.WidgetView || defaultWidgetView;\n const windowMock = deps.global || (typeof window !== undefined ? window : global);\n\n const createSendMethod = deps.createSendMethod || __webpack_require__(302);\n\n const setEncodersActiveState = deps.setEncodersActiveState || setEncodersActiveStateDefault;\n const processPubOptions = deps.processPubOptions;\n /**\n * The Publisher object provides the mechanism through which control of the\n * published stream is accomplished. Calling the OT.initPublisher()
method\n * creates a Publisher object.
\n *\n * The following code instantiates a session, and publishes an audio-video stream\n * upon connection to the session:
\n *\n * \n * var apiKey = ''; // Replace with your API key. See https://tokbox.com/account\n * var sessionID = ''; // Replace with your own session ID.\n * // See https://tokbox.com/developer/guides/create-session/.\n * var token = ''; // Replace with a generated token that has been assigned the moderator role.\n * // See https://tokbox.com/developer/guides/create-token/.\n *\n * var session = OT.initSession(apiKey, sessionID);\n * session.connect(token, function(error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * // This example assumes that a DOM element with the ID 'publisherElement' exists\n * var publisherProperties = {width: 400, height:300, name:\"Bob's stream\"};\n * publisher = OT.initPublisher('publisherElement', publisherProperties);\n * session.publish(publisher);\n * }\n * });\n *
\n *\n * This example creates a Publisher object and adds its video to a DOM element\n * with the ID publisherElement
by calling the OT.initPublisher()
\n * method. It then publishes a stream to the session by calling\n * the publish()
method of the Session object.
\n *\n * @property {Boolean} accessAllowed Whether the user has granted access to the camera\n * and microphone. The Publisher object dispatches an accessAllowed
event when\n * the user grants access. The Publisher object dispatches an accessDenied
event\n * when the user denies access.\n * @property {Element} element The HTML DOM element containing the Publisher. (Note:\n * when you set the insertDefaultUI
option to false
in the call to\n * OT.initPublisher, the element
property\n * is undefined.)\n * @property {String} id The DOM ID of the Publisher.\n * @property {Stream} stream The {@link Stream} object corresponding the stream of\n * the Publisher.\n * @property {Session} session The {@link Session} to which the Publisher belongs.\n *\n * @see OT.initPublisher\n * @see Session.publish()\n *\n * @class Publisher\n * @augments EventDispatcher\n */\n\n const Publisher = function Publisher(options) {\n var _this = this;\n\n if (options === void 0) {\n options = {};\n }\n\n let privateEvents = eventing({});\n const peerConnectionMetaMap = new WeakMap();\n /**\n * @typedef {Object} peerConnectionMeta\n * @property {String} remoteConnectionId The connection id of the remote side\n * @property {String} remoteSubscriberId The subscriber id of the remote side\n * @property {String} peerId The peerId of this peer connection\n * @property {String} peerConnectionId Our local identifier for this peer connection\n */\n\n /**\n * Retrieve meta information for this peer connection\n * @param {PublisherPeerConnection} peerConnection\n * @returns {peerConnectionMeta} meta data regarding this peer connection\n */\n\n const getPeerConnectionMeta = peerConnection => peerConnectionMetaMap.get(peerConnection);\n\n const setPeerConnectionMeta = (peerConnection, value) => peerConnectionMetaMap.set(peerConnection, value);\n\n eventing(this);\n const streamCleanupJobs = createCleanupJobs();\n /** @type AnalyticsHelperDefault */\n\n let analytics = new AnalyticsHelper(); // Check that the client meets the minimum requirements, if they don't the upgrade\n // flow will be triggered.\n\n if (!systemRequirements.check()) {\n systemRequirements.upgrade();\n }\n /** @type {WidgetView|null} */\n\n\n let widgetView;\n let lastRequestedStreamId;\n let webRTCStream;\n let publishStartTime;\n let microphone;\n let state;\n let rumorIceServers;\n let attemptStartTime;\n let audioDevices;\n let videoDevices;\n let selectedVideoInputDeviceId;\n let selectedAudioInputDeviceId;\n let didPublishComplete = false;\n let activeSourceStreamId;\n\n let _keepSendingRtcpToMantisTimeout;\n\n const hybridSessionTransitionStartTimes = {};\n const lastIceConnectionStates = {};\n /** @type IntervalRunnerDefault | undefined */\n\n let connectivityAttemptPinger; // previousSession mimics the publisher.session variable except it's never set to null\n // this allows analytics to refer to it in cases where we disconnect/destroy\n // and go to log analytics and publisher.session has been set to null\n\n let previousSession;\n\n const getLastSession = () => this.session || previousSession || {\n isConnected() {\n return false;\n }\n\n };\n\n const streamChannels = [];\n this.once('publishComplete', err => {\n if (!err) {\n var _this$session;\n\n didPublishComplete = true;\n activeSourceStreamId = ((_this$session = this.session) == null ? void 0 : _this$session.sessionInfo.p2pEnabled) ? 'P2P' : 'MANTIS';\n }\n });\n this.on('sourceStreamIdChanged', newSourceStreamId => {\n activeSourceStreamId = newSourceStreamId;\n });\n this.on('audioAcquisitionProblem', (_ref3) => {\n let method = _ref3.method;\n logAnalyticsEvent('publisher:audioAcquisitionProblem', 'Event', {\n didPublishComplete,\n method\n });\n });\n\n function getCommonAnalyticsFields() {\n return {\n connectionId: getLastSession().isConnected() ? getLastSession().connection.connectionId : null,\n streamId: lastRequestedStreamId,\n widgetType: 'Publisher'\n };\n }\n\n const onStreamAvailableError = plainError => {\n const names = Object.keys(Errors).map(shortName => Errors[shortName]);\n const error = otError(names.indexOf(plainError.name) > -1 ? plainError.name : Errors.MEDIA_ERR_ABORTED, plainError, ExceptionCodes.UNABLE_TO_PUBLISH);\n logging.error(`onStreamAvailableError ${error.name}: ${error.message}`);\n state.set('Failed');\n\n if (widgetView) {\n widgetView.destroy();\n widgetView = null;\n }\n\n const logOptions = {\n failureReason: 'GetUserMedia',\n failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,\n failureMessage: `OT.Publisher failed to access camera/mic: ${error.message}`\n };\n logConnectivityEvent('Failure', {}, logOptions);\n OTErrorClass.handleJsException({\n error,\n errorMsg: logOptions.failureReason,\n code: logOptions.failureCode,\n target: this,\n analytics\n });\n this.trigger('publishComplete', error);\n };\n\n const onScreenSharingError = errorParam => {\n const error = cloneDeep(errorParam);\n error.code = ExceptionCodes.UNABLE_TO_PUBLISH;\n logging.error(`OT.Publisher.onScreenSharingError ${error.message}`);\n state.set('Failed');\n error.message = `Screensharing: ${error.message}`;\n this.trigger('publishComplete', error);\n logConnectivityEvent('Failure', {}, {\n failureReason: 'ScreenSharing',\n failureMessage: error.message\n });\n\n if (widgetView) {\n widgetView.destroy();\n widgetView = null;\n }\n }; // The user has clicked the 'deny' button in the allow access dialog, or it's\n // set to always deny, or the access was denied due to HTTP restrictions;\n\n\n const onAccessDenied = errorParam => {\n const error = cloneDeep(errorParam);\n let isIframe;\n\n try {\n isIframe = window.self !== window.top;\n } catch (err) {// ignore errors, (some browsers throw a security error when accessing cross domain)\n }\n\n if ((typeof window !== undefined ? window : global).location.protocol !== 'https:') {\n if (isScreenSharing) {\n /*\n * in http:// the browser will deny permission without asking the\n * user. There is also no way to tell if it was denied by the\n * user, or prevented from the browser.\n */\n error.message += ' Note: https:// is required for screen sharing.';\n } else if (isSecureContextRequired() && OTHelpers.env.hostName !== 'localhost') {\n error.message += ` Note: ${OTHelpers.env.name} requires HTTPS for camera and microphone access.`;\n }\n }\n\n if (isIframe && !isScreenSharing) {\n error.message += ' Note: Check that the iframe has the allow attribute for camera and microphone';\n }\n\n logging.error(error.message);\n state.set('Failed'); // Note: The substring 'Publisher Access Denied:' is part of our api contract for now.\n // https://tokbox.com/developer/guides/publish-stream/js/#troubleshooting\n\n error.message = `OT.Publisher Access Denied: Permission Denied: ${error.message}`;\n error.code = ExceptionCodes.UNABLE_TO_PUBLISH;\n\n if (widgetView) {\n widgetView.destroy();\n widgetView = null;\n }\n\n logConnectivityEvent('Cancel', {\n reason: 'AccessDenied'\n });\n this.trigger('publishComplete', error);\n this.dispatchEvent(new Event(eventNames.ACCESS_DENIED));\n };\n\n const userMediaError = error => {\n const isPermissionError = error.name === Errors.USER_MEDIA_ACCESS_DENIED || error.name === Errors.NOT_SUPPORTED && error.originalMessage.match(/Only secure origins/);\n\n if (isPermissionError) {\n onAccessDenied(error);\n } else if (processPubOptions.isScreenSharing) {\n onScreenSharingError(error);\n } else {\n onStreamAvailableError(error);\n }\n\n throw error;\n };\n\n const onAccessDialogOpened = () => {\n logAnalyticsEvent('accessDialog', 'Opened');\n this.dispatchEvent(new Event(eventNames.ACCESS_DIALOG_OPENED, true));\n };\n\n const onAccessDialogClosed = () => {\n logAnalyticsEvent('accessDialog', 'Closed');\n this.dispatchEvent(new Event(eventNames.ACCESS_DIALOG_CLOSED, false));\n };\n\n const guid = uuid();\n const peerConnectionsAsync = {};\n let loaded = false;\n let previousAnalyticsStats = {};\n let audioAcquisitionProblemDetected = false;\n let processedOptions = processPubOptions(options, 'OT.Publisher', () => state && state.isDestroyed());\n processedOptions.on({\n accessDialogOpened: onAccessDialogOpened,\n accessDialogClosed: onAccessDialogClosed\n });\n const _processedOptions = processedOptions,\n isScreenSharing = _processedOptions.isScreenSharing,\n isCustomAudioTrack = _processedOptions.isCustomAudioTrack,\n isCustomVideoTrack = _processedOptions.isCustomVideoTrack,\n shouldAllowAudio = _processedOptions.shouldAllowAudio,\n properties = _processedOptions.properties,\n getUserMedia = _processedOptions.getUserMedia; // start with undefined\n\n Object.defineProperty(this, 'loudness', {\n writable: false,\n value: undefined,\n configurable: true\n });\n\n function removeTrackListeners(trackListeners) {\n trackListeners.forEach(off => off());\n }\n\n const listenWithOff = (obj, event, listener) => {\n if (!obj.addEventListener) {\n // noop\n return () => {};\n }\n\n obj.addEventListener(event, listener);\n return () => obj.removeEventListener(event, listener);\n };\n\n (function handleAudioEnded() {\n const trackListeners = [];\n privateEvents.on('streamDestroy', () => removeTrackListeners(trackListeners));\n privateEvents.on('streamChange', () => {\n removeTrackListeners(trackListeners);\n const newListeners = webRTCStream.getAudioTracks().map(track => listenWithOff(track, 'ended', () => {\n // chrome audio acquisition issue\n audioAcquisitionProblemDetected = true;\n this.trigger('audioAcquisitionProblem', {\n method: 'trackEndedEvent'\n });\n }));\n trackListeners.splice(0, trackListeners.length, ...newListeners);\n });\n }).call(this);\n\n (function handleMuteTrack() {\n const trackListeners = [];\n privateEvents.on('streamDestroy', () => removeTrackListeners(trackListeners));\n privateEvents.on('streamChange', () => {\n removeTrackListeners(trackListeners); // Screensharing in Chrome sometimes triggers 'mute' and 'unmute'\n // repeatedly for now reason OPENTOK-37818\n // https://bugs.chromium.org/p/chromium/issues/detail?id=931033\n\n if (!isScreenSharing) {\n webRTCStream.getTracks().forEach(track => {\n if (track.addEventListener) {\n trackListeners.push(listenWithOff(track, 'mute', () => {\n refreshAudioVideoUI();\n }));\n trackListeners.push(listenWithOff(track, 'unmute', () => {\n refreshAudioVideoUI();\n }));\n }\n });\n }\n });\n })(); // / Private Methods\n\n\n const logAnalyticsEvent = options.logAnalyticsEvent || ((action, variation, payload, logOptions, throttle) => {\n let stats = assign({\n action,\n variation,\n payload\n }, getCommonAnalyticsFields(), logOptions);\n\n if (variation === 'Failure') {\n stats = assign(previousAnalyticsStats, stats);\n }\n\n previousAnalyticsStats = pick(stats, 'sessionId', 'connectionId', 'partnerId');\n analytics.logEvent(stats, throttle);\n });\n\n const logConnectivityEvent = function logConnectivityEvent(variation, payload, logOptions) {\n if (payload === void 0) {\n payload = {};\n }\n\n if (logOptions === void 0) {\n logOptions = {};\n }\n\n if (logOptions.failureReason === 'Non-fatal') {\n // we don't want to log because it was a non-fatal failure\n return;\n }\n\n if (variation === 'Attempt') {\n attemptStartTime = new Date().getTime();\n\n if (connectivityAttemptPinger) {\n connectivityAttemptPinger.stop();\n logging.error('_connectivityAttemptPinger should have been cleaned up');\n }\n\n connectivityAttemptPinger = new IntervalRunner(() => {\n logAnalyticsEvent('Publish', 'Attempting', payload, (0, _extends2.default)({}, getCommonAnalyticsFields(), logOptions));\n }, 1 / 5, 6);\n }\n\n if (variation === 'Failure' || variation === 'Success' || variation === 'Cancel') {\n if (connectivityAttemptPinger) {\n connectivityAttemptPinger.stop();\n connectivityAttemptPinger = undefined;\n } else {\n logging.warn(`Received connectivity event: \"${variation}\" without \"Attempt\"`);\n }\n\n logAnalyticsEvent('Publish', variation, (0, _extends2.default)({\n videoInputDevices: videoDevices,\n audioInputDevices: audioDevices,\n videoInputDeviceCount: videoDevices ? videoDevices.length : undefined,\n audioInputDeviceCount: audioDevices ? audioDevices.length : undefined,\n selectedVideoInputDeviceId,\n selectedAudioInputDeviceId\n }, payload), (0, _extends2.default)({\n attemptDuration: new Date().getTime() - attemptStartTime\n }, logOptions));\n } else {\n logAnalyticsEvent('Publish', variation, payload, logOptions);\n }\n };\n\n const logRepublish = (variation, payload) => {\n logAnalyticsEvent('ICERestart', variation, payload);\n };\n\n const logHybridSessionTransition = (action, variation, payload) => {\n if (variation === 'Attempt') {\n hybridSessionTransitionStartTimes[action] = new Date().getTime();\n logAnalyticsEvent(action, variation, payload);\n } else if (variation === 'Failure' || variation === 'Success') {\n logAnalyticsEvent(action, variation, payload, {\n attemptDuration: new Date().getTime() - hybridSessionTransitionStartTimes[action]\n });\n }\n };\n\n const logRoutedToRelayedTransition = function logRoutedToRelayedTransition(variation, payload) {\n if (payload === void 0) {\n payload = {};\n }\n\n logHybridSessionTransition('RoutedToRelayedTransition', variation, payload);\n };\n\n const logRelayedToRoutedTransition = function logRelayedToRoutedTransition(variation, payload) {\n if (payload === void 0) {\n payload = {};\n }\n\n logHybridSessionTransition('RelayedToRoutedTransition', variation, payload);\n }; // logs an analytics event for getStats on the first call\n\n\n const notifyGetStatsCalled = once(() => {\n logAnalyticsEvent('GetStats', 'Called');\n });\n const notifyGetRtcStatsCalled = once(() => {\n logAnalyticsEvent('GetRtcStatsReport', 'Called');\n });\n\n const recordQOS = (_ref4) => {\n let parsedStats = _ref4.parsedStats,\n simulcastEnabled = _ref4.simulcastEnabled,\n remoteConnectionId = _ref4.remoteConnectionId,\n peerId = _ref4.peerId,\n sourceStreamId = _ref4.sourceStreamId;\n const QoSBlob = {\n peerId,\n widgetType: 'Publisher',\n connectionId: this.session && this.session.isConnected() ? this.session.connection.connectionId : null,\n streamId: lastRequestedStreamId,\n width: widgetView.width,\n height: widgetView.height,\n audioTrack: webRTCStream && webRTCStream.getAudioTracks().length > 0,\n hasAudio: hasAudio(),\n publishAudio: properties.publishAudio,\n videoTrack: webRTCStream && webRTCStream.getVideoTracks().length > 0,\n hasVideo: hasVideo(),\n publishVideo: properties.publishVideo,\n audioSource: isCustomAudioTrack ? 'Custom' : undefined,\n duration: publishStartTime ? Math.round((new Date().getTime() - publishStartTime.getTime()) / 1000) : 0,\n remoteConnectionId,\n scalableVideo: simulcastEnabled,\n sourceStreamId\n };\n let videoSource = isScreenSharing && isCustomVideoTrack && 'Screen' || isScreenSharing && options.videoSource || isCustomVideoTrack && 'Custom' || properties.constraints.video && 'Camera' || null; // Normalize videoSource so that \"application\" becomes \"Application\"\n\n if (isString(videoSource)) {\n videoSource = startCase(videoSource);\n }\n\n QoSBlob.videoSource = videoSource;\n const videoDimensions = {\n videoWidth: this.videoWidth(),\n videoHeight: this.videoHeight()\n };\n const parsedAndQosStats = assign(QoSBlob, parsedStats);\n const videoParsedStats = assign(parsedAndQosStats, videoDimensions);\n analytics.logQOS(parsedAndQosStats);\n this.trigger('qos', videoParsedStats);\n }; // Returns the video dimensions. Which could either be the ones that\n // the developer specific in the videoDimensions property, or just\n // whatever the video element reports.\n //\n // If all else fails then we'll just default to 640x480\n //\n\n\n const getVideoDimensions = () => {\n let streamWidth;\n let streamHeight;\n const video = widgetView && widgetView.video(); // We set the streamWidth and streamHeight to be the minimum of the requested\n // resolution and the actual resolution.\n\n if (properties.videoDimensions) {\n streamWidth = Math.min(properties.videoDimensions.width, video && video.videoWidth() || 640);\n streamHeight = Math.min(properties.videoDimensions.height, video && video.videoHeight() || 480);\n } else {\n streamWidth = video && video.videoWidth() || 640;\n streamHeight = video && video.videoHeight() || 480;\n }\n\n return {\n width: streamWidth,\n height: streamHeight\n };\n }; // / Private Events\n\n\n const stateChangeFailed = changeFailed => {\n logging.error('OT.Publisher State Change Failed: ', changeFailed.message);\n logging.debug(changeFailed);\n };\n\n const onLoaded = () => {\n if (state.isDestroyed()) {\n // The publisher was destroyed before loading finished\n if (widgetView) {\n widgetView.destroyVideo();\n }\n\n return;\n }\n\n logging.debug('OT.Publisher.onLoaded; resolution:', `${this.videoWidth()}x${this.videoHeight()}`);\n state.set('MediaBound'); // Try unblock audio on all subscribers\n\n unblockAudio().catch(logging.error); // If we have a session and we haven't created the stream yet then\n // wait until that is complete before hiding the loading spinner\n\n widgetView.loading(this.session ? !this.stream : false);\n loaded = true;\n };\n\n const onLoadFailure = plainError => {\n // eslint-disable-next-line no-param-reassign\n const err = otError(Errors.CONNECT_FAILED, plainError, ExceptionCodes.P2P_CONNECTION_FAILED);\n err.message = `OT.Publisher PeerConnection Error: ${err.message}`;\n logConnectivityEvent('Failure', {}, {\n failureReason: 'PeerConnectionError',\n failureCode: err.code,\n failureMessage: err.message\n });\n state.set('Failed');\n this.trigger('publishComplete', err);\n OTErrorClass.handleJsException({\n error: err,\n target: this,\n analytics\n });\n }; // Clean up our LocalMediaStream\n\n\n const cleanupLocalStream = () => {\n if (webRTCStream) {\n privateEvents.emit('streamDestroy'); // Stop revokes our access cam and mic access for this instance\n // of localMediaStream.\n\n if (windowMock.MediaStreamTrack && windowMock.MediaStreamTrack.prototype.stop) {\n // Newer spec\n webRTCStream.getTracks().forEach(track => track.stop());\n } else {\n // Older spec\n webRTCStream.stop();\n }\n }\n };\n\n const bindVideo = /*#__PURE__*/function () {\n var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {\n var videoContainerOptions;\n return _regenerator.default.wrap(function _callee$(_context) {\n while (1) switch (_context.prev = _context.next) {\n case 0:\n videoContainerOptions = {\n muted: true\n };\n\n if (widgetView) {\n _context.next = 3;\n break;\n }\n\n throw new Error('Cannot bind video after widget view has been destroyed');\n\n case 3:\n return _context.abrupt(\"return\", widgetView.bindVideo(webRTCStream, videoContainerOptions));\n\n case 4:\n case \"end\":\n return _context.stop();\n }\n }, _callee);\n }));\n\n return function bindVideo() {\n return _ref5.apply(this, arguments);\n };\n }();\n\n const onStreamAvailable = webOTStream => {\n logging.debug('OT.Publisher.onStreamAvailable');\n state.set('BindingMedia'); // see https://bugs.webkit.org/show_bug.cgi?id=208209 & https://bugs.webkit.org/show_bug.cgi?id=208516\n // in iOS 13.3 and later, the audiotrack sometimes fails to unmute after receivng a phone call\n // this work around relies on the visibility of the page to see if the call is over\n\n const isBuggediOS = isiOS() && env.version >= 13.3;\n const audioTrack = webOTStream.getAudioTracks()[0];\n\n if (isBuggediOS && audioTrack) {\n audioTrack.onmute = () => handleBuggedMutedLocalAudioTrack(audioTrack); // When iOS uses compact UI for phone calls, the visibility of the page is not relevant.\n // As a workaround for this specific case, when the track is back to unmuted\n // we reset the audio source. See OPENTOK-42233.\n\n\n audioTrack.onunmute = () => handleBuggedUnMutedLocalAudioTrack(audioTrack);\n }\n\n if (properties.videoContentHint !== undefined) {\n setVideoContentHint(webOTStream, properties.videoContentHint);\n }\n\n cleanupLocalStream();\n webRTCStream = webOTStream;\n privateEvents.emit('streamChange');\n\n const findSelectedDeviceId = (tracks, devices) => {\n // Store the device labels to log later\n let selectedDeviceId;\n tracks.forEach(track => {\n if (track.deviceId) {\n selectedDeviceId = track.deviceId.toString();\n } else if (track.label && devices) {\n const selectedDevice = find(devices, el => el.label === track.label);\n\n if (selectedDevice) {\n selectedDeviceId = selectedDevice.deviceId;\n }\n }\n });\n return selectedDeviceId;\n };\n\n selectedVideoInputDeviceId = findSelectedDeviceId(webRTCStream.getVideoTracks(), videoDevices);\n selectedAudioInputDeviceId = findSelectedDeviceId(webRTCStream.getAudioTracks(), audioDevices);\n microphone = new Microphone(webRTCStream, !properties.publishAudio);\n updateVideo();\n updateAudio();\n this.accessAllowed = true;\n this.dispatchEvent(new Event(eventNames.ACCESS_ALLOWED, false));\n };\n\n const onPublishingTimeout = session => {\n logging.error('OT.Publisher.onPublishingTimeout');\n let errorName;\n let errorMessage;\n\n if (audioAcquisitionProblemDetected) {\n errorName = Errors.CHROME_MICROPHONE_ACQUISITION_ERROR;\n 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.';\n } else {\n errorName = Errors.TIMEOUT;\n errorMessage = 'Could not publish in a reasonable amount of time';\n }\n\n const logOptions = {\n failureReason: 'ICEWorkflow',\n failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,\n failureMessage: 'OT.Publisher failed to publish in a reasonable amount of time (timeout)'\n };\n logConnectivityEvent('Failure', {}, logOptions);\n OTErrorClass.handleJsException({\n errorMsg: logOptions.failureReason,\n code: logOptions.failureCode,\n target: this,\n analytics\n });\n\n if (session.isConnected() && this.streamId) {\n session._.streamDestroy(this.streamId);\n } // Disconnect immediately, rather than wait for the WebSocket to\n // reply to our destroyStream message.\n\n\n this.disconnect();\n this.session = null; // We're back to being a stand-alone publisher again.\n\n if (!state.isDestroyed()) {\n state.set('MediaBound');\n }\n\n this.trigger('publishComplete', otError(errorName, new Error(errorMessage), ExceptionCodes.UNABLE_TO_PUBLISH));\n };\n\n const onVideoError = plainErr => {\n // eslint-disable-next-line no-param-reassign\n const err = otError(Errors.MEDIA_ERR_DECODE, plainErr, ExceptionCodes.UNABLE_TO_PUBLISH);\n err.message = `OT.Publisher while playing stream: ${err.message}`;\n logging.error('OT.Publisher.onVideoError:', err);\n logAnalyticsEvent('stream', null, {\n reason: err.message\n }); // Check if attempting to publish *before* overwriting the state\n\n const isAttemptingToPublish = state.isAttemptingToPublish();\n state.set('Failed');\n\n if (isAttemptingToPublish) {\n this.trigger('publishComplete', err);\n } else {\n // FIXME: This emits a string instead of an error here for backwards compatibility despite\n // being undocumented. When possible we should remove access to this and other undocumented\n // events, and restore emitting actual errors here.\n this.trigger('error', err.message);\n }\n\n OTErrorClass.handleJsException({\n error: err,\n target: this,\n analytics\n });\n };\n\n this._removePeerConnection = peerConnection => {\n const _getPeerConnectionMet = getPeerConnectionMeta(peerConnection),\n peerConnectionId = _getPeerConnectionMet.peerConnectionId;\n\n delete peerConnectionsAsync[peerConnectionId];\n peerConnection.destroy();\n };\n\n this._removeSubscriber = subscriberId => {\n const isAdaptiveEnabled = this.session.sessionInfo.isAdaptiveEnabled;\n\n if (isAdaptiveEnabled) {\n this._.startRelayedToRoutedTransition();\n }\n\n getPeerConnectionsBySubscriber(subscriberId).then(peerConnections => {\n peerConnections.forEach(pc => this._removePeerConnection(pc));\n });\n };\n\n const onPeerDisconnected = peerConnection => {\n const _getPeerConnectionMet2 = getPeerConnectionMeta(peerConnection),\n remoteSubscriberId = _getPeerConnectionMet2.remoteSubscriberId,\n peerConnectionId = _getPeerConnectionMet2.peerConnectionId;\n\n logging.debug('Subscriber has been disconnected from the Publisher\\'s PeerConnection');\n logAnalyticsEvent('disconnect', 'PeerConnection', {\n subscriberConnection: peerConnectionId\n });\n\n this._removeSubscriber(remoteSubscriberId);\n }; // @todo find out if we get onPeerDisconnected when a failure occurs.\n\n\n const onPeerConnectionFailure = /*#__PURE__*/function () {\n var _ref7 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(peerConnection, _ref6) {\n var reason, prefix, sessionInfo, _ref8, _ref8$remoteConnectio, remoteConnectionId, peerConnectionId, error, payload, logOptions, pc;\n\n return _regenerator.default.wrap(function _callee2$(_context2) {\n while (1) switch (_context2.prev = _context2.next) {\n case 0:\n reason = _ref6.reason, prefix = _ref6.prefix;\n sessionInfo = _this.session && _this.session.sessionInfo;\n\n if (!(prefix === 'ICEWorkflow' && sessionInfo && sessionInfo.reconnection && loaded)) {\n _context2.next = 5;\n break;\n }\n\n // @todo not sure this is the right thing to do\n logging.debug('Ignoring peer connection failure due to possibility of reconnection.');\n return _context2.abrupt(\"return\");\n\n case 5:\n _ref8 = getPeerConnectionMeta(peerConnection) || {}, _ref8$remoteConnectio = _ref8.remoteConnectionId, remoteConnectionId = _ref8$remoteConnectio === void 0 ? '(not found)' : _ref8$remoteConnectio, peerConnectionId = _ref8.peerConnectionId;\n error = interpretPeerConnectionError(undefined, reason, prefix, remoteConnectionId, 'Publisher');\n payload = {\n hasRelayCandidates: peerConnection && peerConnection.hasRelayCandidates()\n };\n logOptions = {\n failureReason: prefix || 'PeerConnectionError',\n failureCode: error.code,\n failureMessage: error.message\n };\n\n if (state.isPublishing()) {\n // We're already publishing so this is a Non-fatal failure, must be p2p and one of our\n // peerconnections failed\n logOptions.reason = 'Non-fatal';\n } else {\n _this.trigger('publishComplete', error);\n }\n\n logConnectivityEvent('Failure', payload, logOptions);\n OTErrorClass.handleJsException({\n errorMsg: `OT.Publisher PeerConnection Error: ${reason}`,\n code: error.code,\n target: _this,\n analytics\n });\n _context2.next = 14;\n return peerConnectionsAsync[peerConnectionId];\n\n case 14:\n pc = _context2.sent;\n pc.destroy();\n delete peerConnectionsAsync[peerConnectionId];\n\n case 17:\n case \"end\":\n return _context2.stop();\n }\n }, _callee2);\n }));\n\n return function onPeerConnectionFailure(_x, _x2) {\n return _ref7.apply(this, arguments);\n };\n }();\n\n const isRoutedToRelayedTransitionComplete = peerConnection => {\n const isAdaptiveEnabled = this.session.sessionInfo.isAdaptiveEnabled;\n return isAdaptiveEnabled && peerConnection.getSourceStreamId() === 'P2P';\n };\n\n const onIceRestartSuccess = peerConnection => {\n const _getPeerConnectionMet3 = getPeerConnectionMeta(peerConnection),\n remoteConnectionId = _getPeerConnectionMet3.remoteConnectionId;\n\n logRepublish('Success', {\n remoteConnectionId\n });\n };\n\n const onIceRestartFailure = peerConnection => {\n const _getPeerConnectionMet4 = getPeerConnectionMeta(peerConnection),\n remoteConnectionId = _getPeerConnectionMet4.remoteConnectionId;\n\n logRepublish('Failure', {\n reason: 'ICEWorkflow',\n 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.',\n remoteConnectionId\n });\n };\n\n const onIceConnectionStateChange = /*#__PURE__*/function () {\n var _ref9 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(newState, peerConnection) {\n var isAdaptiveEnabled, sourceStreamId, isMantisConnected, isP2PConnected;\n return _regenerator.default.wrap(function _callee3$(_context3) {\n while (1) switch (_context3.prev = _context3.next) {\n case 0:\n isAdaptiveEnabled = _this.session.sessionInfo.isAdaptiveEnabled;\n sourceStreamId = peerConnection.getSourceStreamId();\n lastIceConnectionStates[sourceStreamId] = newState;\n\n if (newState === 'disconnected') {\n setTimeout(() => {\n const isSocketReconnecting = _this.session._.isSocketReconnecting;\n\n const socket = _this.session._.getSocket();\n\n const isSocketConnected = socket.is('connected') && !isSocketReconnecting();\n\n if (lastIceConnectionStates[sourceStreamId] === 'disconnected' && isSocketConnected) {\n const _getPeerConnectionMet5 = getPeerConnectionMeta(peerConnection),\n remoteConnectionId = _getPeerConnectionMet5.remoteConnectionId;\n\n logRepublish('Attempt', {\n remoteConnectionId\n });\n peerConnection.iceRestart();\n }\n }, 2000);\n }\n\n if (!(newState === 'connected' && isAdaptiveEnabled)) {\n _context3.next = 12;\n break;\n }\n\n _context3.next = 7;\n return getMantisPeerConnection();\n\n case 7:\n isMantisConnected = _context3.sent.iceConnectionStateIsConnected();\n _context3.next = 10;\n return getP2pPeerConnection();\n\n case 10:\n isP2PConnected = _context3.sent.iceConnectionStateIsConnected();\n\n if (isMantisConnected && isP2PConnected) {\n _stopSendingRtpToMantis();\n }\n\n case 12:\n case \"end\":\n return _context3.stop();\n }\n }, _callee3);\n }));\n\n return function onIceConnectionStateChange(_x3, _x4) {\n return _ref9.apply(this, arguments);\n };\n }();\n\n const onPeerConnected = peerConnection => {\n if (isRoutedToRelayedTransitionComplete(peerConnection)) {\n logRoutedToRelayedTransition('Success');\n }\n }; // / Private Helpers\n // Assigns +stream+ to this publisher. The publisher listens for a bunch of events on the stream\n // so it can respond to changes.\n\n\n const assignStream = stream => {\n // the Publisher only expects a stream in the PublishingToSession state\n if (state.current !== 'PublishingToSession') {\n throw new Error('assignStream called when publisher is not successfully publishing');\n }\n\n streamCleanupJobs.releaseAll();\n this.stream = stream;\n this.stream.on('destroyed', this.disconnect, this);\n streamCleanupJobs.add(() => {\n if (this.stream) {\n this.stream.off('destroyed', this.disconnect, this);\n }\n });\n state.set('Publishing');\n widgetView.loading(!loaded);\n publishStartTime = new Date();\n this.dispatchEvent(new Events.StreamEvent('streamCreated', stream, null, false));\n logConnectivityEvent('Success');\n this.trigger('publishComplete', null, this);\n };\n /**\n * Provides the peer connection associated to the given peerConnectionId.\n *\n * It there is no PC associated it creates a new one and stores it so that the next call returns\n * the same instance.\n *\n * @param {Object} configuration\n * @param {string} configuration.peerConnectionId\n * @returns {Promise}\n */\n\n\n const createPeerConnection = (_ref10) => {\n let peerConnectionId = _ref10.peerConnectionId,\n send = _ref10.send,\n log = _ref10.log,\n logQoS = _ref10.logQoS,\n sourceStreamId = _ref10.sourceStreamId;\n\n if (getPeerConnectionById(peerConnectionId)) {\n return Promise.reject(new Error('PeerConnection already exists'));\n } // Calculate the number of streams to use. 1 for normal, >1 for Simulcast\n\n\n const capableSimulcastStreams = calculateCapableSimulcastStreams({\n browserName: OTHelpers.env.name,\n isScreenSharing,\n isCustomVideoTrack,\n sessionInfo: this.session.sessionInfo,\n constraints: properties.constraints,\n videoDimensions: getVideoDimensions()\n });\n peerConnectionsAsync[peerConnectionId] = Promise.all([this.session._.getIceConfig(), this.session._.getVideoCodecsCompatible(webRTCStream)]).then((_ref11) => {\n let iceConfig = _ref11[0],\n videoCodecsCompatible = _ref11[1];\n let pcStream = webRTCStream;\n\n if (!videoCodecsCompatible) {\n pcStream = webRTCStream.clone();\n\n const _pcStream$getVideoTra = pcStream.getVideoTracks(),\n videoTrack = _pcStream$getVideoTra[0];\n\n if (videoTrack) {\n videoTrack.stop();\n pcStream.removeTrack(videoTrack);\n }\n }\n\n const peerConnection = new PublisherPeerConnection({\n iceConfig,\n sendMessage: (type, content) => {\n if (type === 'offer') {\n this.trigger('connected');\n }\n\n send(type, content);\n },\n webRTCStream: pcStream,\n channels: properties.channels,\n capableSimulcastStreams,\n overrideSimulcastEnabled: options._enableSimulcast,\n logAnalyticsEvent: log,\n offerOverrides: {\n enableStereo: properties.enableStereo,\n audioBitrate: properties.audioBitrate,\n priorityVideoCodec: properties._priorityVideoCodec || this.session.sessionInfo.priorityVideoCodec,\n codecFlags: properties._codecFlags || this.session._.getCodecFlags()\n },\n // FIXME - Remove answerOverrides once b=AS is supported by Mantis\n answerOverrides: this.session.sessionInfo.p2pEnabled ? undefined : {\n audioBitrate: properties.audioBitrate\n },\n sourceStreamId,\n isP2pEnabled: this.session.sessionInfo.p2pEnabled\n });\n peerConnection.on({\n disconnected: () => onPeerDisconnected(peerConnection),\n error: (_ref12) => {\n let reason = _ref12.reason,\n prefix = _ref12.prefix;\n return onPeerConnectionFailure(peerConnection, {\n reason,\n prefix\n });\n },\n qos: logQoS,\n iceRestartSuccess: () => onIceRestartSuccess(peerConnection),\n iceRestartFailure: () => onIceRestartFailure(peerConnection),\n iceConnectionStateChange: newState => onIceConnectionStateChange(newState, peerConnection),\n audioAcquisitionProblem: () => {\n // will be only triggered in Chrome\n audioAcquisitionProblemDetected = true;\n this.trigger('audioAcquisitionProblem', {\n method: 'getStats'\n });\n }\n });\n peerConnection.once('connected', () => onPeerConnected(peerConnection));\n return new Promise((resolve, reject) => {\n const rejectOnError = err => {\n reject(err);\n };\n\n peerConnection.once('error', rejectOnError);\n peerConnection.init(rumorIceServers, err => {\n if (err) {\n return reject(err);\n }\n\n peerConnection.off('error', rejectOnError);\n resolve(peerConnection);\n return undefined;\n });\n });\n });\n return getPeerConnectionById(peerConnectionId);\n };\n\n const getAllPeerConnections = () => Promise.all(Object.keys(peerConnectionsAsync).map(getPeerConnectionById));\n\n const getPeerConnectionsBySubscriber = subscriberId => getAllPeerConnections().then(peerConnections => peerConnections.filter(peerConnection => getPeerConnectionMeta(peerConnection).remoteSubscriberId === subscriberId));\n\n const getPeerConnectionById = id => peerConnectionsAsync[id];\n\n const getPeerConnectionBySourceStreamId = sourceStreamId => {\n // Find the peerConnectionId which includes the sourceStreamId that we're looking for.\n const peerConnectionId = Object.keys(peerConnectionsAsync).find(key => key.endsWith(`~${sourceStreamId}`));\n return peerConnectionsAsync[peerConnectionId];\n };\n\n const getMantisPeerConnection = () => getPeerConnectionBySourceStreamId('MANTIS');\n\n const getP2pPeerConnection = () => getPeerConnectionBySourceStreamId('P2P');\n\n let chromeMixin = createChromeMixin(this, {\n name: properties.name,\n publishAudio: properties.publishAudio,\n publishVideo: properties.publishVideo,\n audioSource: properties.audioSource,\n showControls: properties.showControls,\n shouldAllowAudio,\n logAnalyticsEvent\n });\n\n const reset = () => {\n this.off('publishComplete', refreshAudioVideoUI);\n\n if (chromeMixin) {\n chromeMixin.reset();\n }\n\n streamCleanupJobs.releaseAll();\n this.disconnect();\n microphone = null;\n cleanupLocalStream();\n webRTCStream = null;\n\n if (widgetView) {\n widgetView.destroy();\n widgetView = null;\n }\n\n if (this.session) {\n this._.unpublishFromSession(this.session, 'reset');\n }\n\n this.id = null;\n this.stream = null;\n loaded = false;\n this.session = null;\n\n if (!state.isDestroyed()) {\n state.set('NotPublishing');\n }\n };\n\n const hasVideo = () => {\n if (!webRTCStream || webRTCStream.getVideoTracks().length === 0) {\n return false;\n }\n\n return webRTCStream.getVideoTracks().reduce((isEnabled, track) => isEnabled && !track.muted && track.enabled && track.readyState !== 'ended', properties.publishVideo);\n };\n\n const hasAudio = () => {\n if (!webRTCStream || webRTCStream.getAudioTracks().length === 0) {\n return false;\n }\n\n return webRTCStream.getAudioTracks().length > 0 && webRTCStream.getAudioTracks().reduce((isEnabled, track) => isEnabled && !track.muted && track.enabled && track.readyState !== 'ended', properties.publishAudio);\n };\n\n const refreshAudioVideoUI = activeReason => {\n if (widgetView) {\n widgetView.audioOnly(!hasVideo());\n widgetView.showPoster(!hasVideo());\n }\n\n if (chromeMixin) {\n chromeMixin.setAudioOnly(!hasVideo() && hasAudio());\n }\n\n if (this.stream) {\n this.stream.setChannelActiveState('audio', hasAudio(), activeReason);\n this.stream.setChannelActiveState('video', hasVideo(), activeReason);\n } else {\n this.once('publishComplete', refreshAudioVideoUI);\n }\n };\n\n const _getStatsWrapper = (reportType, callback) => {\n let isRtcStatsReport = false;\n\n if (typeof reportType === 'function') {\n /* eslint-disable-next-line no-param-reassign */\n callback = reportType;\n } else {\n isRtcStatsReport = reportType === 'rtcStatsReport';\n }\n\n if (isRtcStatsReport) {\n notifyGetRtcStatsCalled();\n } else {\n notifyGetStatsCalled();\n }\n\n if (isRtcStatsReport && !isGetRtcStatsReportSupported) {\n const errorCode = ExceptionCodes.GET_RTC_STATS_REPORT_NOT_SUPPORTED;\n callback(otError(Errors.GET_RTC_STATS_REPORT_NOT_SUPPORTED, new Error(OTErrorClass.getTitleByCode(errorCode)), errorCode));\n return;\n }\n\n getAllPeerConnections().then(peerConnections => {\n if (peerConnections.length === 0) {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n throw otError(Errors.PEER_CONNECTION_NOT_CONNECTED, new Error(OTErrorClass.getTitleByCode(errorCode)), errorCode);\n }\n\n return peerConnections;\n }).then(peerConnections => {\n const isAdaptiveEnabled = this.session.sessionInfo.isAdaptiveEnabled;\n\n if (!isAdaptiveEnabled) {\n return peerConnections;\n } // When the session is adaptive, we only return stats for the active peer connection.\n\n\n return peerConnections.filter(pc => pc.getSourceStreamId() === activeSourceStreamId);\n }).then(peerConnections => Promise.all(peerConnections.map(peerConnection => (isRtcStatsReport ? promisify(peerConnection.getRtcStatsReport.bind(peerConnection)) : promisify(peerConnection.getStats.bind(peerConnection)))().then(stats => ({\n pc: peerConnection,\n stats\n }))))).then(pcsAndStats => {\n // @todo this publishStartTime is going to be so wrong in P2P\n const startTimestamp = publishStartTime ? publishStartTime.getTime() : Date.now();\n const results = pcsAndStats.map((_ref13) => {\n let pc = _ref13.pc,\n stats = _ref13.stats;\n\n const _getPeerConnectionMet6 = getPeerConnectionMeta(pc),\n remoteConnectionId = _getPeerConnectionMet6.remoteConnectionId,\n remoteSubscriberId = _getPeerConnectionMet6.remoteSubscriberId;\n\n return assign(remoteConnectionId.match(/^symphony\\./) ? {} : {\n subscriberId: remoteSubscriberId,\n connectionId: remoteConnectionId\n }, isRtcStatsReport ? {\n rtcStatsReport: stats\n } : {\n stats: getStatsHelpers.normalizeStats(stats, false, startTimestamp)\n });\n });\n callback(null, results);\n }).catch(callback);\n };\n\n const _getStats = callback => _getStatsWrapper(callback);\n\n const _getRtcStatsReport = callback => _getStatsWrapper('rtcStatsReport', callback);\n\n const _createStream = (sourceStreamId, completionHandler) => {\n this.session._.streamCreate(properties.name || '', this.streamId, properties.audioFallbackEnabled, streamChannels, properties.minVideoBitrate, sourceStreamId, completionHandler);\n };\n\n const _stopSendingRtpToMantis = /*#__PURE__*/function () {\n var _ref14 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() {\n var peerConnection;\n return _regenerator.default.wrap(function _callee5$(_context5) {\n while (1) switch (_context5.prev = _context5.next) {\n case 0:\n _context5.next = 2;\n return getMantisPeerConnection();\n\n case 2:\n peerConnection = _context5.sent;\n\n if (!peerConnection) {\n _context5.next = 9;\n break;\n }\n\n setTimeout( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {\n return _regenerator.default.wrap(function _callee4$(_context4) {\n while (1) switch (_context4.prev = _context4.next) {\n case 0:\n _context4.next = 2;\n return peerConnection.changeMediaDirectionToInactive();\n\n case 2:\n case \"end\":\n return _context4.stop();\n }\n }, _callee4);\n })), STOP_SENDING_MEDIA_DELAY);\n\n _this.trigger('sourceStreamIdChanged', 'P2P'); // In FF, when the media direction is changed to inactive, it stops sending RTCP.\n // This causes that after ~60 seconds, MANTIS considers the stream is inactive\n // and destroys it.\n // As a workaround, we are going to send RTP and RTCP every 30 seconds to keep the\n // connection alive. See: OPENTOK-44341\n\n\n if (!OTHelpers.env.isFirefox) {\n _context5.next = 9;\n break;\n }\n\n _context5.next = 9;\n return _keepSendingRtcpToMantis();\n\n case 9:\n case \"end\":\n return _context5.stop();\n }\n }, _callee5);\n }));\n\n return function _stopSendingRtpToMantis() {\n return _ref14.apply(this, arguments);\n };\n }();\n\n const _restartSendingRtpToMantis = /*#__PURE__*/function () {\n var _ref16 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {\n var peerConnection;\n return _regenerator.default.wrap(function _callee6$(_context6) {\n while (1) switch (_context6.prev = _context6.next) {\n case 0:\n _context6.next = 2;\n return getMantisPeerConnection();\n\n case 2:\n peerConnection = _context6.sent;\n\n if (!peerConnection) {\n _context6.next = 8;\n break;\n }\n\n _context6.next = 6;\n return peerConnection.changeMediaDirectionToRecvOnly();\n\n case 6:\n if (_keepSendingRtcpToMantisTimeout) {\n clearTimeout(_keepSendingRtcpToMantisTimeout);\n }\n\n _this.trigger('sourceStreamIdChanged', 'MANTIS');\n\n case 8:\n case \"end\":\n return _context6.stop();\n }\n }, _callee6);\n }));\n\n return function _restartSendingRtpToMantis() {\n return _ref16.apply(this, arguments);\n };\n }();\n\n const _keepSendingRtcpToMantis = /*#__PURE__*/function () {\n var _ref17 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() {\n var peerConnection;\n return _regenerator.default.wrap(function _callee8$(_context8) {\n while (1) switch (_context8.prev = _context8.next) {\n case 0:\n _context8.next = 2;\n return getMantisPeerConnection();\n\n case 2:\n peerConnection = _context8.sent;\n\n if (peerConnection) {\n _keepSendingRtcpToMantisTimeout = setTimeout( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7() {\n return _regenerator.default.wrap(function _callee7$(_context7) {\n while (1) switch (_context7.prev = _context7.next) {\n case 0:\n if (!(activeSourceStreamId === 'P2P')) {\n _context7.next = 9;\n break;\n }\n\n _context7.next = 3;\n return peerConnection.changeMediaDirectionToRecvOnly();\n\n case 3:\n _context7.next = 5;\n return promiseDelay(STOP_SENDING_MEDIA_DELAY);\n\n case 5:\n _context7.next = 7;\n return peerConnection.changeMediaDirectionToInactive();\n\n case 7:\n _context7.next = 9;\n return _keepSendingRtcpToMantis();\n\n case 9:\n case \"end\":\n return _context7.stop();\n }\n }, _callee7);\n })), KEEP_SENDING_RTCP_DELAY);\n }\n\n case 4:\n case \"end\":\n return _context8.stop();\n }\n }, _callee8);\n }));\n\n return function _keepSendingRtcpToMantis() {\n return _ref17.apply(this, arguments);\n };\n }();\n\n this.publish = targetElement => {\n logging.debug('OT.Publisher: publish');\n\n if (state.isAttemptingToPublish() || state.isPublishing()) {\n reset();\n }\n\n state.set('GetUserMedia');\n\n if (properties.style) {\n this.setStyle(properties.style, null, true);\n }\n\n properties.classNames = 'OT_root OT_publisher'; // Defer actually creating the publisher DOM nodes until we know\n // the DOM is actually loaded.\n\n EnvironmentLoader.onLoad(() => {\n logging.debug('OT.Publisher: publish: environment loaded'); // @note If ever replacing the widgetView with a new one elsewhere, you'll need to be\n // mindful that audioLevelBehaviour has a reference to this one, and it will need to be\n // updated accordingly.\n // widgetView = new WidgetView(targetElement, properties);\n\n widgetView = new WidgetView(targetElement, (0, _extends2.default)({}, properties, {\n widgetType: 'publisher'\n }));\n\n if (shouldAllowAudio) {\n audioLevelBehaviour({\n publisher: this,\n widgetView\n });\n }\n\n widgetView.on('error', onVideoError);\n this.id = widgetView.domId();\n this.element = widgetView.domElement;\n\n if (this.element && chromeMixin) {\n // Only create the chrome if we have an element to insert it into\n // for insertDefautlUI:false we don't create the chrome\n chromeMixin.init(widgetView);\n }\n\n widgetView.on('videoDimensionsChanged', (oldValue, newValue) => {\n if (this.stream) {\n this.stream.setVideoDimensions(newValue.width, newValue.height);\n }\n\n this.dispatchEvent(new Events.VideoDimensionsChangedEvent(this, oldValue, newValue));\n });\n widgetView.on('mediaStopped', track => {\n const event = new Events.MediaStoppedEvent(this, track);\n this.dispatchEvent(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (track) {\n const kind = String(track.kind).toLowerCase(); // If we are publishing this kind when the track stops then\n // make sure we start publishing again if we switch to a new track\n\n if (kind === 'audio') {\n updateAudio();\n } else if (kind === 'video') {\n updateVideo();\n } else {\n logging.warn(`Track with invalid kind has ended: ${track.kind}`);\n }\n\n return;\n }\n\n if (this.session) {\n this._.unpublishFromSession(this.session, 'mediaStopped');\n } else {\n this.destroy('mediaStopped');\n }\n });\n widgetView.on('videoElementCreated', element => {\n const event = new Events.VideoElementCreatedEvent(element);\n this.dispatchEvent(event);\n });\n getUserMedia().catch(userMediaError).then(stream => {\n // this comes from deviceHelpers.shouldAskForDevices in a round-about way\n audioDevices = processedOptions.audioDevices;\n videoDevices = processedOptions.videoDevices;\n onStreamAvailable(stream);\n\n if (!properties.publishVideo) {\n this._toggleVideo(properties.publishVideo);\n }\n\n if (!isScreenSharing && !isCustomVideoTrack) {\n currentDeviceId = getDeviceIdFromStream(stream, videoDevices);\n }\n\n return bindVideo().catch(error => {\n if (error instanceof CancellationError) {\n // If we get a CancellationError, it means something newer tried\n // to bindVideo before the old one succeeded, perhaps they called\n // switchTracks.. It should be rare, and they shouldn't be doing\n // this before loaded, but we'll handle it anyway.\n return undefined;\n }\n\n throw error;\n }).then(() => {\n onLoaded();\n\n if (!state.isDestroyed()) {\n this.trigger('initSuccess');\n this.trigger('loaded', this);\n }\n }, err => {\n logging.error(`OT.Publisher.publish failed to bind video: ${err}`);\n onLoadFailure(err);\n });\n });\n });\n return this;\n };\n\n this._setScalableValues = /*#__PURE__*/function () {\n var _ref19 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9(scalableParam, scalableValues) {\n var senders, sender, sendParameters;\n return _regenerator.default.wrap(function _callee9$(_context9) {\n while (1) switch (_context9.prev = _context9.next) {\n case 0:\n _context9.next = 2;\n return getAllPeerConnections().then(peerConnections => peerConnections[0].getSenders().filter((_ref20) => {\n let kind = _ref20.track.kind;\n return kind === 'video';\n }));\n\n case 2:\n senders = _context9.sent;\n sender = senders[0];\n sendParameters = sender.getParameters();\n sendParameters.encodings.forEach((encoding, index) => {\n encoding[scalableParam] = scalableValues[index]; // eslint-disable-line no-param-reassign\n });\n _context9.next = 8;\n return sender.setParameters(sendParameters);\n\n case 8:\n case \"end\":\n return _context9.stop();\n }\n }, _callee9);\n }));\n\n return function (_x5, _x6) {\n return _ref19.apply(this, arguments);\n };\n }();\n\n this._setScalableFramerates = /*#__PURE__*/function () {\n var _ref21 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10(frameRates) {\n var framerateValues;\n return _regenerator.default.wrap(function _callee10$(_context10) {\n while (1) switch (_context10.prev = _context10.next) {\n case 0:\n framerateValues = normalizeScalableValues(frameRates);\n\n if (!(framerateValues && areValidFramerates(framerateValues))) {\n _context10.next = 4;\n break;\n }\n\n _context10.next = 4;\n return _this._setScalableValues('maxFramerate', framerateValues);\n\n case 4:\n case \"end\":\n return _context10.stop();\n }\n }, _callee10);\n }));\n\n return function (_x7) {\n return _ref21.apply(this, arguments);\n };\n }();\n\n this._setScalableVideoLayers = /*#__PURE__*/function () {\n var _ref22 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11(videoLayers) {\n var videoLayerValues;\n return _regenerator.default.wrap(function _callee11$(_context11) {\n while (1) switch (_context11.prev = _context11.next) {\n case 0:\n videoLayerValues = normalizeScalableValues(videoLayers);\n\n if (!(videoLayerValues && areValidResolutionScales(videoLayerValues))) {\n _context11.next = 4;\n break;\n }\n\n _context11.next = 4;\n return _this._setScalableValues('scaleResolutionDownBy', videoLayerValues);\n\n case 4:\n case \"end\":\n return _context11.stop();\n }\n }, _callee11);\n }));\n\n return function (_x8) {\n return _ref22.apply(this, arguments);\n };\n }();\n\n const areValidFramerates = framerates => {\n let previousFps = 0; // Only 15 and 30 fps are valid values and it cannot decrease when upscaling resolutions\n\n return framerates.every(fps => {\n if (fps !== 15 && fps !== 30 || fps < previousFps) {\n return false;\n }\n\n previousFps = fps;\n return true;\n });\n };\n\n const areValidResolutionScales = scales => {\n // Maximum to downscale is 16 so previous scale should not be equal or greater to 17\n let previousScale = 17;\n return scales.every(scale => {\n // Only downscale values; i.e. <= 1 means to upscale, which is not valid\n if (scale < 1 || scale >= previousScale) {\n return false;\n }\n\n previousScale = scale;\n return true;\n });\n };\n\n const normalizeScalableValues = scalableValues => {\n let normalizedValues; // API only accepts a colon separated value string\n\n if (typeof scalableValues !== 'string') {\n return normalizedValues;\n }\n\n const scalableValuesArr = scalableValues.split(':'); // It cannot be empty nor larger than 3 values (HD, VGA and QVGA)\n\n if (scalableValuesArr.length === 0 || scalableValuesArr.length > 3) {\n return normalizedValues;\n }\n\n if (!scalableValuesArr.every(value => !isNaN(value))) {\n return normalizedValues;\n }\n\n normalizedValues = scalableValuesArr.map(value => parseInt(value, 10)).reverse();\n return normalizedValues;\n };\n\n const haveWorkingTracks = type => webRTCStream && webRTCStream[`get${capitalize(type)}Tracks`]().length > 0 && webRTCStream[`get${capitalize(type)}Tracks`]().every(track => track.readyState !== 'ended');\n\n const updateAudio = activeReason => {\n const shouldSendAudio = haveWorkingTracks('audio') && properties.publishAudio;\n\n if (chromeMixin) {\n chromeMixin.setMuted(!shouldSendAudio);\n }\n\n if (microphone) {\n microphone.muted(!shouldSendAudio);\n }\n\n refreshAudioVideoUI(activeReason);\n };\n /**\n * Starts publishing audio (if it is currently not being published)\n * when the value
is true
; stops publishing audio\n * (if it is currently being published) when the value
is false
.\n *\n * @param {Boolean} value Whether to start publishing audio (true
)\n * or not (false
).\n *\n * @see OT.initPublisher()\n * @see Stream.hasAudio\n * @see StreamPropertyChangedEvent\n * @method #publishAudio\n * @memberOf Publisher\n */\n\n\n this.publishAudio = value => {\n logAnalyticsEvent('publishAudio', 'Attempt', {\n publishAudio: value\n });\n properties.publishAudio = value;\n\n try {\n updateAudio();\n logAnalyticsEvent('publishAudio', 'Success', {\n publishAudio: value\n });\n } catch (e) {\n logAnalyticsEvent('publishAudio', 'Failure', {\n message: e.message\n });\n }\n\n return this;\n };\n\n let updateVideoSenderParametersSentinel; // keeps track of if the client has called mediaStreamTrack.stop(), so that we don't restart\n // the camera if they then call publishVideo(true)\n\n let isTrackManuallyStopped = false;\n\n const updateVideo = () => {\n const shouldSendVideo = haveWorkingTracks('video') && properties.publishVideo;\n\n if (env.name === 'Chrome' && env.version >= 69) {\n (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12() {\n var executionSentinel, peerConnections;\n return _regenerator.default.wrap(function _callee12$(_context12) {\n while (1) switch (_context12.prev = _context12.next) {\n case 0:\n if (updateVideoSenderParametersSentinel) {\n updateVideoSenderParametersSentinel.cancel();\n }\n\n updateVideoSenderParametersSentinel = new Cancellation();\n executionSentinel = updateVideoSenderParametersSentinel;\n _context12.next = 5;\n return getAllPeerConnections();\n\n case 5:\n peerConnections = _context12.sent;\n\n if (!executionSentinel.isCanceled()) {\n // only proceed if we weren't canceled during the async operation above\n peerConnections.forEach(peerConnection => {\n setEncodersActiveState(peerConnection, shouldSendVideo);\n });\n }\n\n case 7:\n case \"end\":\n return _context12.stop();\n }\n }, _callee12);\n }))();\n }\n\n if (webRTCStream) {\n webRTCStream.getVideoTracks().forEach(track => {\n track.enabled = shouldSendVideo; // eslint-disable-line no-param-reassign\n\n if (track.isCreatedCanvas) {\n // eslint-disable-next-line no-param-reassign\n track.enabled = false;\n }\n });\n }\n\n refreshAudioVideoUI();\n };\n\n let currentDeviceId;\n this._toggleVideo = blockCallsUntilComplete( /*#__PURE__*/function () {\n var _ref24 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee13(shouldHaveVideo) {\n var oldTrack, _properties$videoDime, videoDimensions, canvasTrack, vidDevices, newTrack;\n\n return _regenerator.default.wrap(function _callee13$(_context13) {\n while (1) switch (_context13.prev = _context13.next) {\n case 0:\n if (!(isScreenSharing || isCustomVideoTrack || isTrackManuallyStopped)) {\n _context13.next = 2;\n break;\n }\n\n return _context13.abrupt(\"return\");\n\n case 2:\n oldTrack = getCurrentTrack();\n\n if (oldTrack) {\n _context13.next = 5;\n break;\n }\n\n throw otError(Errors.NOT_SUPPORTED, new Error('Publisher._toggleVideo cannot toggleVideo when you have no video source.'));\n\n case 5:\n if (!(oldTrack.readyState === 'ended')) {\n _context13.next = 8;\n break;\n }\n\n isTrackManuallyStopped = true;\n return _context13.abrupt(\"return\");\n\n case 8:\n // create a canvas and grab the track from it to pass into video\n // resize the canvas so that we don't emit a 'streamPropertyChanged' event\n _properties$videoDime = properties.videoDimensions, videoDimensions = _properties$videoDime === void 0 ? getVideoDimensions() : _properties$videoDime;\n _context13.prev = 9;\n canvasTrack = createCanvasVideoTrack(videoDimensions);\n _context13.next = 16;\n break;\n\n case 13:\n _context13.prev = 13;\n _context13.t0 = _context13[\"catch\"](9);\n return _context13.abrupt(\"return\");\n\n case 16:\n _context13.next = 18;\n return getVideoDevices();\n\n case 18:\n vidDevices = _context13.sent;\n\n if (shouldHaveVideo && OTHelpers.env.isAndroid && OTHelpers.env.isChrome) {\n // On Chrome on Android you need to stop the previous video track OPENTOK-37206\n if (oldTrack && oldTrack.stop) {\n oldTrack.stop();\n }\n } // store the current deviceId to reacquire the video later\n\n\n if (!shouldHaveVideo) {\n currentDeviceId = vidDevices.find(device => device.label === oldTrack.label).deviceId;\n }\n\n if (!(currentDeviceId && vidDevices.findIndex(device => device.deviceId === currentDeviceId) === -1)) {\n _context13.next = 23;\n break;\n }\n\n throw otError(Errors.NO_DEVICES_FOUND, new Error('Previous device no longer available - deviceId not found'));\n\n case 23:\n privateEvents.emit('streamDestroy');\n\n if (!shouldHaveVideo) {\n _context13.next = 30;\n break;\n }\n\n _context13.next = 27;\n return getTrackFromDeviceId(currentDeviceId);\n\n case 27:\n _context13.t1 = _context13.sent;\n _context13.next = 31;\n break;\n\n case 30:\n _context13.t1 = canvasTrack;\n\n case 31:\n newTrack = _context13.t1;\n _context13.next = 34;\n return replaceTrackAndUpdate(oldTrack, newTrack);\n\n case 34:\n case \"end\":\n return _context13.stop();\n }\n }, _callee13, null, [[9, 13]]);\n }));\n\n return function (_x9) {\n return _ref24.apply(this, arguments);\n };\n }());\n /**\n * Starts publishing video (if it is currently not being published)\n * when the value
is true
; stops publishing video\n * (if it is currently being published) when the value
is false
.\n *\n * @param {Boolean} value Whether to start publishing video (true
)\n * or not (false
).\n *\n * @see OT.initPublisher()\n * @see Stream.hasVideo\n * @see StreamPropertyChangedEvent\n * @method #publishVideo\n * @memberOf Publisher\n */\n\n this.publishVideo = value => {\n logAnalyticsEvent('publishVideo', 'Attempt', {\n publishVideo: value\n });\n properties.publishVideo = value;\n\n try {\n this._toggleVideo(properties.publishVideo);\n\n updateVideo();\n logAnalyticsEvent('publishVideo', 'Success', {\n publishVideo: value\n });\n } catch (e) {\n logAnalyticsEvent('publishVideo', 'Failure', {\n message: e.message\n });\n }\n\n return this;\n };\n /**\n * Sets the content hint for the video track of the publisher's stream. This allows browsers\n * to use encoding or processing methods more appropriate to the type of content.\n * \n * Use this method to change the video content hit dynamically. Set the initial video content\n * hit by setting the videoContentHint
property of the options passed into the\n * OT.initPublisher() method.\n *
\n * Chrome 60+, Safari 12.1+, Edge 79+, and Opera 47+ support video content hints.\n *\n * @param {String} videoContentHint You can set this to one of the following values:\n *
\n *
\n * - \n *
\"\"
— No hint is provided.\n * \n * - \n *
\"motion\"
— The track should be treated as if it contains video\n * where motion is important.\n * \n * - \n *
\"detail\"
— The track should be treated as if video details\n * are extra important.\n * \n * - \n *
\"text\"
— The track should be treated as if text details are\n * extra important.\n * \n *
\n *\n * @see Publisher.getVideoContentHint()\n * @see OT.initPublisher()\n * @method #setVideoContentHint\n * @memberOf Publisher\n */\n\n\n this.setVideoContentHint = videoContentHint => setVideoContentHint(webRTCStream, videoContentHint);\n /**\n * Returns the content hint for the video track.\n *\n * @return {String} One of the following values: \"\"
,\n * \"motion\"
, \"detail
, or \"text\"
.\n * @see Publisher.setVideoContentHint()\n * @method #getVideoContentHint\n * @memberOf Publisher\n */\n\n\n this.getVideoContentHint = () => getVideoContentHint(webRTCStream);\n /**\n * Deletes the Publisher object and removes it from the HTML DOM.\n * \n * The Publisher object dispatches a destroyed
event when the DOM\n * element is removed.\n *
\n * @method #destroy\n * @memberOf Publisher\n * @return {Publisher} The Publisher.\n */\n\n\n this.destroy = function (\n /* unused */\n reason, quiet) {\n // @todo OPENTOK-36652 this.session should not be needed here\n if (state.isAttemptingToPublish() && this.session) {\n logConnectivityEvent('Cancel', {\n reason: 'destroy'\n });\n }\n\n if (state.isDestroyed()) {\n return this;\n }\n\n state.set('Destroyed');\n reset();\n\n if (processedOptions) {\n processedOptions.off();\n processedOptions = null;\n }\n\n if (chromeMixin) {\n chromeMixin.destroy();\n chromeMixin = null;\n }\n\n if (privateEvents) {\n privateEvents.off();\n privateEvents = null;\n }\n\n if (quiet !== true) {\n this.dispatchEvent(new Events.DestroyedEvent(eventNames.PUBLISHER_DESTROYED, this, reason));\n }\n\n this.off();\n return this;\n };\n /*\n * @methodOf Publisher\n * @private\n */\n\n\n this.disconnect = () => {\n Object.keys(peerConnectionsAsync).forEach(peerConnectionId => {\n const futurePeerConnection = getPeerConnectionById(peerConnectionId);\n delete peerConnectionsAsync[peerConnectionId];\n futurePeerConnection.then(peerConnection => this._removePeerConnection(peerConnection));\n });\n };\n\n this.processMessage = (type, fromConnectionId, message) => {\n const subscriberId = get(message, 'params.subscriber', fromConnectionId).replace(/^INVALID-STREAM$/, fromConnectionId);\n const peerId = get(message, 'content.peerId');\n const sourceStreamId = get(message, 'content.sourceStreamId', 'MANTIS'); // Symphony will not have a subscriberId so we'll fallback to using the connectionId for it.\n // Also fallback to the connectionId if it is equal to 'INVALID-STREAM' (See OPENTOK-30029).\n\n const peerConnectionId = `${subscriberId}~${peerId}~${sourceStreamId}`;\n logging.debug(`OT.Publisher.processMessage: Received ${type} from ${fromConnectionId} for ${peerConnectionId}`);\n logging.debug(message);\n const futurePeerConnection = getPeerConnectionById(peerConnectionId);\n\n const addPeerConnection = () => {\n const send = createSendMethod({\n socket: this.session._.getSocket(),\n uri: message.uri,\n content: {\n peerId,\n sourceStreamId\n }\n });\n\n const log = function log(action, variation, payload, logOptions, throttle) {\n if (logOptions === void 0) {\n logOptions = {};\n }\n\n const transformedOptions = (0, _extends2.default)({\n peerId,\n sourceStreamId\n }, logOptions);\n return logAnalyticsEvent(action, variation, payload, transformedOptions, throttle);\n };\n\n const logQoS = qos => {\n // We only log data from the active peer connection\n if (sourceStreamId !== activeSourceStreamId) {\n return;\n }\n\n recordQOS((0, _extends2.default)({}, qos, {\n peerId,\n remoteConnectionId: fromConnectionId,\n sourceStreamId\n }));\n };\n\n createPeerConnection({\n peerConnectionId,\n send,\n log,\n logQoS,\n sourceStreamId\n }).then(peerConnection => {\n setPeerConnectionMeta(peerConnection, {\n remoteConnectionId: fromConnectionId,\n remoteSubscriberId: subscriberId,\n peerId,\n sourceStreamId,\n peerConnectionId\n });\n peerConnection.processMessage(type, message); // Allow this runaway promise\n // http://bluebirdjs.com/docs/warning-explanations.html#warning-a-promise-was-created-in-a-handler-but-was-not-returned-from-it\n\n return null;\n }).catch(err => {\n logging.error('OT.Publisher failed to create a peerConnection', err);\n });\n };\n\n switch (type) {\n case 'unsubscribe':\n this._removeSubscriber(subscriberId);\n\n break;\n\n default:\n if (!futurePeerConnection) {\n addPeerConnection();\n } else {\n futurePeerConnection.then(peerConnection => peerConnection.processMessage(type, message));\n }\n\n break;\n }\n };\n /**\n * Returns the base-64-encoded string of PNG data representing the Publisher video.\n *\n * You can use the string as the value for a data URL scheme passed to the src parameter of\n * an image file, as in the following:
\n *\n * \n * var imgData = publisher.getImgData();\n *\n * var img = document.createElement(\"img\");\n * img.setAttribute(\"src\", \"data:image/png;base64,\" + imgData);\n * var imgWin = window.open(\"about:blank\", \"Screenshot\");\n * imgWin.document.write(\"<body></body>\");\n * imgWin.document.body.appendChild(img);\n *
\n *\n * @method #getImgData\n * @memberOf Publisher\n * @return {String} The base-64 encoded string. Returns an empty string if there is no video.\n */\n\n\n this.getImgData = function () {\n if (!loaded) {\n logging.error('OT.Publisher.getImgData: Cannot getImgData before the Publisher is publishing.');\n return null;\n }\n\n const video = widgetView && widgetView.video();\n return video ? video.imgData() : null;\n };\n\n const setNewStream = newStream => {\n cleanupLocalStream();\n webRTCStream = newStream;\n privateEvents.emit('streamChange');\n microphone = new Microphone(webRTCStream, !properties.publishAudio);\n };\n\n const defaultReplaceTrackLogic = peerConnection => {\n peerConnection.getSenders().forEach(sender => {\n if (sender.track.kind === 'audio' && webRTCStream.getAudioTracks().length) {\n return sender.replaceTrack(webRTCStream.getAudioTracks()[0]);\n } else if (sender.track.kind === 'video' && webRTCStream.getVideoTracks().length) {\n return sender.replaceTrack(webRTCStream.getVideoTracks()[0]);\n }\n\n return undefined;\n });\n };\n\n const replaceTracks = function replaceTracks(replaceTrackLogic) {\n if (replaceTrackLogic === void 0) {\n replaceTrackLogic = defaultReplaceTrackLogic;\n }\n\n return getAllPeerConnections().then(peerConnections => {\n const tasks = [];\n peerConnections.map(replaceTrackLogic);\n return Promise.all(tasks);\n });\n };\n\n {\n let videoIndex = 0;\n /**\n * Switches the video input source used by the publisher to the next one in the list\n * of available devices.\n * \n * This will result in an error (the Promise returned by the method is rejected) in the\n * following conditions:\n *
\n * - \n * The user denied access to the video input device.\n *
\n * - \n * The publisher is not using a camera video source. (The
videoSource
\n * option of the OT.initPublisher() method was\n * set to null
, false
, a MediaStreamTrack object, or\n * \"screen\"
).\n * \n * - \n * There are no video input devices (cameras) available.\n *
\n * - \n * There was an error acquiring video from the video input device.\n *
\n *
\n * \n *\n * @method #cycleVideo\n * @memberOf Publisher\n *\n * @return {Promise} A promise that resolves when the operation completes\n * successfully. The promise resolves with an object that has a\n * deviceId
property set to the device ID of the camera used:\n *\n * \n * publisher.cycleVideo().then(console.log);\n * // Output: {deviceId: \"967a86e52...\"}\n *
\n *\n * If there is an error, the promise is rejected.\n *\n * @see Publisher.setVideoSource()\n */\n\n this.cycleVideo = blockCallsUntilComplete( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee14() {\n var oldTrack, vidDevices, hasOtherVideoDevices, newVideoDevice, deviceId;\n return _regenerator.default.wrap(function _callee14$(_context14) {\n while (1) switch (_context14.prev = _context14.next) {\n case 0:\n if (!(OTHelpers.env.isLegacyEdge || !windowMock.RTCRtpSender || typeof windowMock.RTCRtpSender.prototype.replaceTrack !== 'function')) {\n _context14.next = 2;\n break;\n }\n\n throw otError(Errors.UNSUPPORTED_BROWSER, new Error('Publisher#cycleVideo is not supported in your browser.'), ExceptionCodes.UNABLE_TO_PUBLISH);\n\n case 2:\n if (!(isCustomVideoTrack || isScreenSharing)) {\n _context14.next = 4;\n break;\n }\n\n throw otError(Errors.NOT_SUPPORTED, new Error('Publisher#cycleVideo: The publisher is not using a camera video source'));\n\n case 4:\n oldTrack = getCurrentTrack();\n\n if (oldTrack) {\n _context14.next = 7;\n break;\n }\n\n throw otError(Errors.NOT_SUPPORTED, new Error('Publisher#cycleVideo cannot cycleVideo when you have no video source.'));\n\n case 7:\n videoIndex += 1;\n _context14.next = 10;\n return getVideoDevices();\n\n case 10:\n vidDevices = _context14.sent;\n // different devices return the cameras in different orders\n hasOtherVideoDevices = vidDevices.filter(device => device.label !== oldTrack.label).length > 0;\n\n if (hasOtherVideoDevices) {\n while (vidDevices[videoIndex % vidDevices.length].label === oldTrack.label) {\n videoIndex += 1;\n }\n }\n\n privateEvents.emit('streamDestroy');\n newVideoDevice = vidDevices[videoIndex % vidDevices.length];\n deviceId = newVideoDevice.deviceId;\n _context14.next = 18;\n return attemptToSetVideoTrack(deviceId);\n\n case 18:\n return _context14.abrupt(\"return\", {\n deviceId: currentDeviceId\n });\n\n case 19:\n case \"end\":\n return _context14.stop();\n }\n }, _callee14);\n })));\n }\n\n const replaceTrackAndUpdate = /*#__PURE__*/function () {\n var _ref26 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee15(oldTrack, newTrack) {\n var pcs, video;\n return _regenerator.default.wrap(function _callee15$(_context15) {\n while (1) switch (_context15.prev = _context15.next) {\n case 0:\n _context15.next = 2;\n return getAllPeerConnections();\n\n case 2:\n pcs = _context15.sent;\n _context15.next = 5;\n return Promise.all(pcs.map(pc => pc.findAndReplaceTrack(oldTrack, newTrack)));\n\n case 5:\n webRTCStream.addTrack(newTrack);\n webRTCStream.removeTrack(oldTrack);\n\n if (oldTrack && oldTrack.stop) {\n oldTrack.stop();\n }\n\n if (OTHelpers.env.name === 'Firefox' || OTHelpers.env.name === 'Safari') {\n // Local video freezes on old stream without this for some reason\n _this.videoElement().srcObject = null;\n _this.videoElement().srcObject = webRTCStream;\n }\n\n video = widgetView && widgetView.video();\n\n if (video) {\n video.refreshTracks();\n }\n\n privateEvents.emit('streamChange');\n updateVideo();\n\n case 13:\n case \"end\":\n return _context15.stop();\n }\n }, _callee15);\n }));\n\n return function replaceTrackAndUpdate(_x10, _x11) {\n return _ref26.apply(this, arguments);\n };\n }();\n\n const getTrackFromDeviceId = /*#__PURE__*/function () {\n var _ref27 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee16(deviceId) {\n var newOptions, _processedOptions2, getUserMediaHelper, newVideoStream;\n\n return _regenerator.default.wrap(function _callee16$(_context16) {\n while (1) switch (_context16.prev = _context16.next) {\n case 0:\n newOptions = cloneDeep(options);\n newOptions.audioSource = null;\n newOptions.videoSource = deviceId;\n processedOptions = processPubOptions(newOptions, 'OT.Publisher.getTrackFromDeviceId', () => state && state.isDestroyed());\n processedOptions.on({\n accessDialogOpened: onAccessDialogOpened,\n accessDialogClosed: onAccessDialogClosed\n });\n _processedOptions2 = processedOptions, getUserMediaHelper = _processedOptions2.getUserMedia;\n _context16.prev = 6;\n _context16.next = 9;\n return getUserMediaHelper();\n\n case 9:\n newVideoStream = _context16.sent;\n _context16.next = 15;\n break;\n\n case 12:\n _context16.prev = 12;\n _context16.t0 = _context16[\"catch\"](6);\n logging.error(_context16.t0);\n\n case 15:\n return _context16.abrupt(\"return\", newVideoStream && newVideoStream.getVideoTracks()[0]);\n\n case 16:\n case \"end\":\n return _context16.stop();\n }\n }, _callee16, null, [[6, 12]]);\n }));\n\n return function getTrackFromDeviceId(_x12) {\n return _ref27.apply(this, arguments);\n };\n }();\n\n const getCurrentTrack = () => {\n const _webRTCStream$getVide = webRTCStream.getVideoTracks(),\n currentTrack = _webRTCStream$getVide[0];\n\n return currentTrack;\n };\n\n const getVideoDevices = /*#__PURE__*/function () {\n var _ref28 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee17() {\n var devices, vidDevices;\n return _regenerator.default.wrap(function _callee17$(_context17) {\n while (1) switch (_context17.prev = _context17.next) {\n case 0:\n _context17.next = 2;\n return deviceHelpers.shouldAskForDevices();\n\n case 2:\n devices = _context17.sent;\n vidDevices = devices.videoDevices;\n\n if (!(!devices.video || !vidDevices || !vidDevices.length)) {\n _context17.next = 6;\n break;\n }\n\n throw otError(Errors.NO_DEVICES_FOUND, new Error('No video devices available'), ExceptionCodes.UNABLE_TO_PUBLISH);\n\n case 6:\n return _context17.abrupt(\"return\", vidDevices);\n\n case 7:\n case \"end\":\n return _context17.stop();\n }\n }, _callee17);\n }));\n\n return function getVideoDevices() {\n return _ref28.apply(this, arguments);\n };\n }();\n\n const replaceAudioTrack = (oldTrack, newTrack) => {\n if (newTrack) {\n webRTCStream.addTrack(newTrack);\n }\n\n if (oldTrack) {\n webRTCStream.removeTrack(oldTrack);\n }\n\n const video = widgetView && widgetView.video();\n\n if (video) {\n video.refreshTracks();\n }\n\n if (chromeMixin) {\n if (newTrack && !oldTrack) {\n chromeMixin.addAudioTrack();\n }\n\n if (oldTrack && !newTrack) {\n chromeMixin.removeAudioTrack();\n }\n }\n\n if (oldTrack && oldTrack.stop) {\n oldTrack.stop();\n }\n\n if (newTrack) {\n // Turn the audio back on if the audio track stopped because it was disconnected\n updateAudio();\n microphone = new Microphone(webRTCStream, !properties.publishAudio);\n }\n\n privateEvents.emit('streamChange');\n refreshAudioVideoUI();\n };\n\n const resetAudioSource = /*#__PURE__*/function () {\n var _ref29 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee18(audioTrack) {\n var audioDeviceId, newAudioTrack;\n return _regenerator.default.wrap(function _callee18$(_context18) {\n while (1) switch (_context18.prev = _context18.next) {\n case 0:\n audioDeviceId = audioTrack.getSettings().deviceId;\n _context18.prev = 1;\n _context18.next = 4;\n return _this.setAudioSource(audioDeviceId);\n\n case 4:\n // We need to add the onmute listener to the new audio track.\n newAudioTrack = webRTCStream.getAudioTracks()[0];\n\n if (newAudioTrack) {\n newAudioTrack.onmute = () => handleBuggedMutedLocalAudioTrack(newAudioTrack);\n\n newAudioTrack.onunmute = () => handleBuggedUnMutedLocalAudioTrack(newAudioTrack);\n }\n\n _context18.next = 11;\n break;\n\n case 8:\n _context18.prev = 8;\n _context18.t0 = _context18[\"catch\"](1);\n logging.error(_context18.t0);\n\n case 11:\n case \"end\":\n return _context18.stop();\n }\n }, _callee18, null, [[1, 8]]);\n }));\n\n return function resetAudioSource(_x13) {\n return _ref29.apply(this, arguments);\n };\n }(); // this should be called when we detect a mute event from a bugged device\n\n\n const handleBuggedMutedLocalAudioTrack = audioTrack => {\n let shouldRePublishVideo = false;\n\n if (properties.publishVideo && document.hidden) {\n shouldRePublishVideo = true; // turning the video off to prevent that videotrack is ended\n\n this.publishVideo(false);\n } // trigger the handler onVisibilityChange\n\n\n const visibilityHandler = /*#__PURE__*/function () {\n var _ref30 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee19() {\n return _regenerator.default.wrap(function _callee19$(_context19) {\n while (1) switch (_context19.prev = _context19.next) {\n case 0:\n if (document.hidden) {\n _context19.next = 5;\n break;\n }\n\n _context19.next = 3;\n return resetAudioSource(audioTrack);\n\n case 3:\n if (shouldRePublishVideo) {\n _this.publishVideo(true);\n }\n\n document.removeEventListener('visibilitychange', visibilityHandler);\n\n case 5:\n case \"end\":\n return _context19.stop();\n }\n }, _callee19);\n }));\n\n return function visibilityHandler() {\n return _ref30.apply(this, arguments);\n };\n }();\n\n document.addEventListener('visibilitychange', visibilityHandler);\n };\n\n const handleBuggedUnMutedLocalAudioTrack = audioTrack => {\n if (hasAudio()) {\n if (!hasVideo()) {\n // We only need to reset the audio source when the publisher is audio only.\n resetAudioSource(audioTrack);\n } else {\n // Inconsistenly the publisher shows a black frame after the incoming call with compact UI\n // ends. In order to unblock the video element we added this hack that needs to be\n // revisited.\n // We need to call pause and play again, and since we have a listener on the onpause event\n // that internally calls the play function, we only need to call pause(),\n // and the play will be automatically executed.\n this.videoElement().pause();\n this.session.trigger('gsmCallEnded');\n }\n }\n };\n /**\n * Switches the audio input source used by the publisher. You can set the\n * audioSource
to a device ID (string) or audio MediaStreamTrack object.\n * \n * This will result in an error (the Promise returned by the method is rejected) in the\n * following conditions:\n *
\n * - \n * The browser does not support this method. This method is not supported in\n * Internet Explorer or non-Chromium versions of Edge (older than version 79).\n *
\n * - \n * The publisher was not initiated with an audio source. (The
audioSource
\n * option of the OT.initPublisher() method was\n * set to null
or false
).\n * \n * - \n * The user denied access to the audio input device.\n *
\n * - \n * There was an error acquiring audio from the audio input device or MediaStreamTrack\n * object.\n *
\n * - \n * The
audioSource
value is not a string or MediaStreamTrack object.\n * \n * - \n * The
audioSource
string is not a valid audio input device available\n * to the browser.\n * \n *
\n * \n *\n * @param {Object} audioSource The device ID (string) of an audio input device, or an audio\n * MediaStreamTrack object.\n *\n * @method #setAudioSource\n * @memberOf Publisher\n *\n * @see Publisher.getAudioSource()\n *\n * @return {Promise} A promise that resolves when the operation completes successfully.\n * If there is an error, the promise is rejected.\n */\n\n\n let cancelPreviousSetAudioSourceSentinel;\n\n const setAudioSource = /*#__PURE__*/function () {\n var _ref31 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee20(audioSource) {\n var CANCEL_ERR_MSG, currentCancelSentinel, setStreamIfNotCancelled, prevAudioSource, pcs, newOptions, prevLabel, prevDeviceId, _processedOptions3, getUserMediaHelper, prevOptions, previousDevice, stream;\n\n return _regenerator.default.wrap(function _callee20$(_context20) {\n while (1) switch (_context20.prev = _context20.next) {\n case 0:\n CANCEL_ERR_MSG = 'Operation did not succeed due to a new request.';\n\n if (cancelPreviousSetAudioSourceSentinel) {\n cancelPreviousSetAudioSourceSentinel.cancel();\n }\n\n cancelPreviousSetAudioSourceSentinel = new Cancellation();\n currentCancelSentinel = cancelPreviousSetAudioSourceSentinel;\n\n setStreamIfNotCancelled = stream => {\n if (currentCancelSentinel.isCanceled()) {\n stream.getTracks(track => track.stop());\n throw otError(Errors.CANCEL, new Error(CANCEL_ERR_MSG));\n }\n\n return setAudioSource(stream.getAudioTracks()[0]);\n };\n\n if (!(OTHelpers.env.isLegacyEdge || !windowMock.RTCRtpSender || typeof windowMock.RTCRtpSender.prototype.replaceTrack !== 'function')) {\n _context20.next = 7;\n break;\n }\n\n throw otError(Errors.UNSUPPORTED_BROWSER, new Error('Publisher#setAudioSource is not supported in your browser.'));\n\n case 7:\n prevAudioSource = _this.getAudioSource();\n\n if (prevAudioSource) {\n _context20.next = 10;\n break;\n }\n\n throw otError(Errors.NOT_SUPPORTED, new Error('Publisher#setAudioSource cannot add an audio source when you started without one.'));\n\n case 10:\n if (!(audioSource instanceof MediaStreamTrack)) {\n _context20.next = 21;\n break;\n }\n\n _context20.next = 13;\n return getAllPeerConnections();\n\n case 13:\n pcs = _context20.sent;\n\n if (!currentCancelSentinel.isCanceled()) {\n _context20.next = 16;\n break;\n }\n\n throw otError(Errors.CANCEL, new Error(CANCEL_ERR_MSG));\n\n case 16:\n _context20.next = 18;\n return Promise.all(pcs.map(pc => pc.findAndReplaceTrack(prevAudioSource, audioSource)));\n\n case 18:\n return _context20.abrupt(\"return\", replaceAudioTrack(prevAudioSource, audioSource));\n\n case 21:\n if (!(typeof audioSource === 'string')) {\n _context20.next = 68;\n break;\n }\n\n // Must be a deviceId, call getUserMedia and get the MediaStreamTrack\n newOptions = cloneDeep(options);\n newOptions.audioSource = audioSource;\n newOptions.videoSource = null;\n processedOptions = processPubOptions(newOptions, 'OT.Publisher.setAudioSource', () => currentCancelSentinel.isCanceled() || state && state.isDestroyed());\n processedOptions.on({\n accessDialogOpened: onAccessDialogOpened,\n accessDialogClosed: onAccessDialogClosed\n });\n prevLabel = prevAudioSource.label;\n prevDeviceId = prevAudioSource.getConstraints && prevAudioSource.getSettings().deviceId || undefined; // In firefox we have to stop the previous track before we get a new one\n\n if (prevAudioSource) {\n prevAudioSource.stop();\n }\n\n _processedOptions3 = processedOptions, getUserMediaHelper = _processedOptions3.getUserMedia;\n _context20.prev = 31;\n _context20.t0 = setStreamIfNotCancelled;\n _context20.next = 35;\n return getUserMediaHelper();\n\n case 35:\n _context20.t1 = _context20.sent;\n _context20.next = 38;\n return (0, _context20.t0)(_context20.t1);\n\n case 38:\n return _context20.abrupt(\"return\", _context20.sent);\n\n case 41:\n _context20.prev = 41;\n _context20.t2 = _context20[\"catch\"](31);\n\n if (!currentCancelSentinel.isCanceled()) {\n _context20.next = 45;\n break;\n }\n\n throw otError(Errors.CANCEL, new Error(CANCEL_ERR_MSG));\n\n case 45:\n prevOptions = cloneDeep(options);\n prevOptions.videoSource = null;\n prevOptions.audioSource = prevDeviceId;\n\n if (!(!prevOptions.audioSource && prevLabel)) {\n _context20.next = 55;\n break;\n }\n\n _context20.next = 51;\n return getMediaDevices();\n\n case 51:\n previousDevice = _context20.sent.filter(x => x.label === prevLabel)[0];\n\n if (!currentCancelSentinel.isCanceled()) {\n _context20.next = 54;\n break;\n }\n\n throw otError(Errors.CANCEL, new Error(CANCEL_ERR_MSG));\n\n case 54:\n if (previousDevice) {\n prevOptions.audioSource = previousDevice.deviceId;\n }\n\n case 55:\n if (prevOptions.audioSource) {\n _context20.next = 58;\n break;\n }\n\n _context20.t2.message += ' (could not determine previous audio device)';\n throw otError(Errors.NOT_FOUND, _context20.t2);\n\n case 58:\n processedOptions = processPubOptions(prevOptions, 'OT.Publisher.setAudioSource', () => currentCancelSentinel.isCanceled() || state && state.isDestroyed());\n _context20.next = 61;\n return processedOptions.getUserMedia().catch(error => {\n // eslint-disable-next-line no-param-reassign\n error.message += ' (could not obtain previous audio device)';\n throw error;\n });\n\n case 61:\n stream = _context20.sent;\n _context20.next = 64;\n return setStreamIfNotCancelled(stream);\n\n case 64:\n _context20.t2.message += ' (reverted to previous audio device)';\n throw _context20.t2;\n\n case 66:\n _context20.next = 69;\n break;\n\n case 68:\n throw otError(Errors.INVALID_PARAMETER, new Error('Invalid parameter passed to OT.Publisher.setAudioSource(). Expected string or MediaStreamTrack.'));\n\n case 69:\n case \"end\":\n return _context20.stop();\n }\n }, _callee20, null, [[31, 41]]);\n }));\n\n return function setAudioSource(_x14) {\n return _ref31.apply(this, arguments);\n };\n }();\n\n this.setAudioSource = setAudioSource;\n /**\n * Returns the MediaStreamTrack object used as the audio input source for the publisher.\n * If the publisher does not have an audio source, this method returns null.\n *\n * @method #getAudioSource\n * @memberOf Publisher\n * @see Publisher.setAudioSource()\n *\n * @return {MediaStreamTrack} The audio source for the publisher (or null, if there is none).\n */\n\n this.getAudioSource = () => {\n if (webRTCStream && webRTCStream.getAudioTracks().length > 0) {\n return webRTCStream.getAudioTracks()[0];\n }\n\n return null;\n };\n /**\n * This method sets the video source for a publisher that is using a camera.\n * Pass in the device ID of the new video source.\n *\n * \n * The following will result in errors:\n *\n *
\n * - If the
videoSourceId
parameter is not a string\n * or the device ID for a valid video input device, the promise\n * will reject with an error with the name
property\n * set to 'OT_INVALID_VIDEO_SOURCE'
.\n * \n *\n * - If the publisher does not currently use a camera input, the promise\n * will reject with an error with the
name
property\n * set to 'OT_SET_VIDEO_SOURCE_FAILURE'
.\n * \n *
\n *\n * @param {String} videoSourceId The device ID of a video input (camera) device.\n * @method #setVideoSource\n * @memberOf Publisher\n *\n * @see OT.getDevices()\n * @see Publisher.getVideoSource()\n * @see Publisher.cycleVideo()\n *\n * @return {Promise} A promise that resolves with no value when the operation\n * completes successfully. If there is an error, the promise is rejected.\n */\n\n\n const setVideoSource = /*#__PURE__*/function () {\n var _setVideoSource = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee21(videoSourceId) {\n var invalidVideoSourceOtError, setVideoSourceOtError, isAudioOnly, deviceList, isValidVideoDeviceId;\n return _regenerator.default.wrap(function _callee21$(_context21) {\n while (1) switch (_context21.prev = _context21.next) {\n case 0:\n invalidVideoSourceOtError = otError(Errors.INVALID_VIDEO_SOURCE, new Error('Invalid video source. Video source must be a valid video input deviceId'), 1041);\n setVideoSourceOtError = otError(Errors.SET_VIDEO_SOURCE_FAILURE, new Error('You cannot reset the video source on a publisher that does not currently use a camera source.'), 1040);\n\n if (!(OTHelpers.env.isLegacyEdge || !windowMock.RTCRtpSender || typeof windowMock.RTCRtpSender.prototype.replaceTrack !== 'function')) {\n _context21.next = 4;\n break;\n }\n\n throw otError(Errors.UNSUPPORTED_BROWSER, new Error('setVideoSource is not supported in your browser.'), ExceptionCodes.UNABLE_TO_PUBLISH);\n\n case 4:\n if (!(typeof videoSourceId !== 'string')) {\n _context21.next = 6;\n break;\n }\n\n throw invalidVideoSourceOtError;\n\n case 6:\n // we can't use hasVideo because that only checks if the video is\n isAudioOnly = !webRTCStream || webRTCStream.getVideoTracks().length === 0;\n\n if (!(isCustomVideoTrack || isScreenSharing || isAudioOnly)) {\n _context21.next = 9;\n break;\n }\n\n throw setVideoSourceOtError;\n\n case 9:\n _context21.next = 11;\n return getMediaDevices();\n\n case 11:\n deviceList = _context21.sent;\n isValidVideoDeviceId = deviceList.find(device => device.kind === 'videoInput' && device.deviceId === videoSourceId);\n\n if (isValidVideoDeviceId) {\n _context21.next = 15;\n break;\n }\n\n throw invalidVideoSourceOtError;\n\n case 15:\n _context21.next = 17;\n return attemptToSetVideoTrack(videoSourceId);\n\n case 17:\n case \"end\":\n return _context21.stop();\n }\n }, _callee21);\n }));\n\n function setVideoSource(_x15) {\n return _setVideoSource.apply(this, arguments);\n }\n\n return setVideoSource;\n }();\n\n this.setVideoSource = setVideoSource;\n\n const attemptToSetVideoTrack = /*#__PURE__*/function () {\n var _ref32 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee22(newVideoDeviceId) {\n var oldDeviceID, oldTrack, newVideoTrack;\n return _regenerator.default.wrap(function _callee22$(_context22) {\n while (1) switch (_context22.prev = _context22.next) {\n case 0:\n oldDeviceID = currentDeviceId;\n currentDeviceId = newVideoDeviceId; // We shouldn't replace the track unless the video is on\n\n if (properties.publishVideo) {\n _context22.next = 4;\n break;\n }\n\n return _context22.abrupt(\"return\");\n\n case 4:\n oldTrack = getCurrentTrack();\n\n if (properties.publishVideo && OTHelpers.env.isAndroid && (OTHelpers.env.isChrome || OTHelpers.env.isFirefox)) {\n // On Chrome on Android you need to stop the previous video track OPENTOK-37206\n // In case we are not publishing video, we don't need to stop the oldTrack since\n // there isn't going to be a new track, once we publish video again, the oldTrack\n // will be properly stopped in _toggleVideo\n if (oldTrack && oldTrack.stop) {\n oldTrack.stop();\n }\n }\n\n _context22.prev = 6;\n _context22.next = 9;\n return getTrackFromDeviceId(newVideoDeviceId);\n\n case 9:\n newVideoTrack = _context22.sent;\n _context22.next = 17;\n break;\n\n case 12:\n _context22.prev = 12;\n _context22.t0 = _context22[\"catch\"](6);\n currentDeviceId = oldDeviceID;\n logging.error(_context22.t0);\n return _context22.abrupt(\"return\");\n\n case 17:\n _context22.next = 19;\n return replaceTrackAndUpdate(oldTrack, newVideoTrack);\n\n case 19:\n if (properties.publishVideo) {\n isTrackManuallyStopped = false;\n }\n\n case 20:\n case \"end\":\n return _context22.stop();\n }\n }, _callee22, null, [[6, 12]]);\n }));\n\n return function attemptToSetVideoTrack(_x16) {\n return _ref32.apply(this, arguments);\n };\n }();\n /**\n * Returns an object containing properties defining the publisher's current\n * video source.\n *\n * @method #getVideoSource\n * @memberOf Publisher\n * @see Publisher.setVideoSource()\n * @see OT.initPublisher()\n *\n * @return {VideoSource} The return object has the following properties:\n *\n * \n *
\n * deviceId
(String | null) — The device ID. \n * type
(String | null) — This is set to\n * 'camera' (for a camera-based video), 'screen' (for a screen-sharing video),\n * or 'custom' (for a video with a MediaStreamTrack source). \n * track
(MediaStreamTrack | null) — The\n * MediaStreamTrack for the video. \n *
\n * \n * Any inapplicable properties will be set to null.\n */\n\n\n this.getVideoSource = () => {\n const sourceProperties = {};\n const isAudioOnly = !webRTCStream || webRTCStream.getVideoTracks().length === 0;\n sourceProperties.track = webRTCStream && properties.publishVideo && webRTCStream.getVideoTracks()[0] || null;\n sourceProperties.deviceId = !isScreenSharing && !isCustomVideoTrack && currentDeviceId ? currentDeviceId : null;\n\n if (isCustomVideoTrack) {\n sourceProperties.type = 'custom';\n } else if (isScreenSharing) {\n sourceProperties.type = 'screen';\n } else if (!isAudioOnly) {\n sourceProperties.type = 'camera';\n } else {\n sourceProperties.type = null;\n }\n\n return sourceProperties;\n }; // API Compatibility layer for Flash Publisher, this could do with some tidyup.\n\n\n this._ = {\n publishToSession: (session, analyticsReplacement) => {\n if (analyticsReplacement) {\n analytics = analyticsReplacement;\n } // Add session property to Publisher\n\n\n previousSession = session;\n this.session = session;\n const requestedStreamId = uuid();\n lastRequestedStreamId = requestedStreamId;\n this.streamId = requestedStreamId;\n logConnectivityEvent('Attempt', {\n dataChannels: properties.channels,\n properties: whitelistPublisherProperties(properties)\n });\n const loadedPromise = new Promise((resolve, reject) => {\n if (loaded) {\n resolve();\n return;\n }\n\n this.once('initSuccess', resolve);\n this.once('destroyed', (_ref33) => {\n let reason = _ref33.reason;\n let reasonDescription = '';\n\n if (reason) {\n reasonDescription = ` Reason: ${reason}`;\n }\n\n reject(new Error(`Publisher destroyed before it finished loading.${reasonDescription}`));\n });\n });\n logging.debug('publishToSession: waiting for publishComplete, which is triggered by ' + 'stream#created from rumor');\n const completedPromise = new Promise((resolve, reject) => {\n this.once('publishComplete', error => {\n if (error) {\n reject(error);\n return;\n }\n\n this._setScalableFramerates(properties.scalableFramerates);\n\n this._setScalableVideoLayers(properties.scalableVideoLayers);\n\n logging.debug('publishToSession: got publishComplete');\n resolve();\n });\n });\n\n const processMessagingError = error => {\n const publicError = createStreamErrorMap(error);\n logConnectivityEvent('Failure', {}, {\n failureReason: 'Publish',\n failureCode: publicError.code,\n failureMessage: publicError.message\n });\n\n if (state.isAttemptingToPublish()) {\n this.trigger('publishComplete', publicError);\n }\n\n OTErrorClass.handleJsException({\n errorMsg: error.message,\n code: publicError.code,\n target: this,\n error,\n analytics\n });\n throw publicError;\n };\n\n logging.debug('publishToSession: waiting for loaded');\n const streamCreatedPromise = loadedPromise.then(() => session._.getVideoCodecsCompatible(webRTCStream)).then(videoCodecsCompatible => {\n logging.debug('publishToSession: loaded'); // Bail if this.session is gone, it means we were unpublished\n // before createStream could finish.\n\n if (!this.session) {\n return undefined;\n } // make sure we trigger an error if we are not getting any \"ack\" after a reasonable\n // amount of time\n\n\n const publishGuardingTo = setTimeout(() => {\n onPublishingTimeout(session);\n }, PUBLISH_MAX_DELAY);\n this.once('publishComplete', () => {\n clearTimeout(publishGuardingTo);\n });\n state.set('PublishingToSession');\n const video = videoCodecsCompatible && widgetView && widgetView.video();\n const hasVideoTrack = webRTCStream.getVideoTracks().length > 0;\n const didRequestVideo = properties.videoSource !== null && properties.videoSource !== false;\n\n if (video && hasVideoTrack && didRequestVideo) {\n streamChannels.push(new StreamChannel({\n id: 'video1',\n type: 'video',\n active: properties.publishVideo,\n orientation: VideoOrientation.ROTATED_NORMAL,\n frameRate: properties.frameRate,\n width: video.videoWidth(),\n height: video.videoHeight(),\n source: (() => {\n if (isScreenSharing) {\n return 'screen';\n }\n\n if (isCustomVideoTrack) {\n return 'custom';\n }\n\n return 'camera';\n })(),\n fitMode: properties.fitMode\n }));\n }\n\n const hasAudioTrack = webRTCStream.getAudioTracks().length > 0;\n const didRequestAudio = properties.audioSource !== null && properties.audioSource !== false; // @todo should we just use hasAudioTrack here? if hasAudioTrack is true\n // then does it matter if didRequestAudio is false? we still have an audio\n // track for some reason!\n\n if (didRequestAudio && hasAudioTrack) {\n streamChannels.push(new StreamChannel({\n id: 'audio1',\n type: 'audio',\n active: properties.publishAudio\n }));\n }\n\n logging.debug('publishToSession: creating rumor stream id');\n return new Promise((resolve, reject) => {\n _createStream(null, (messagingError, streamId, message) => {\n if (messagingError) {\n reject(processMessagingError(messagingError));\n return;\n }\n\n resolve({\n streamId,\n message\n });\n });\n });\n }).then(maybeStream => {\n if (maybeStream === undefined) {\n return;\n }\n\n const streamId = maybeStream.streamId,\n message = maybeStream.message;\n logging.debug('publishToSession: rumor stream id created:', streamId, '(this is different from stream#created, which requires media to actually be ' + 'flowing for mantis sessions)');\n\n if (streamId !== requestedStreamId) {\n throw new Error('streamId response does not match request');\n }\n\n this.streamId = streamId;\n rumorIceServers = parseIceServers(message);\n }).catch(err => {\n this.trigger('publishComplete', err);\n throw err;\n });\n return Promise.all([streamCreatedPromise, completedPromise]);\n },\n unpublishFromSession: (session, reason) => {\n if (!this.session || session.id !== this.session.id) {\n if (reason === 'unpublished') {\n const selfSessionText = this.session && this.session.id || 'no session';\n logging.warn(`The publisher ${guid} is trying to unpublish from a session ${session.id} it is not ` + `attached to (it is attached to ${selfSessionText})`);\n }\n\n return this;\n }\n\n if (session.isConnected() && (this.stream || state.isAttemptingToPublish())) {\n session._.streamDestroy(this.streamId);\n }\n\n streamCleanupJobs.releaseAll(); // Disconnect immediately, rather than wait for the WebSocket to\n // reply to our destroyStream message.\n\n this.disconnect();\n\n if (state.isAttemptingToPublish()) {\n logConnectivityEvent('Cancel', {\n reason: 'unpublish'\n });\n\n const createErrorFromReason = () => {\n switch (reason) {\n case 'mediaStopped':\n return 'The video element fired the ended event, indicating there is an issue with the media';\n\n case 'unpublished':\n return 'The publisher was unpublished before it could be published';\n\n case 'reset':\n return 'The publisher was reset';\n\n default:\n return `The publisher was destroyed due to ${reason}`;\n }\n };\n\n const err = new Error(createErrorFromReason());\n this.trigger('publishComplete', otError(reason === 'mediaStopped' ? Errors.MEDIA_ENDED : Errors.CANCEL, err));\n }\n\n this.session = null;\n logAnalyticsEvent('unpublish', 'Success');\n\n this._.streamDestroyed(reason);\n\n return this;\n },\n unpublishStreamFromSession: (stream, session, reason) => {\n if (!lastRequestedStreamId || stream.id !== lastRequestedStreamId) {\n logging.warn(`The publisher ${guid} is trying to destroy a stream ${stream.id} that is not attached to it (it has ${lastRequestedStreamId || 'no stream'} attached to it)`);\n return this;\n }\n\n return this._.unpublishFromSession(session, reason);\n },\n streamDestroyed: reason => {\n if (['reset'].indexOf(reason) < 0) {\n // We're back to being a stand-alone publisher again.\n if (!state.isDestroyed()) {\n state.set('MediaBound');\n }\n }\n\n const event = new Events.StreamEvent('streamDestroyed', this.stream, reason, true);\n this.dispatchEvent(event);\n\n if (!event.isDefaultPrevented()) {\n this.destroy();\n }\n },\n\n archivingStatus(status) {\n if (chromeMixin) {\n chromeMixin.setArchivingStatus(status);\n }\n\n return status;\n },\n\n webRtcStream() {\n return webRTCStream;\n },\n\n switchTracks() {\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee23() {\n var stream;\n return _regenerator.default.wrap(function _callee23$(_context23) {\n while (1) switch (_context23.prev = _context23.next) {\n case 0:\n _context23.prev = 0;\n _context23.next = 3;\n return getUserMedia().catch(userMediaError);\n\n case 3:\n stream = _context23.sent;\n _context23.next = 10;\n break;\n\n case 6:\n _context23.prev = 6;\n _context23.t0 = _context23[\"catch\"](0);\n logging.error(`OT.Publisher.switchTracks failed to getUserMedia: ${_context23.t0}`);\n throw _context23.t0;\n\n case 10:\n setNewStream(stream);\n _context23.prev = 11;\n bindVideo();\n _context23.next = 21;\n break;\n\n case 15:\n _context23.prev = 15;\n _context23.t1 = _context23[\"catch\"](11);\n\n if (!(_context23.t1 instanceof CancellationError)) {\n _context23.next = 19;\n break;\n }\n\n return _context23.abrupt(\"return\");\n\n case 19:\n logging.error('Error while binding video', _context23.t1);\n throw _context23.t1;\n\n case 21:\n _context23.prev = 21;\n replaceTracks();\n _context23.next = 29;\n break;\n\n case 25:\n _context23.prev = 25;\n _context23.t2 = _context23[\"catch\"](21);\n logging.error('Error replacing tracks', _context23.t2);\n throw _context23.t2;\n\n case 29:\n case \"end\":\n return _context23.stop();\n }\n }, _callee23, null, [[0, 6], [11, 15], [21, 25]]);\n }))();\n },\n\n getDataChannel(label, getOptions, completion) {\n const pc = getPeerConnectionById(Object.keys(peerConnectionsAsync)[0]); // @fixme this will fail if it's called before we have a PublisherPeerConnection.\n // I.e. before we have a subscriber.\n\n if (!pc) {\n completion(new OTHelpers.Error('Cannot create a DataChannel before there is a subscriber.'));\n return;\n }\n\n pc.then(peerConnection => {\n peerConnection.getDataChannel(label, getOptions, completion);\n });\n },\n\n iceRestart() {\n getAllPeerConnections().then(peerConnections => {\n peerConnections.forEach(peerConnection => {\n const _getPeerConnectionMet7 = getPeerConnectionMeta(peerConnection),\n remoteConnectionId = _getPeerConnectionMet7.remoteConnectionId;\n\n logRepublish('Attempt', {\n remoteConnectionId\n });\n logging.debug('Publisher: ice restart attempt');\n peerConnection.iceRestart();\n });\n });\n },\n\n getState() {\n return state;\n },\n\n demoOnlyCycleVideo: this.cycleVideo,\n\n testOnlyGetFramesEncoded() {\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee24() {\n var peerConnections;\n return _regenerator.default.wrap(function _callee24$(_context24) {\n while (1) switch (_context24.prev = _context24.next) {\n case 0:\n _context24.next = 2;\n return getAllPeerConnections();\n\n case 2:\n peerConnections = _context24.sent;\n\n if (peerConnections.length) {\n _context24.next = 5;\n break;\n }\n\n throw new Error('No established PeerConnections yet');\n\n case 5:\n return _context24.abrupt(\"return\", peerConnections[0]._testOnlyGetFramesEncoded());\n\n case 6:\n case \"end\":\n return _context24.stop();\n }\n }, _callee24);\n }))();\n },\n\n onStreamAvailable,\n startRoutedToRelayedTransition: () => {\n logRoutedToRelayedTransition('Attempt');\n\n const processMessagingError = error => {\n const publicError = createStreamErrorMap(error);\n this.trigger('streamCreateForP2PComplete', publicError);\n logRoutedToRelayedTransition('Failure', {\n reason: publicError.message\n });\n };\n\n if (!this.session) {\n logRoutedToRelayedTransition('Failure', {\n reason: 'Not connected to the session.'\n });\n return;\n }\n\n const streamCreateForP2PCompleteTimeout = setTimeout(() => {\n logRoutedToRelayedTransition('Failure', {\n reason: 'Timeout'\n });\n }, PUBLISH_MAX_DELAY);\n this.once('streamCreateForP2PComplete', () => {\n clearTimeout(streamCreateForP2PCompleteTimeout);\n });\n logging.debug('streamCreateWithSource: send a message to RUMOR for ' + `creating the stream with the sourceStreaId P2P and stream ${this.streamId}`);\n\n _createStream('P2P', messagingError => {\n if (messagingError) {\n processMessagingError(messagingError);\n } else {\n this.trigger('streamCreateForP2PComplete');\n }\n });\n },\n startRelayedToRoutedTransition: function () {\n var _startRelayedToRoutedTransition = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee25() {\n return _regenerator.default.wrap(function _callee25$(_context25) {\n while (1) switch (_context25.prev = _context25.next) {\n case 0:\n if (!(activeSourceStreamId === 'MANTIS')) {\n _context25.next = 2;\n break;\n }\n\n return _context25.abrupt(\"return\");\n\n case 2:\n logRelayedToRoutedTransition('Attempt');\n\n if (_this.session) {\n _context25.next = 6;\n break;\n }\n\n logRelayedToRoutedTransition('Failure', {\n reason: 'Not connected to the session.'\n });\n return _context25.abrupt(\"return\");\n\n case 6:\n if (_this.streamId) {\n _context25.next = 9;\n break;\n }\n\n logRelayedToRoutedTransition('Failure', {\n reason: 'No streamId available'\n });\n return _context25.abrupt(\"return\");\n\n case 9:\n _this.session._.streamDestroy(_this.streamId, 'P2P');\n\n _context25.next = 12;\n return _restartSendingRtpToMantis();\n\n case 12:\n logRelayedToRoutedTransition('Success');\n\n _this.trigger('streamDestroyForP2PComplete');\n\n case 14:\n case \"end\":\n return _context25.stop();\n }\n }, _callee25);\n }));\n\n function startRelayedToRoutedTransition() {\n return _startRelayedToRoutedTransition.apply(this, arguments);\n }\n\n return startRelayedToRoutedTransition;\n }(),\n forceMuteAudio: function () {\n logAnalyticsEvent('publishAudio', 'Attempt', {\n publishAudio: false\n });\n properties.publishAudio = false;\n\n try {\n updateAudio('auto');\n this.dispatchEvent(new Events.MuteForcedEvent());\n logAnalyticsEvent('publishAudio', 'Success', {\n publishAudio: false\n });\n } catch (e) {\n logAnalyticsEvent('publishAudio', 'Failure', {\n message: e.message\n });\n }\n }.bind(this)\n };\n\n this.detectDevices = function () {\n logging.warn('Publisher.detectDevices() is not implemented.');\n };\n\n this.detectMicActivity = function () {\n logging.warn('Publisher.detectMicActivity() is not implemented.');\n };\n\n this.getEchoCancellationMode = function () {\n logging.warn('Publisher.getEchoCancellationMode() is not implemented.');\n return 'fullDuplex';\n };\n\n this.setMicrophoneGain = function () {\n logging.warn('Publisher.setMicrophoneGain() is not implemented.');\n };\n\n this.getMicrophoneGain = function () {\n logging.warn('Publisher.getMicrophoneGain() is not implemented.');\n return 0.5;\n };\n\n this.setCamera = function () {\n logging.warn('Publisher.setCamera() is not implemented.');\n };\n\n this.setMicrophone = function () {\n logging.warn('Publisher.setMicrophone() is not implemented.');\n }; // Platform methods:\n\n\n this.guid = function () {\n return guid;\n };\n\n this.videoElement = function () {\n const video = widgetView && widgetView.video();\n return video ? video.domElement() : null;\n };\n\n this.setStream = assignStream;\n this.isWebRTC = true;\n\n this.isLoading = function () {\n return widgetView && widgetView.loading();\n };\n /**\n * Returns the width, in pixels, of the Publisher video. This may differ from the\n * resolution
property passed in as the properties
property\n * the options passed into the OT.initPublisher()
method, if the browser\n * does not support the requested resolution.\n *\n * @method #videoWidth\n * @memberOf Publisher\n * @return {Number} the width, in pixels, of the Publisher video.\n */\n\n\n this.videoWidth = function () {\n const video = widgetView && widgetView.video();\n return video ? video.videoWidth() : undefined;\n };\n /**\n * Returns the height, in pixels, of the Publisher video. This may differ from the\n * resolution
property passed in as the properties
property\n * the options passed into the OT.initPublisher()
method, if the browser\n * does not support the requested resolution.\n *\n * @method #videoHeight\n * @memberOf Publisher\n * @return {Number} the height, in pixels, of the Publisher video.\n */\n\n\n this.videoHeight = function () {\n const video = widgetView && widgetView.video();\n return video ? video.videoHeight() : undefined;\n };\n /**\n * Returns the details on the publisher's stream quality, including the following:\n *\n *
\n *\n * - The total number of audio and video packets lost
\n * - The total number of audio and video packets sent
\n * - The total number of audio and video bytes sent
\n * - The current video frame rate
\n *\n *
\n *\n * You can use these stats to assess the quality of the publisher's audio-video stream.\n *\n * @param {Function} completionHandler A function that takes the following\n * parameters:\n *\n * \n *\n * error
(Error) — Upon successful completion\n * the method, this is undefined. An error results if the publisher is not connected to a\n * session or if it is not publishing audio or video. \n *\n * statsArray
(Array) — An array of objects defining the current\n * audio-video statistics for the publisher. For a publisher in a routed session (one that\n * uses the OpenTok\n * Media Router), this array includes one object, defining the statistics for the single\n * audio-media stream that is sent to the OpenTok Media Router. In a relayed session, the\n * array includes an object for each subscriber to the published stream. Each object in the\n * array contains a stats
property that includes the following properties:\n *\n * \n *
\n * audio.bytesSent
(Number) — The total number of audio bytes\n * sent to the subscriber (or to the OpenTok Media Router) \n *\n * audio.packetsLost
(Number) — The total number audio packets\n * that did not reach the subscriber (or to the OpenTok Media Router) \n *\n * audio.packetsSent
(Number) — The total number of audio\n * packets sent to the subscriber (or to the OpenTok Media Router) \n *\n * timestamp
(Number) — The timestamp, in milliseconds since\n * the Unix epoch, for when these stats were gathered \n *\n * video.bytesSent
(Number) — The total video bytes sent to\n * the subscriber (or to the OpenTok Media Router) \n *\n * video.packetsLost
(Number) — The total number of video packets\n * that did not reach the subscriber (or to the OpenTok Media Router) \n *\n * video.packetsSent
(Number) — The total number of video\n * packets sent to the subscriber \n *\n * video.frameRate
(Number) — The current video frame rate \n *
\n *\n * Additionally, for a publisher in a relayed session, each object in the array contains\n * the following two properties:\n *\n *
\n * connectionId
(String) — The unique ID of the client's\n * connection, which matches the id
property of the connection
\n * property of the connectionCreated\n * event that the Session object dispatched for the remote client. \n *\n * subscriberId
(String) — The unique ID of the subscriber, which\n * matches the id
property of the Subscriber object in the subscribing\n * client's app. \n *
\n *\n * These two properties are undefined for a publisher in a routed session.\n *\n *
\n *
\n *\n * @see Subscriber.getStats()\n * @see Publisher.getRtcStatsReport()\n *\n * @method #getStats\n * @memberOf Publisher\n */\n\n\n this.getStats = function getStats(callback) {\n _getStats((err, stats) => {\n if (err) {\n callback(err);\n } else {\n callback(null, stats);\n }\n });\n };\n /**\n * Returns a promise that, on success, resolves with an array of objects that include\n * RTCStatsReport properties for the published stream. (See\n * \n * RTCStatsReport.)\n *\n * \n * For a publisher in a routed session (one that uses the\n * OpenTok\n * Media Router), this array includes one object, defining the statistics for the single\n * audio-media stream that is sent to the OpenTok Media Router. In a relayed session, the\n * array includes an object for each subscriber to the published stream. Each object in the\n * array contains an rtcStatsReport
property that is a RTCStatsReport object.\n *\n *
\n * Additionally, for a publisher in a relayed session, each object in the array contains\n * the following two properties:\n *\n *
\n *\n * connectionId
(String) — The unique ID of the client's\n * connection, which matches the id
property of the connection
\n * property of the connectionCreated\n * event that the Session object dispatched for the remote client. \n *\n * subscriberId
(String) — The unique ID of the subscriber, which\n * matches the id
property of the Subscriber object in the subscribing\n * client's app. \n *\n *
\n *\n * \n * These two properties are undefined for a publisher in a routed session.\n *\n *
\n * The Promise will be rejected in the following conditions:\n *
\n * \n *\n * @method #getRtcStatsReport\n * @memberOf Publisher\n *\n * @see Subscriber.getRtcStatsReport()\n *\n * @return {Promise} A promise that resolves when the operation completes successfully.\n * If there is an error, the promise is rejected.\n */\n\n\n this.getRtcStatsReport = () => new Promise((resolve, reject) => {\n _getRtcStatsReport((err, stats) => {\n if (err) {\n reject(err);\n } else {\n resolve(stats);\n }\n });\n }); // Make read-only: element, guid, _.webRtcStream\n\n\n state = new PublishingState(stateChangeFailed);\n this.accessAllowed = false;\n };\n /**\n * Dispatched when the user has clicked the Allow button, granting the\n * app access to the camera and microphone. The Publisher object has an\n * accessAllowed
property which indicates whether the user\n * has granted access to the camera and microphone.\n * @see Event\n * @name accessAllowed\n * @event\n * @memberof Publisher\n */\n\n /**\n * Dispatched when the user has clicked the Deny button, preventing the\n * app from having access to the camera and microphone.\n * \n * Note: On macOS 10.15+ (Catalina), to publish a screen-sharing stream\n * the user must grant the browser access to the screen in macOS System Preferences >\n * Security & Privacy > Privacy > Screen Recording. Otherwise,\n * the Publisher will dispatch an accessDenied
event.\n *\n * @see Event\n * @name accessDenied\n * @event\n * @memberof Publisher\n */\n\n /**\n * Dispatched when the Allow/Deny dialog box is opened. (This is the dialog box in which\n * the user can grant the app access to the camera and microphone.)\n * @see Event\n * @name accessDialogOpened\n * @event\n * @memberof Publisher\n */\n\n /**\n * Dispatched when the Allow/Deny box is closed. (This is the dialog box in which the\n * user can grant the app access to the camera and microphone.)\n * @see Event\n * @name accessDialogClosed\n * @event\n * @memberof Publisher\n */\n\n /**\n * Dispatched periodically to indicate the publisher's audio level. The event is dispatched\n * up to 60 times per second, depending on the browser. The audioLevel
property\n * of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more\n * information.\n *
\n * The following example adjusts the value of a meter element that shows volume of the\n * publisher. Note that the audio level is adjusted logarithmically and a moving average\n * is applied:\n *
\n *
\n * var movingAvg = null;\n * publisher.on('audioLevelUpdated', function(event) {\n * if (movingAvg === null || movingAvg <= event.audioLevel) {\n * movingAvg = event.audioLevel;\n * } else {\n * movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;\n * }\n *\n * // 1.5 scaling to map the -30 - 0 dBm range to [0,1]\n * var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;\n * logLevel = Math.min(Math.max(logLevel, 0), 1);\n * document.getElementById('publisherMeter').value = logLevel;\n * });\n *
\n * This example shows the algorithm used by the default audio level indicator displayed\n * in an audio-only Publisher.\n *\n * @name audioLevelUpdated\n * @event\n * @memberof Publisher\n * @see AudioLevelUpdatedEvent\n */\n\n /**\n * The publisher has started streaming to the session.\n * @name streamCreated\n * @event\n * @memberof Publisher\n * @see StreamEvent\n * @see Session.publish()\n */\n\n /**\n * The publisher has stopped streaming to the session. The default behavior is that\n * the Publisher object is removed from the HTML DOM. The Publisher object dispatches a\n * destroyed
event when the element is removed from the HTML DOM. If you call the\n * preventDefault()
method of the event object in the event listener, the default\n * behavior is prevented, and you can, optionally, retain the Publisher for reuse or clean it up\n * using your own code.\n * @name streamDestroyed\n * @event\n * @memberof Publisher\n * @see StreamEvent\n */\n\n /**\n * Dispatched when the Publisher element is removed from the HTML DOM. When this event\n * is dispatched, you may choose to adjust or remove HTML DOM elements related to the publisher.\n * @name destroyed\n * @event\n * @memberof Publisher\n */\n\n /**\n * Dispatched when the video dimensions of the video change. This can only occur in when the\n * stream.videoType
property is set to \"screen\"
(for a screen-sharing\n * video stream), when the user resizes the window being captured. This event object has a\n * newValue
property and an oldValue
property, representing the new and\n * old dimensions of the video. Each of these has a height
property and a\n * width
property, representing the height and width, in pixels.\n * @name videoDimensionsChanged\n * @event\n * @memberof Publisher\n * @see VideoDimensionsChangedEvent\n */\n\n /**\n * Dispatched when the Publisher's video element is created. Add a listener for this event when\n * you set the insertDefaultUI
option to false
in the call to the\n * OT.initPublisher() method. The element
\n * property of the event object is a reference to the Publisher's video
element\n * (or in Internet Explorer the object
element containing the video). Add it to\n * the HTML DOM to display the video. When you set the insertDefaultUI
option to\n * false
, the video
(or object
) element is not\n * automatically inserted into the DOM.\n *
\n * Add a listener for this event only if you have set the insertDefaultUI
option to\n * false
. If you have not set insertDefaultUI
option to\n * false
, do not move the video
(or object
) element in\n * in the HTML DOM. Doing so causes the Publisher object to be destroyed.\n *\n * @name videoElementCreated\n * @event\n * @memberof Publisher\n * @see VideoElementCreatedEvent\n */\n\n /**\n * A moderator has forced this client to mute audio.\n *\n * This is a beta feature.\n *\n * @name muteForced\n * @event\n * @memberof Publisher\n * @see Event\n */\n\n /**\n * The user publishing the stream has stopped sharing one or all media\n * types (video, audio and/or screen). This can occur when a user disconnects a camera or\n * microphone used as a media source for the Publisher. Or it can occur when a user closes\n * a when the video and audio sources of the stream are MediaStreamTrack elements and\n * tracks are stopped or destroyed.\n *\n * @name mediaStopped\n * @event\n * @memberof Publisher\n * @see MediaStoppedEvent\n */\n\n\n return Publisher;\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 167 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst hasOpenTokSupport = __webpack_require__(59).once;\n/**\n * A class defining properties of the capabilities
property of a\n * Session object. See Session.capabilities.\n *
\n * All Capabilities properties are undefined until you have connected to a session\n * and the Session object has dispatched the sessionConnected
event.\n *
\n * For more information on token roles, see the\n * Token Creation Overview.\n *\n * @class Capabilities\n *\n * @property {Number} forceDisconnect Specifies whether you can call\n * the Session.forceDisconnect()
method (1) or not (0). To call the\n * Session.forceDisconnect()
method,\n * the user must have a token that is assigned the role of moderator.\n * @property {Number} forceUnpublish Specifies whether you can call\n * the Session.forceUnpublish()
method (1) or not (0). To call the\n * Session.forceUnpublish()
method, the user must have a token that\n * is assigned the role of moderator.\n * @property {Number} forceMute Specifies whether you can call\n * the Session.forceMuteStream()
and Session.forceMuteAll()
\n * methods (1) or not (0). To call the Session.forceMuteStream()
and\n * Session.forceMuteAll()
methods, the user must have a token that is\n * assigned the role of moderator. This is a beta feature.\n * @property {Number} publish Specifies whether you can publish to the session (1) or not (0).\n * The ability to publish is based on a few factors. To publish, the user must have a token that\n * is assigned a role that supports publishing. There must be a connected camera and microphone.\n * @property {Number} subscribe Specifies whether you can subscribe to streams\n * in the session (1) or not (0). Currently, this capability is available for all users on all\n * platforms.\n */\n\n\nmodule.exports = function Capabilities(permissions) {\n this.publish = permissions.indexOf('publish') !== -1 ? 1 : 0;\n this.subscribe = permissions.indexOf('subscribe') !== -1 ? 1 : 0;\n this.forceUnpublish = permissions.indexOf('forceunpublish') !== -1 ? 1 : 0;\n this.forceDisconnect = permissions.indexOf('forcedisconnect') !== -1 ? 1 : 0;\n this.forceMute = permissions.indexOf('forcemute') !== -1 ? 1 : 0;\n this.supportsWebRTC = hasOpenTokSupport() ? 1 : 0;\n\n this.permittedTo = action => Object.prototype.hasOwnProperty.call(this, action) && this[action] === 1;\n};\n\n/***/ }),\n/* 168 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-shadow, no-underscore-dangle, max-len, no-use-before-define */\n\n/* eslint-disable no-void, vars-on-top, no-var, no-restricted-syntax, no-prototype-builtins */\n\n/* eslint-disable no-continue */\nconst logging = __webpack_require__(0)('Stream');\n\nconst OTHelpers = __webpack_require__(4);\n\nconst sessionObjects = __webpack_require__(23);\n\nconst eventing = __webpack_require__(5);\n\nconst Events = __webpack_require__(21)();\n\nconst validPropertyNames = ['name', 'archiving'];\n/**\n * Specifies a stream. A stream is a representation of a published stream in a session. When a\n * client calls the Session.publish() method, a new stream is\n * created. Properties of the Stream object provide information about the stream.\n *\n *
When a stream is added to a session, the Session object dispatches a\n * streamCreatedEvent
. When a stream is destroyed, the Session object dispatches a\n * streamDestroyed
event. The StreamEvent object, which defines these event objects,\n * has a stream
property, which is an array of Stream object. For details and a code\n * example, see {@link StreamEvent}.
\n *\n * When a connection to a session is made, the Session object dispatches a\n * sessionConnected
event, defined by the SessionConnectEvent object. The\n * SessionConnectEvent object has a streams
property, which is an array of Stream\n * objects pertaining to the streams in the session at that time. For details and a code example,\n * see {@link SessionConnectEvent}.
\n *\n * @class Stream\n * @property {Connection} connection The Connection object corresponding\n * to the connection that is publishing the stream. You can compare this to the\n * connection
property of the Session object to see if the stream is being published\n * by the local web page.\n *\n * @property {Number} creationTime The timestamp for the creation\n * of the stream. This value is calculated in milliseconds. You can convert this value to a\n * Date object by calling new Date(creationTime)
, where creationTime
is\n * the creationTime
property of the Stream object.\n *\n * @property {Number} frameRate The frame rate of the video stream. This property is only set if\n * the publisher of the stream specifies a frame rate when calling the\n * OT.initPublisher()
method; otherwise, this property is undefined.\n *\n * @property {Boolean} hasAudio Whether the stream has audio. This property can change if the\n * publisher turns on or off audio (by calling\n * Publisher.publishAudio()). When this occurs, the\n * {@link Session} object dispatches a streamPropertyChanged
event (see\n * {@link StreamPropertyChangedEvent}).\n *\n * @property {Boolean} hasVideo Whether the stream has video. This property can change if the\n * publisher turns on or off video (by calling\n * Publisher.publishVideo()). When this occurs, the\n * {@link Session} object dispatches a streamPropertyChanged
event (see\n * {@link StreamPropertyChangedEvent}).\n *\n * @property {String} name The name of the stream. Publishers can specify a name when publishing\n * a stream (using the publish()
method of the publisher's Session object).\n *\n * @property {String} streamId The unique ID of the stream.\n *\n * @property {Object} videoDimensions This object has two properties: width
and\n * height
. Both are numbers. The width
property is the width of the\n * encoded stream; the height
property is the height of the encoded stream. (These\n * are independent of the actual width of Publisher and Subscriber objects corresponding to the\n * stream.) This property can change if a stream published from a mobile device resizes, based on\n * a change in the device orientation. When the video dimensions change,\n * the {@link Session} object dispatches a streamPropertyChanged
event\n * (see {@link StreamPropertyChangedEvent}).\n *\n * @property {String} videoType The type of video — either \"camera\"
,\n * \"screen\"
, or \"custom\"
.\n * A \"screen\"
video uses screen sharing on the publisher\n * as the video source; for other videos, this property is set to \"camera\"
.\n * A \"custom\"
video uses a VideoTrack element as the video source on the publisher.\n * (See the videoSource
property of the options
parameter passed\n * into the OT.initPublisher() method.)\n * This property can change if a stream published from a mobile device changes from a\n * camera to a screen-sharing video type. When the video type changes, the {@link Session} object\n * dispatches a streamPropertyChanged
event (see {@link StreamPropertyChangedEvent}).\n */\n\nmodule.exports = function Stream(id, name, creationTime, connection, session, channel) {\n const self = this;\n let destroyedReason;\n this.id = id;\n this.streamId = id;\n this.name = name;\n this.creationTime = Number(creationTime);\n this.connection = connection;\n this.channel = channel;\n this.publisher = sessionObjects.publishers.find({\n streamId: this.id\n });\n eventing(this);\n\n const onChannelUpdate = function onChannelUpdate(channel, key, oldValue, newValue) {\n let _key = key;\n\n switch (_key) {\n case 'active':\n _key = channel.type === 'audio' ? 'hasAudio' : 'hasVideo';\n self[_key] = newValue;\n break;\n\n case 'disableWarning':\n _key = channel.type === 'audio' ? 'audioDisableWarning' : 'videoDisableWarning';\n self[_key] = newValue;\n\n if (!self[channel.type === 'audio' ? 'hasAudio' : 'hasVideo']) {\n return; // Do NOT event in this case.\n }\n\n break;\n\n case 'fitMode':\n _key = 'defaultFitMode';\n self[_key] = newValue;\n break;\n\n case 'source':\n _key = channel.type === 'audio' ? 'audioType' : 'videoType';\n self[_key] = newValue;\n break;\n\n case 'videoDimensions':\n self.videoDimensions = newValue;\n break;\n\n case 'orientation':\n case 'width':\n case 'height':\n // We dispatch this via the videoDimensions key instead so do not\n // trigger an event for them.\n return;\n\n default:\n }\n\n if (self.videoType === 'screen' && _key === 'hasVideo' && newValue === false) {\n // if this stream is from a screen ignore hasVideo=false events from rumor\n return;\n }\n\n self.dispatchEvent(new Events.StreamUpdatedEvent(self, _key, oldValue, newValue));\n };\n\n const associatedWidget = function associatedWidget() {\n if (self.publisher) {\n return self.publisher;\n }\n\n return sessionObjects.subscribers.find(subscriber => subscriber.stream && subscriber.stream.id === self.id && subscriber.session.id === session.id);\n }; // Returns true if this stream is subscribe to.\n\n\n const isBeingSubscribedTo = function isBeingSubscribedTo() {\n // @fixme This is not strictly speaking the right test as a stream\n // can be published and subscribed by the same connection. But the\n // update features don't handle this case properly right now anyway.\n //\n // The issue is that the stream needs to know whether the stream is\n // 'owned' by a publisher or a subscriber. The reason for that is that\n // when a Publisher updates a stream channel then we need to send the\n // `streamChannelUpdate` message, whereas if a Subscriber does then we\n // need to send `subscriberChannelUpdate`. The current code will always\n // send `streamChannelUpdate`.\n return !self.publisher;\n }; // Returns all channels that have a type of +type+.\n\n\n this.getChannelsOfType = function getChannelsOfType(type) {\n return self.channel.filter(channel => channel.type === type);\n };\n\n this.getChannel = function getChannel(id) {\n for (let i = 0; i < self.channel.length; ++i) {\n if (self.channel[i].id === id) {\n return self.channel[i];\n }\n }\n\n return null;\n }; // implement the following using the channels\n // * hasAudio\n // * hasVideo\n // * videoDimensions\n\n\n const audioChannel = this.getChannelsOfType('audio')[0];\n const videoChannel = this.getChannelsOfType('video')[0]; // @todo this should really be: \"has at least one video/audio track\" instead of\n // \"the first video/audio track\"\n\n this.hasAudio = audioChannel != null && audioChannel.active;\n this.hasVideo = videoChannel != null && videoChannel.active;\n this.videoType = videoChannel && videoChannel.source;\n this.defaultFitMode = videoChannel && videoChannel.fitMode;\n this.videoDimensions = {};\n\n if (videoChannel) {\n this.videoDimensions.width = videoChannel.width;\n this.videoDimensions.height = videoChannel.height;\n this.videoDimensions.orientation = videoChannel.orientation;\n videoChannel.on('update', onChannelUpdate);\n this.frameRate = videoChannel.frameRate;\n }\n\n if (audioChannel) {\n audioChannel.on('update', onChannelUpdate);\n }\n\n this.setChannelActiveState = function setChannelActiveState(channelType, activeState, activeReason) {\n const attributes = {\n active: activeState\n };\n\n if (activeReason) {\n attributes.activeReason = activeReason;\n }\n\n updateChannelsOfType(channelType, attributes);\n };\n\n this.setVideoDimensions = function setVideoDimensions(width, height) {\n updateChannelsOfType('video', {\n width,\n height,\n orientation: 0\n });\n };\n\n this.setRestrictFrameRate = function setRestrictFrameRate(restrict) {\n updateChannelsOfType('video', {\n restrictFrameRate: restrict\n });\n };\n\n this.setPreferredResolution = function setPreferredResolution(resolution) {\n if (!isBeingSubscribedTo()) {\n logging.warn('setPreferredResolution has no affect when called by a publisher');\n return;\n }\n\n if (session.sessionInfo.p2pEnabled) {\n logging.warn('Stream.setPreferredResolution will not work in a P2P Session');\n return;\n }\n\n if (resolution && resolution.width === void 0 && resolution.height === void 0) {\n return;\n } // This duplicates some of the code in updateChannelsOfType. We do this for a\n // couple of reasons:\n // 1. Because most of the work that updateChannelsOfType does is in calling\n // getChannelsOfType, which we need to do here anyway so that we can update\n // the value of maxResolution in the Video Channel.\n // 2. updateChannelsOfType on only sends a message to update the channel in\n // Rumor. The client then expects to receive a subsequent channel update\n // indicating that the update was successful. We don't receive those updates\n // for preferredFrameRate/maxResolution so we need to complete both tasks and it's\n // neater to do the related tasks right next to each other.\n // 3. This code shouldn't be in Stream anyway. There is way too much coupling\n // between Stream, Session, Publisher, and Subscriber. This will eventually be\n // fixed, and when it is then it will be easier to exact the code if it's a\n // single piece.\n //\n\n\n const video = self.getChannelsOfType('video')[0];\n\n if (!video) {\n return;\n }\n\n if (resolution && resolution.width) {\n if (isNaN(parseInt(resolution.width, 10))) {\n throw new OTHelpers.Error('stream preferred width must be an integer', 'Subscriber');\n }\n\n video.preferredWidth = parseInt(resolution.width, 10);\n } else {\n video.preferredWidth = void 0;\n }\n\n if (resolution && resolution.height) {\n if (isNaN(parseInt(resolution.height, 10))) {\n throw new OTHelpers.Error('stream preferred height must be an integer', 'Subscriber');\n }\n\n video.preferredHeight = parseInt(resolution.height, 10);\n } else {\n video.preferredHeight = void 0;\n }\n\n session._.subscriberChannelUpdate(self, associatedWidget(), video, {\n preferredWidth: video.preferredWidth || 0,\n preferredHeight: video.preferredHeight || 0\n });\n };\n\n this.getPreferredResolution = function getPreferredResolution() {\n const videoChannel = self.getChannelsOfType('video')[0];\n\n if (!videoChannel || !videoChannel.preferredWidth && !videoChannel.preferredHeight) {\n return void 0;\n }\n\n return {\n width: videoChannel.preferredWidth,\n height: videoChannel.preferredHeight\n };\n };\n\n this.setPreferredFrameRate = function setPreferredFrameRate(preferredFrameRate) {\n if (!isBeingSubscribedTo()) {\n logging.warn('setPreferredFrameRate has no affect when called by a publisher');\n return;\n }\n\n if (session.sessionInfo.p2pEnabled) {\n logging.warn('Stream.setPreferredFrameRate will not work in a P2P Session');\n return;\n }\n\n if (preferredFrameRate && isNaN(parseFloat(preferredFrameRate))) {\n throw new OTHelpers.Error('stream preferred frameRate must be a number', 'Subscriber');\n } // This duplicates some of the code in updateChannelsOfType. We do this for a\n // couple of reasons:\n // 1. Because most of the work that updateChannelsOfType does is in calling\n // getChannelsOfType, which we need to do here anyway so that we can update\n // the value of preferredFrameRate in the Video Channel.\n // 2. updateChannelsOfType on only sends a message to update the channel in\n // Rumor. The client then expects to receive a subsequent channel update\n // indicating that the update was successful. We don't receive those updates\n // for preferredFrameRate/maxResolution so we need to complete both tasks and it's\n // neater to do the related tasks right next to each other.\n // 3. This code shouldn't be in Stream anyway. There is way too much coupling\n // between Stream, Session, Publisher, and Subscriber. This will eventually be\n // fixed, and when it is then it will be easier to exact the code if it's a\n // single piece.\n //\n\n\n const video = self.getChannelsOfType('video')[0];\n\n if (video) {\n video.preferredFrameRate = preferredFrameRate ? parseFloat(preferredFrameRate) : null;\n\n session._.subscriberChannelUpdate(self, associatedWidget(), video, {\n preferredFrameRate: video.preferredFrameRate || 0\n });\n }\n };\n\n this.getPreferredFrameRate = function getPreferredFrameRate() {\n const videoChannel = self.getChannelsOfType('video')[0];\n return videoChannel ? videoChannel.preferredFrameRate : null;\n };\n\n let updateChannelsOfType = function updateChannelsOfType(channelType, attributes) {\n let setChannelActiveState;\n\n if (!self.publisher) {\n const subscriber = associatedWidget();\n\n setChannelActiveState = channel => session._.subscriberChannelUpdate(self, subscriber, channel, attributes);\n } else {\n setChannelActiveState = channel => session._.streamChannelUpdate(self, channel, attributes);\n }\n\n self.getChannelsOfType(channelType).forEach(setChannelActiveState);\n };\n\n this.destroyed = false;\n this.destroyedReason = void 0;\n\n this.destroy = function destroy(reason, quiet) {\n if (reason === void 0) {\n reason = 'clientDisconnected';\n }\n\n destroyedReason = reason;\n self.destroyed = true;\n self.destroyedReason = destroyedReason;\n\n if (quiet !== true) {\n self.dispatchEvent(new Events.DestroyedEvent('destroyed', // This should be eventNames.STREAM_DESTROYED, but\n // the value of that is currently shared with Session\n self, destroyedReason));\n }\n }; // PRIVATE STUFF CALLED BY Raptor.Dispatcher\n //\n // Confusingly, this should not be called when you want to change\n // the stream properties. This is used by Raptor dispatch to notify\n // the stream that it's properties have been successfully updated\n //\n // @todo make this sane. Perhaps use setters for the properties that can\n // send the appropriate Raptor message. This would require that Streams\n // have access to their session.\n\n\n this._ = {};\n\n this._.updateProperty = function privateUpdateProperty(key, value) {\n if (validPropertyNames.indexOf(key) === -1) {\n logging.warn(`Unknown stream property \"${key}\" was modified to \"${value}\".`);\n return;\n }\n\n const oldValue = self[key];\n const newValue = value;\n\n switch (key) {\n case 'name':\n self[key] = newValue;\n break;\n\n case 'archiving':\n var widget = associatedWidget();\n\n if (self.publisher && widget) {\n widget._.archivingStatus(newValue);\n }\n\n self[key] = newValue;\n break;\n\n default:\n }\n\n const event = new Events.StreamUpdatedEvent(self, key, oldValue, newValue);\n self.dispatchEvent(event);\n }; // Mass update, called by Raptor.Dispatcher\n\n\n this._.update = function privateUpdate(attributes) {\n for (const key in attributes) {\n if (!attributes.hasOwnProperty(key)) {\n continue;\n }\n\n self._.updateProperty(key, attributes[key]);\n }\n };\n\n this._.forceMute = function forceMute(content) {\n if (content.hasOwnProperty('channels')) {\n if (content.channels.includes('audio')) {\n if (self.publisher) {\n self.publisher._.forceMuteAudio();\n }\n }\n }\n };\n\n this._.updateChannel = function privateUpdateChannel(channelId, attributes) {\n const channel = self.getChannel(channelId);\n\n if (channel) {\n channel.update(attributes);\n }\n };\n};\n\n/***/ }),\n/* 169 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\nexports.STATUS = exports.PONG = exports.PING = exports.DISCONNECT = exports.CONNECT = exports.MESSAGE = exports.UNSUBSCRIBE = exports.SUBSCRIBE = void 0;\n// Rumor Messaging for JS\n//\n// https://tbwiki.tokbox.com/index.php/Rumor_:_Messaging_FrameWork\n//\n// @todo Rumor {\n// Add error codes for all the error cases\n// Add Dependability commands\n// }\n// This is used to subscribe to address/addresses. The address/addresses the\n// client specifies here is registered on the server. Once any message is sent to\n// that address/addresses, the client receives that message.\nconst SUBSCRIBE = 0; // This is used to unsubscribe to address / addresses. Once the client unsubscribe\n// to an address, it will stop getting messages sent to that address.\n\nexports.SUBSCRIBE = SUBSCRIBE;\nconst UNSUBSCRIBE = 1; // This is used to send messages to arbitrary address/ addresses. Messages can be\n// anything and Rumor will not care about what is included.\n\nexports.UNSUBSCRIBE = UNSUBSCRIBE;\nconst MESSAGE = 2; // This will be the first message that the client sends to the server. It includes\n// the uniqueId for that client connection and a disconnect_notify address that will\n// be notified once the client disconnects.\n\nexports.MESSAGE = MESSAGE;\nconst CONNECT = 3; // This will be the message used by the server to notify an address that a\n// client disconnected.\n\nexports.CONNECT = CONNECT;\nconst DISCONNECT = 4; // Enhancements to support Keepalives\n\nexports.DISCONNECT = DISCONNECT;\nconst PING = 7;\nexports.PING = PING;\nconst PONG = 8;\nexports.PONG = PONG;\nconst STATUS = 9;\nexports.STATUS = STATUS;\n\n/***/ }),\n/* 170 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst assign = __webpack_require__(7);\n\nconst Capabilities = __webpack_require__(167);\n\nconst eventing = __webpack_require__(5);\n\nconst Events = __webpack_require__(21)();\n\nconst connectionCapabilities = __webpack_require__(701);\n/**\n * The Connection object represents a connection to an OpenTok session. Each client that connects\n * to a session has a unique connection, with a unique connection ID (represented by the\n * id
property of the Connection object for the client).\n * \n * The Session object has a connection
property that is a Connection object.\n * It represents the local client's connection. (A client only has a connection once the\n * client has successfully called the connect()
method of the {@link Session}\n * object.)\n *
\n * The Session object dispatches a connectionCreated
event when each client (including\n * your own) connects to a session (and for clients that are present in the session when you\n * connect). The connectionCreated
event object has a connection
\n * property, which is a Connection object corresponding to the client the event pertains to.\n *
\n * The Stream object has a connection
property that is a Connection object.\n * It represents the connection of the client that is publishing the stream.\n *\n * @class Connection\n * @property {String} connectionId The ID of this connection.\n * @property {Number} creationTime The timestamp for the creation of the connection. This\n * value is calculated in milliseconds.\n * You can convert this value to a Date object by calling new Date(creationTime)
,\n * where creationTime
\n * is the creationTime
property of the Connection object.\n * @property {String} data A string containing metadata describing the\n * connection. When you generate a user token, you can define connection data (see the\n * Token creation overview).\n */\n\n\nfunction Connection(id, creationTime, data, capabilitiesHash, permissionsHash) {\n let destroyedReason;\n this.id = id;\n this.connectionId = id;\n this.creationTime = creationTime ? Number(creationTime) : null;\n this.data = data;\n this.capabilities = connectionCapabilities(capabilitiesHash);\n this.permissions = new Capabilities(permissionsHash);\n this.quality = null;\n eventing(this);\n\n this.destroy = (reason, quiet) => {\n destroyedReason = reason || 'clientDisconnected';\n\n if (quiet !== true) {\n this.dispatchEvent(new Events.DestroyedEvent( // This should be eventNames.CONNECTION_DESTROYED, but\n // the value of that is currently shared with Session\n 'destroyed', this, destroyedReason));\n }\n };\n\n this.destroyed = () => destroyedReason !== undefined;\n\n this.destroyedReason = () => destroyedReason;\n}\n\nConnection.fromHash = (_ref) => {\n let id = _ref.id,\n creationTime = _ref.creationTime,\n data = _ref.data,\n capablities = _ref.capablities,\n permissions = _ref.permissions;\n return new Connection(id, creationTime, data, assign(capablities || {}, {\n supportsWebRTC: true\n }), permissions || []);\n};\n\nmodule.exports = Connection;\n\n/***/ }),\n/* 171 */\n/***/ (function(module, exports) {\n\n/** Detect free variable `global` from Node.js. */\nvar 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);\n\nmodule.exports = freeGlobal;\n\n\n/***/ }),\n/* 172 */\n/***/ (function(module, exports) {\n\n/** Used for built-in method references. */\nvar funcProto = Function.prototype;\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to convert.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n}\n\nmodule.exports = toSource;\n\n\n/***/ }),\n/* 173 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar getNative = __webpack_require__(35);\n\nvar defineProperty = (function() {\n try {\n var func = getNative(Object, 'defineProperty');\n func({}, '', {});\n return func;\n } catch (e) {}\n}());\n\nmodule.exports = defineProperty;\n\n\n/***/ }),\n/* 174 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar copyObject = __webpack_require__(37),\n keys = __webpack_require__(28);\n\n/**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\nfunction baseAssign(object, source) {\n return object && copyObject(source, keys(source), object);\n}\n\nmodule.exports = baseAssign;\n\n\n/***/ }),\n/* 175 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseTimes = __webpack_require__(365),\n isArguments = __webpack_require__(63),\n isArray = __webpack_require__(10),\n isBuffer = __webpack_require__(64),\n isIndex = __webpack_require__(65),\n isTypedArray = __webpack_require__(85);\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n var isArr = isArray(value),\n isArg = !isArr && isArguments(value),\n isBuff = !isArr && !isArg && isBuffer(value),\n isType = !isArr && !isArg && !isBuff && isTypedArray(value),\n skipIndexes = isArr || isArg || isBuff || isType,\n result = skipIndexes ? baseTimes(value.length, String) : [],\n length = result.length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (\n // Safari 9 has enumerable `arguments.length` in strict mode.\n key == 'length' ||\n // Node.js 0.10 has enumerable non-index properties on buffers.\n (isBuff && (key == 'offset' || key == 'parent')) ||\n // PhantomJS 2 has enumerable non-index properties on typed arrays.\n (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||\n // Skip index properties.\n isIndex(key, length)\n ))) {\n result.push(key);\n }\n }\n return result;\n}\n\nmodule.exports = arrayLikeKeys;\n\n\n/***/ }),\n/* 176 */\n/***/ (function(module, exports) {\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n}\n\nmodule.exports = overArg;\n\n\n/***/ }),\n/* 177 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(12);\n\n/** Detect free variable `exports`. */\nvar freeExports = true && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined,\n allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined;\n\n/**\n * Creates a clone of `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\nfunction cloneBuffer(buffer, isDeep) {\n if (isDeep) {\n return buffer.slice();\n }\n var length = buffer.length,\n result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);\n\n buffer.copy(result);\n return result;\n}\n\nmodule.exports = cloneBuffer;\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(84)(module)))\n\n/***/ }),\n/* 178 */\n/***/ (function(module, exports) {\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n return [];\n}\n\nmodule.exports = stubArray;\n\n\n/***/ }),\n/* 179 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayPush = __webpack_require__(116),\n getPrototype = __webpack_require__(117),\n getSymbols = __webpack_require__(115),\n stubArray = __webpack_require__(178);\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols;\n\n/**\n * Creates an array of the own and inherited enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {\n var result = [];\n while (object) {\n arrayPush(result, getSymbols(object));\n object = getPrototype(object);\n }\n return result;\n};\n\nmodule.exports = getSymbolsIn;\n\n\n/***/ }),\n/* 180 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseGetAllKeys = __webpack_require__(181),\n getSymbols = __webpack_require__(115),\n keys = __webpack_require__(28);\n\n/**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeys(object) {\n return baseGetAllKeys(object, keys, getSymbols);\n}\n\nmodule.exports = getAllKeys;\n\n\n/***/ }),\n/* 181 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayPush = __webpack_require__(116),\n isArray = __webpack_require__(10);\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\nmodule.exports = baseGetAllKeys;\n\n\n/***/ }),\n/* 182 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseGetAllKeys = __webpack_require__(181),\n getSymbolsIn = __webpack_require__(179),\n keysIn = __webpack_require__(51);\n\n/**\n * Creates an array of own and inherited enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeysIn(object) {\n return baseGetAllKeys(object, keysIn, getSymbolsIn);\n}\n\nmodule.exports = getAllKeysIn;\n\n\n/***/ }),\n/* 183 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar getNative = __webpack_require__(35),\n root = __webpack_require__(12);\n\n/* Built-in method references that are verified to be native. */\nvar Set = getNative(root, 'Set');\n\nmodule.exports = Set;\n\n\n/***/ }),\n/* 184 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar getNative = __webpack_require__(35),\n root = __webpack_require__(12);\n\n/* Built-in method references that are verified to be native. */\nvar WeakMap = getNative(root, 'WeakMap');\n\nmodule.exports = WeakMap;\n\n\n/***/ }),\n/* 185 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar root = __webpack_require__(12);\n\n/** Built-in value references. */\nvar Uint8Array = root.Uint8Array;\n\nmodule.exports = Uint8Array;\n\n\n/***/ }),\n/* 186 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar cloneArrayBuffer = __webpack_require__(118);\n\n/**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\nfunction cloneTypedArray(typedArray, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n}\n\nmodule.exports = cloneTypedArray;\n\n\n/***/ }),\n/* 187 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseCreate = __webpack_require__(86),\n getPrototype = __webpack_require__(117),\n isPrototype = __webpack_require__(67);\n\n/**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneObject(object) {\n return (typeof object.constructor == 'function' && !isPrototype(object))\n ? baseCreate(getPrototype(object))\n : {};\n}\n\nmodule.exports = initCloneObject;\n\n\n/***/ }),\n/* 188 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar MapCache = __webpack_require__(110);\n\n/** Error message constants. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/**\n * Creates a function that memoizes the result of `func`. If `resolver` is\n * provided, it determines the cache key for storing the result based on the\n * arguments provided to the memoized function. By default, the first argument\n * provided to the memoized function is used as the map cache key. The `func`\n * is invoked with the `this` binding of the memoized function.\n *\n * **Note:** The cache is exposed as the `cache` property on the memoized\n * function. Its creation may be customized by replacing the `_.memoize.Cache`\n * constructor with one whose instances implement the\n * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)\n * method interface of `clear`, `delete`, `get`, `has`, and `set`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to have its output memoized.\n * @param {Function} [resolver] The function to resolve the cache key.\n * @returns {Function} Returns the new memoized function.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n * var other = { 'c': 3, 'd': 4 };\n *\n * var values = _.memoize(_.values);\n * values(object);\n * // => [1, 2]\n *\n * values(other);\n * // => [3, 4]\n *\n * object.a = 2;\n * values(object);\n * // => [1, 2]\n *\n * // Modify the result cache.\n * values.cache.set(object, ['a', 'b']);\n * values(object);\n * // => ['a', 'b']\n *\n * // Replace `_.memoize.Cache`.\n * _.memoize.Cache = WeakMap;\n */\nfunction memoize(func, resolver) {\n if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var memoized = function() {\n var args = arguments,\n key = resolver ? resolver.apply(this, args) : args[0],\n cache = memoized.cache;\n\n if (cache.has(key)) {\n return cache.get(key);\n }\n var result = func.apply(this, args);\n memoized.cache = cache.set(key, result) || cache;\n return result;\n };\n memoized.cache = new (memoize.Cache || MapCache);\n return memoized;\n}\n\n// Expose `MapCache`.\nmemoize.Cache = MapCache;\n\nmodule.exports = memoize;\n\n\n/***/ }),\n/* 189 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseRest = __webpack_require__(87),\n isIterateeCall = __webpack_require__(122);\n\n/**\n * Creates a function like `_.assign`.\n *\n * @private\n * @param {Function} assigner The function to assign values.\n * @returns {Function} Returns the new assigner function.\n */\nfunction createAssigner(assigner) {\n return baseRest(function(object, sources) {\n var index = -1,\n length = sources.length,\n customizer = length > 1 ? sources[length - 1] : undefined,\n guard = length > 2 ? sources[2] : undefined;\n\n customizer = (assigner.length > 3 && typeof customizer == 'function')\n ? (length--, customizer)\n : undefined;\n\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n customizer = length < 3 ? undefined : customizer;\n length = 1;\n }\n object = Object(object);\n while (++index < length) {\n var source = sources[index];\n if (source) {\n assigner(object, source, index, customizer);\n }\n }\n return object;\n });\n}\n\nmodule.exports = createAssigner;\n\n\n/***/ }),\n/* 190 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar apply = __webpack_require__(120);\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max;\n\n/**\n * A specialized version of `baseRest` which transforms the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @param {Function} transform The rest array transform.\n * @returns {Function} Returns the new function.\n */\nfunction overRest(func, start, transform) {\n start = nativeMax(start === undefined ? (func.length - 1) : start, 0);\n return function() {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = transform(array);\n return apply(func, this, otherArgs);\n };\n}\n\nmodule.exports = overRest;\n\n\n/***/ }),\n/* 191 */\n/***/ (function(module, exports) {\n\n/** Used to detect hot functions by number of calls within a span of milliseconds. */\nvar HOT_COUNT = 800,\n HOT_SPAN = 16;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeNow = Date.now;\n\n/**\n * Creates a function that'll short out and invoke `identity` instead\n * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`\n * milliseconds.\n *\n * @private\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new shortable function.\n */\nfunction shortOut(func) {\n var count = 0,\n lastCalled = 0;\n\n return function() {\n var stamp = nativeNow(),\n remaining = HOT_SPAN - (stamp - lastCalled);\n\n lastCalled = stamp;\n if (remaining > 0) {\n if (++count >= HOT_COUNT) {\n return arguments[0];\n }\n } else {\n count = 0;\n }\n return func.apply(undefined, arguments);\n };\n}\n\nmodule.exports = shortOut;\n\n\n/***/ }),\n/* 192 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = __webpack_require__(393)() ? WeakMap : __webpack_require__(394);\n\n\n/***/ }),\n/* 193 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar create = Object.create, getPrototypeOf = Object.getPrototypeOf, plainObject = {};\n\nmodule.exports = function (/* CustomCreate*/) {\n\tvar setPrototypeOf = Object.setPrototypeOf, customCreate = arguments[0] || create;\n\tif (typeof setPrototypeOf !== \"function\") return false;\n\treturn getPrototypeOf(setPrototypeOf(customCreate(null), plainObject)) === plainObject;\n};\n\n\n/***/ }),\n/* 194 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* eslint no-proto: \"off\" */\n\n// Big thanks to @WebReflection for sorting this out\n// https://gist.github.com/WebReflection/5593554\n\n\n\nvar isObject = __webpack_require__(195)\n , value = __webpack_require__(29)\n , objIsPrototypeOf = Object.prototype.isPrototypeOf\n , defineProperty = Object.defineProperty\n , nullDesc = { configurable: true, enumerable: false, writable: true, value: undefined }\n , validate;\n\nvalidate = function (obj, prototype) {\n\tvalue(obj);\n\tif (prototype === null || isObject(prototype)) return obj;\n\tthrow new TypeError(\"Prototype must be null or an object\");\n};\n\nmodule.exports = (function (status) {\n\tvar fn, set;\n\tif (!status) return null;\n\tif (status.level === 2) {\n\t\tif (status.set) {\n\t\t\tset = status.set;\n\t\t\tfn = function (obj, prototype) {\n\t\t\t\tset.call(validate(obj, prototype), prototype);\n\t\t\t\treturn obj;\n\t\t\t};\n\t\t} else {\n\t\t\tfn = function (obj, prototype) {\n\t\t\t\tvalidate(obj, prototype).__proto__ = prototype;\n\t\t\t\treturn obj;\n\t\t\t};\n\t\t}\n\t} else {\n\t\tfn = function self(obj, prototype) {\n\t\t\tvar isNullBase;\n\t\t\tvalidate(obj, prototype);\n\t\t\tisNullBase = objIsPrototypeOf.call(self.nullPolyfill, obj);\n\t\t\tif (isNullBase) delete self.nullPolyfill.__proto__;\n\t\t\tif (prototype === null) prototype = self.nullPolyfill;\n\t\t\tobj.__proto__ = prototype;\n\t\t\tif (isNullBase) defineProperty(self.nullPolyfill, \"__proto__\", nullDesc);\n\t\t\treturn obj;\n\t\t};\n\t}\n\treturn Object.defineProperty(fn, \"level\", {\n\t\tconfigurable: false,\n\t\tenumerable: false,\n\t\twritable: false,\n\t\tvalue: status.level\n\t});\n})(\n\t(function () {\n\t\tvar tmpObj1 = Object.create(null)\n\t\t , tmpObj2 = {}\n\t\t , set\n\t\t , desc = Object.getOwnPropertyDescriptor(Object.prototype, \"__proto__\");\n\n\t\tif (desc) {\n\t\t\ttry {\n\t\t\t\tset = desc.set; // Opera crashes at this point\n\t\t\t\tset.call(tmpObj1, tmpObj2);\n\t\t\t} catch (ignore) {}\n\t\t\tif (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { set: set, level: 2 };\n\t\t}\n\n\t\ttmpObj1.__proto__ = tmpObj2;\n\t\tif (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 2 };\n\n\t\ttmpObj1 = {};\n\t\ttmpObj1.__proto__ = tmpObj2;\n\t\tif (Object.getPrototypeOf(tmpObj1) === tmpObj2) return { level: 1 };\n\n\t\treturn false;\n\t})()\n);\n\n__webpack_require__(396);\n\n\n/***/ }),\n/* 195 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isValue = __webpack_require__(40);\n\nvar map = { function: true, object: true };\n\nmodule.exports = function (value) { return (isValue(value) && map[typeof value]) || false; };\n\n\n/***/ }),\n/* 196 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isFunction = __webpack_require__(399);\n\nvar classRe = /^\\s*class[\\s{/}]/, functionToString = Function.prototype.toString;\n\nmodule.exports = function (value) {\n\tif (!isFunction(value)) return false;\n\tif (classRe.test(functionToString.call(value))) return false;\n\treturn true;\n};\n\n\n/***/ }),\n/* 197 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isValue = __webpack_require__(40);\n\nvar forEach = Array.prototype.forEach, create = Object.create;\n\nvar process = function (src, obj) {\n\tvar key;\n\tfor (key in src) obj[key] = src[key];\n};\n\n// eslint-disable-next-line no-unused-vars\nmodule.exports = function (opts1/*, …options*/) {\n\tvar result = create(null);\n\tforEach.call(arguments, function (options) {\n\t\tif (!isValue(options)) return;\n\t\tprocess(Object(options), result);\n\t});\n\treturn result;\n};\n\n\n/***/ }),\n/* 198 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = __webpack_require__(406)() ? String.prototype.contains : __webpack_require__(407);\n\n\n/***/ }),\n/* 199 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isArguments = __webpack_require__(89)\n , isString = __webpack_require__(90)\n , ArrayIterator = __webpack_require__(408)\n , StringIterator = __webpack_require__(437)\n , iterable = __webpack_require__(438)\n , iteratorSymbol = __webpack_require__(41).iterator;\n\nmodule.exports = function (obj) {\n\tif (typeof iterable(obj)[iteratorSymbol] === \"function\") return obj[iteratorSymbol]();\n\tif (isArguments(obj)) return new ArrayIterator(obj);\n\tif (isString(obj)) return new StringIterator(obj);\n\treturn new ArrayIterator(obj);\n};\n\n\n/***/ }),\n/* 200 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isSymbol = __webpack_require__(413);\n\nmodule.exports = function (value) {\n\tif (!isSymbol(value)) throw new TypeError(value + \" is not a symbol\");\n\treturn value;\n};\n\n\n/***/ }),\n/* 201 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar clear = __webpack_require__(417)\n , assign = __webpack_require__(125)\n , callable = __webpack_require__(69)\n , value = __webpack_require__(29)\n , d = __webpack_require__(30)\n , autoBind = __webpack_require__(418)\n , Symbol = __webpack_require__(41);\n\nvar defineProperty = Object.defineProperty, defineProperties = Object.defineProperties, Iterator;\n\nmodule.exports = Iterator = function (list, context) {\n\tif (!(this instanceof Iterator)) throw new TypeError(\"Constructor requires 'new'\");\n\tdefineProperties(this, {\n\t\t__list__: d(\"w\", value(list)),\n\t\t__context__: d(\"w\", context),\n\t\t__nextIndex__: d(\"w\", 0)\n\t});\n\tif (!context) return;\n\tcallable(context.on);\n\tcontext.on(\"_add\", this._onAdd);\n\tcontext.on(\"_delete\", this._onDelete);\n\tcontext.on(\"_clear\", this._onClear);\n};\n\n// Internal %IteratorPrototype% doesn't expose its constructor\ndelete Iterator.prototype.constructor;\n\ndefineProperties(\n\tIterator.prototype,\n\tassign(\n\t\t{\n\t\t\t_next: d(function () {\n\t\t\t\tvar i;\n\t\t\t\tif (!this.__list__) return undefined;\n\t\t\t\tif (this.__redo__) {\n\t\t\t\t\ti = this.__redo__.shift();\n\t\t\t\t\tif (i !== undefined) return i;\n\t\t\t\t}\n\t\t\t\tif (this.__nextIndex__ < this.__list__.length) return this.__nextIndex__++;\n\t\t\t\tthis._unBind();\n\t\t\t\treturn undefined;\n\t\t\t}),\n\t\t\tnext: d(function () {\n\t\t\t\treturn this._createResult(this._next());\n\t\t\t}),\n\t\t\t_createResult: d(function (i) {\n\t\t\t\tif (i === undefined) return { done: true, value: undefined };\n\t\t\t\treturn { done: false, value: this._resolve(i) };\n\t\t\t}),\n\t\t\t_resolve: d(function (i) {\n\t\t\t\treturn this.__list__[i];\n\t\t\t}),\n\t\t\t_unBind: d(function () {\n\t\t\t\tthis.__list__ = null;\n\t\t\t\tdelete this.__redo__;\n\t\t\t\tif (!this.__context__) return;\n\t\t\t\tthis.__context__.off(\"_add\", this._onAdd);\n\t\t\t\tthis.__context__.off(\"_delete\", this._onDelete);\n\t\t\t\tthis.__context__.off(\"_clear\", this._onClear);\n\t\t\t\tthis.__context__ = null;\n\t\t\t}),\n\t\t\ttoString: d(function () {\n\t\t\t\treturn \"[object \" + (this[Symbol.toStringTag] || \"Object\") + \"]\";\n\t\t\t})\n\t\t},\n\t\tautoBind({\n\t\t\t_onAdd: d(function (index) {\n\t\t\t\tif (index >= this.__nextIndex__) return;\n\t\t\t\t++this.__nextIndex__;\n\t\t\t\tif (!this.__redo__) {\n\t\t\t\t\tdefineProperty(this, \"__redo__\", d(\"c\", [index]));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.__redo__.forEach(function (redo, i) {\n\t\t\t\t\tif (redo >= index) this.__redo__[i] = ++redo;\n\t\t\t\t}, this);\n\t\t\t\tthis.__redo__.push(index);\n\t\t\t}),\n\t\t\t_onDelete: d(function (index) {\n\t\t\t\tvar i;\n\t\t\t\tif (index >= this.__nextIndex__) return;\n\t\t\t\t--this.__nextIndex__;\n\t\t\t\tif (!this.__redo__) return;\n\t\t\t\ti = this.__redo__.indexOf(index);\n\t\t\t\tif (i !== -1) this.__redo__.splice(i, 1);\n\t\t\t\tthis.__redo__.forEach(function (redo, j) {\n\t\t\t\t\tif (redo > index) this.__redo__[j] = --redo;\n\t\t\t\t}, this);\n\t\t\t}),\n\t\t\t_onClear: d(function () {\n\t\t\t\tif (this.__redo__) clear.call(this.__redo__);\n\t\t\t\tthis.__nextIndex__ = 0;\n\t\t\t})\n\t\t})\n\t)\n);\n\ndefineProperty(\n\tIterator.prototype,\n\tSymbol.iterator,\n\td(function () {\n\t\treturn this;\n\t})\n);\n\n\n/***/ }),\n/* 202 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar isValue = __webpack_require__(53)\n , isObject = __webpack_require__(124)\n , stringCoerce = __webpack_require__(420)\n , toShortString = __webpack_require__(421);\n\nvar resolveMessage = function (message, value) {\n\treturn message.replace(\"%v\", toShortString(value));\n};\n\nmodule.exports = function (value, defaultMessage, inputOptions) {\n\tif (!isObject(inputOptions)) throw new TypeError(resolveMessage(defaultMessage, value));\n\tif (!isValue(value)) {\n\t\tif (\"default\" in inputOptions) return inputOptions[\"default\"];\n\t\tif (inputOptions.isOptional) return null;\n\t}\n\tvar errorMessage = stringCoerce(inputOptions.errorMessage);\n\tif (!isValue(errorMessage)) errorMessage = defaultMessage;\n\tthrow new TypeError(resolveMessage(errorMessage, value));\n};\n\n\n/***/ }),\n/* 203 */\n/***/ (function(module, exports) {\n\n/**\n * The base implementation of `_.findIndex` and `_.findLastIndex` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {number} fromIndex The index to search from.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction baseFindIndex(array, predicate, fromIndex, fromRight) {\n var length = array.length,\n index = fromIndex + (fromRight ? 1 : -1);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (predicate(array[index], index, array)) {\n return index;\n }\n }\n return -1;\n}\n\nmodule.exports = baseFindIndex;\n\n\n/***/ }),\n/* 204 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = __webpack_require__(449);\n\n/***/ }),\n/* 205 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function bind(fn, thisArg) {\n return function wrap() {\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n return fn.apply(thisArg, args);\n };\n};\n\n\n/***/ }),\n/* 206 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar utils = __webpack_require__(15);\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n var hashmarkIndex = url.indexOf('#');\n if (hashmarkIndex !== -1) {\n url = url.slice(0, hashmarkIndex);\n }\n\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n\n\n/***/ }),\n/* 207 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function isCancel(value) {\n return !!(value && value.__CANCEL__);\n};\n\n\n/***/ }),\n/* 208 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(process) {\n\nvar utils = __webpack_require__(15);\nvar normalizeHeaderName = __webpack_require__(454);\n\nvar DEFAULT_CONTENT_TYPE = {\n 'Content-Type': 'application/x-www-form-urlencoded'\n};\n\nfunction setContentTypeIfUnset(headers, value) {\n if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {\n headers['Content-Type'] = value;\n }\n}\n\nfunction getDefaultAdapter() {\n var adapter;\n if (typeof XMLHttpRequest !== 'undefined') {\n // For browsers use XHR adapter\n adapter = __webpack_require__(209);\n } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {\n // For node use HTTP adapter\n adapter = __webpack_require__(209);\n }\n return adapter;\n}\n\nvar defaults = {\n adapter: getDefaultAdapter(),\n\n transformRequest: [function transformRequest(data, headers) {\n normalizeHeaderName(headers, 'Accept');\n normalizeHeaderName(headers, 'Content-Type');\n if (utils.isFormData(data) ||\n utils.isArrayBuffer(data) ||\n utils.isBuffer(data) ||\n utils.isStream(data) ||\n utils.isFile(data) ||\n utils.isBlob(data)\n ) {\n return data;\n }\n if (utils.isArrayBufferView(data)) {\n return data.buffer;\n }\n if (utils.isURLSearchParams(data)) {\n setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');\n return data.toString();\n }\n if (utils.isObject(data)) {\n setContentTypeIfUnset(headers, 'application/json;charset=utf-8');\n return JSON.stringify(data);\n }\n return data;\n }],\n\n transformResponse: [function transformResponse(data) {\n /*eslint no-param-reassign:0*/\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n } catch (e) { /* Ignore */ }\n }\n return data;\n }],\n\n /**\n * A timeout in milliseconds to abort a request. If set to 0 (default) a\n * timeout is not created.\n */\n timeout: 0,\n\n xsrfCookieName: 'XSRF-TOKEN',\n xsrfHeaderName: 'X-XSRF-TOKEN',\n\n maxContentLength: -1,\n maxBodyLength: -1,\n\n validateStatus: function validateStatus(status) {\n return status >= 200 && status < 300;\n }\n};\n\ndefaults.headers = {\n common: {\n 'Accept': 'application/json, text/plain, */*'\n }\n};\n\nutils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {\n defaults.headers[method] = {};\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);\n});\n\nmodule.exports = defaults;\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(68)))\n\n/***/ }),\n/* 209 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nvar utils = __webpack_require__(15);\nvar settle = __webpack_require__(455);\nvar cookies = __webpack_require__(457);\nvar buildURL = __webpack_require__(206);\nvar buildFullPath = __webpack_require__(458);\nvar parseHeaders = __webpack_require__(461);\nvar isURLSameOrigin = __webpack_require__(462);\nvar createError = __webpack_require__(210);\n\nmodule.exports = function xhrAdapter(config) {\n return new Promise(function dispatchXhrRequest(resolve, reject) {\n var requestData = config.data;\n var requestHeaders = config.headers;\n\n if (utils.isFormData(requestData)) {\n delete requestHeaders['Content-Type']; // Let the browser set it\n }\n\n var request = new XMLHttpRequest();\n\n // HTTP basic authentication\n if (config.auth) {\n var username = config.auth.username || '';\n var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';\n requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n }\n\n var fullPath = buildFullPath(config.baseURL, config.url);\n request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);\n\n // Set the request timeout in MS\n request.timeout = config.timeout;\n\n // Listen for ready state\n request.onreadystatechange = function handleLoad() {\n if (!request || request.readyState !== 4) {\n return;\n }\n\n // The request errored out and we didn't get a response, this will be\n // handled by onerror instead\n // With one exception: request that using file: protocol, most browsers\n // will return status as 0 even though it's a successful request\n if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n return;\n }\n\n // Prepare the response\n var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;\n var response = {\n data: responseData,\n status: request.status,\n statusText: request.statusText,\n headers: responseHeaders,\n config: config,\n request: request\n };\n\n settle(resolve, reject, response);\n\n // Clean up request\n request = null;\n };\n\n // Handle browser request cancellation (as opposed to a manual cancellation)\n request.onabort = function handleAbort() {\n if (!request) {\n return;\n }\n\n reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\n // Clean up request\n request = null;\n };\n\n // Handle low level network errors\n request.onerror = function handleError() {\n // Real errors are hidden from us by the browser\n // onerror should only fire if it's a network error\n reject(createError('Network Error', config, null, request));\n\n // Clean up request\n request = null;\n };\n\n // Handle timeout\n request.ontimeout = function handleTimeout() {\n var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';\n if (config.timeoutErrorMessage) {\n timeoutErrorMessage = config.timeoutErrorMessage;\n }\n reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',\n request));\n\n // Clean up request\n request = null;\n };\n\n // Add xsrf header\n // This is only done if running in a standard browser environment.\n // Specifically not if we're in a web worker, or react-native.\n if (utils.isStandardBrowserEnv()) {\n // Add xsrf header\n var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?\n cookies.read(config.xsrfCookieName) :\n undefined;\n\n if (xsrfValue) {\n requestHeaders[config.xsrfHeaderName] = xsrfValue;\n }\n }\n\n // Add headers to the request\n if ('setRequestHeader' in request) {\n utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n // Remove Content-Type if data is undefined\n delete requestHeaders[key];\n } else {\n // Otherwise add header to the request\n request.setRequestHeader(key, val);\n }\n });\n }\n\n // Add withCredentials to request if needed\n if (!utils.isUndefined(config.withCredentials)) {\n request.withCredentials = !!config.withCredentials;\n }\n\n // Add responseType to request if needed\n if (config.responseType) {\n try {\n request.responseType = config.responseType;\n } catch (e) {\n // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.\n // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.\n if (config.responseType !== 'json') {\n throw e;\n }\n }\n }\n\n // Handle progress if needed\n if (typeof config.onDownloadProgress === 'function') {\n request.addEventListener('progress', config.onDownloadProgress);\n }\n\n // Not all browsers support upload events\n if (typeof config.onUploadProgress === 'function' && request.upload) {\n request.upload.addEventListener('progress', config.onUploadProgress);\n }\n\n if (config.cancelToken) {\n // Handle cancellation\n config.cancelToken.promise.then(function onCanceled(cancel) {\n if (!request) {\n return;\n }\n\n request.abort();\n reject(cancel);\n // Clean up request\n request = null;\n });\n }\n\n if (!requestData) {\n requestData = null;\n }\n\n // Send the request\n request.send(requestData);\n });\n};\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 210 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar enhanceError = __webpack_require__(456);\n\n/**\n * Create an Error with the specified message, config, error code, request and response.\n *\n * @param {string} message The error message.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The created error.\n */\nmodule.exports = function createError(message, config, code, request, response) {\n var error = new Error(message);\n return enhanceError(error, config, code, request, response);\n};\n\n\n/***/ }),\n/* 211 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar utils = __webpack_require__(15);\n\n/**\n * Config-specific merge-function which creates a new config-object\n * by merging two configuration objects together.\n *\n * @param {Object} config1\n * @param {Object} config2\n * @returns {Object} New object resulting from merging config2 to config1\n */\nmodule.exports = function mergeConfig(config1, config2) {\n // eslint-disable-next-line no-param-reassign\n config2 = config2 || {};\n var config = {};\n\n var valueFromConfig2Keys = ['url', 'method', 'data'];\n var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];\n var defaultToConfig2Keys = [\n 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',\n 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',\n 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',\n 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',\n 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'\n ];\n var directMergeKeys = ['validateStatus'];\n\n function getMergedValue(target, source) {\n if (utils.isPlainObject(target) && utils.isPlainObject(source)) {\n return utils.merge(target, source);\n } else if (utils.isPlainObject(source)) {\n return utils.merge({}, source);\n } else if (utils.isArray(source)) {\n return source.slice();\n }\n return source;\n }\n\n function mergeDeepProperties(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(config1[prop], config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n }\n\n utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(undefined, config2[prop]);\n }\n });\n\n utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);\n\n utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(undefined, config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n });\n\n utils.forEach(directMergeKeys, function merge(prop) {\n if (prop in config2) {\n config[prop] = getMergedValue(config1[prop], config2[prop]);\n } else if (prop in config1) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n });\n\n var axiosKeys = valueFromConfig2Keys\n .concat(mergeDeepPropertiesKeys)\n .concat(defaultToConfig2Keys)\n .concat(directMergeKeys);\n\n var otherKeys = Object\n .keys(config1)\n .concat(Object.keys(config2))\n .filter(function filterAxiosKeys(key) {\n return axiosKeys.indexOf(key) === -1;\n });\n\n utils.forEach(otherKeys, mergeDeepProperties);\n\n return config;\n};\n\n\n/***/ }),\n/* 212 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/**\n * A `Cancel` is an object that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The message.\n */\nfunction Cancel(message) {\n this.message = message;\n}\n\nCancel.prototype.toString = function toString() {\n return 'Cancel' + (this.message ? ': ' + this.message : '');\n};\n\nCancel.prototype.__CANCEL__ = true;\n\nmodule.exports = Cancel;\n\n\n/***/ }),\n/* 213 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseConvert = __webpack_require__(467),\n util = __webpack_require__(469);\n\n/**\n * Converts `func` of `name` to an immutable auto-curried iteratee-first data-last\n * version with conversion `options` applied. If `name` is an object its methods\n * will be converted.\n *\n * @param {string} name The name of the function to wrap.\n * @param {Function} [func] The function to wrap.\n * @param {Object} [options] The options object. See `baseConvert` for more details.\n * @returns {Function|Object} Returns the converted function or object.\n */\nfunction convert(name, func, options) {\n return baseConvert(util, name, func, options);\n}\n\nmodule.exports = convert;\n\n\n/***/ }),\n/* 214 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar identity = __webpack_require__(88),\n metaMap = __webpack_require__(215);\n\n/**\n * The base implementation of `setData` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\nvar baseSetData = !metaMap ? identity : function(func, data) {\n metaMap.set(func, data);\n return func;\n};\n\nmodule.exports = baseSetData;\n\n\n/***/ }),\n/* 215 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar WeakMap = __webpack_require__(184);\n\n/** Used to store function metadata. */\nvar metaMap = WeakMap && new WeakMap;\n\nmodule.exports = metaMap;\n\n\n/***/ }),\n/* 216 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar composeArgs = __webpack_require__(217),\n composeArgsRight = __webpack_require__(218),\n countHolders = __webpack_require__(473),\n createCtor = __webpack_require__(95),\n createRecurry = __webpack_require__(219),\n getHolder = __webpack_require__(225),\n reorder = __webpack_require__(482),\n replaceHolders = __webpack_require__(134),\n root = __webpack_require__(12);\n\n/** Used to compose bitmasks for function metadata. */\nvar WRAP_BIND_FLAG = 1,\n WRAP_BIND_KEY_FLAG = 2,\n WRAP_CURRY_FLAG = 8,\n WRAP_CURRY_RIGHT_FLAG = 16,\n WRAP_ARY_FLAG = 128,\n WRAP_FLIP_FLAG = 512;\n\n/**\n * Creates a function that wraps `func` to invoke it with optional `this`\n * binding of `thisArg`, partial application, and currying.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [partialsRight] The arguments to append to those provided\n * to the new function.\n * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {\n var isAry = bitmask & WRAP_ARY_FLAG,\n isBind = bitmask & WRAP_BIND_FLAG,\n isBindKey = bitmask & WRAP_BIND_KEY_FLAG,\n isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),\n isFlip = bitmask & WRAP_FLIP_FLAG,\n Ctor = isBindKey ? undefined : createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length;\n\n while (index--) {\n args[index] = arguments[index];\n }\n if (isCurried) {\n var placeholder = getHolder(wrapper),\n holdersCount = countHolders(args, placeholder);\n }\n if (partials) {\n args = composeArgs(args, partials, holders, isCurried);\n }\n if (partialsRight) {\n args = composeArgsRight(args, partialsRight, holdersRight, isCurried);\n }\n length -= holdersCount;\n if (isCurried && length < arity) {\n var newHolders = replaceHolders(args, placeholder);\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, thisArg,\n args, newHolders, argPos, ary, arity - length\n );\n }\n var thisBinding = isBind ? thisArg : this,\n fn = isBindKey ? thisBinding[func] : func;\n\n length = args.length;\n if (argPos) {\n args = reorder(args, argPos);\n } else if (isFlip && length > 1) {\n args.reverse();\n }\n if (isAry && ary < length) {\n args.length = ary;\n }\n if (this && this !== root && this instanceof wrapper) {\n fn = Ctor || createCtor(fn);\n }\n return fn.apply(thisBinding, args);\n }\n return wrapper;\n}\n\nmodule.exports = createHybrid;\n\n\n/***/ }),\n/* 217 */\n/***/ (function(module, exports) {\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max;\n\n/**\n * Creates an array that is the composition of partially applied arguments,\n * placeholders, and provided arguments into a single array of arguments.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to prepend to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\nfunction composeArgs(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersLength = holders.length,\n leftIndex = -1,\n leftLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(leftLength + rangeLength),\n isUncurried = !isCurried;\n\n while (++leftIndex < leftLength) {\n result[leftIndex] = partials[leftIndex];\n }\n while (++argsIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[holders[argsIndex]] = args[argsIndex];\n }\n }\n while (rangeLength--) {\n result[leftIndex++] = args[argsIndex++];\n }\n return result;\n}\n\nmodule.exports = composeArgs;\n\n\n/***/ }),\n/* 218 */\n/***/ (function(module, exports) {\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max;\n\n/**\n * This function is like `composeArgs` except that the arguments composition\n * is tailored for `_.partialRight`.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to append to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\nfunction composeArgsRight(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersIndex = -1,\n holdersLength = holders.length,\n rightIndex = -1,\n rightLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(rangeLength + rightLength),\n isUncurried = !isCurried;\n\n while (++argsIndex < rangeLength) {\n result[argsIndex] = args[argsIndex];\n }\n var offset = argsIndex;\n while (++rightIndex < rightLength) {\n result[offset + rightIndex] = partials[rightIndex];\n }\n while (++holdersIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[offset + holders[holdersIndex]] = args[argsIndex++];\n }\n }\n return result;\n}\n\nmodule.exports = composeArgsRight;\n\n\n/***/ }),\n/* 219 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isLaziable = __webpack_require__(474),\n setData = __webpack_require__(223),\n setWrapToString = __webpack_require__(224);\n\n/** Used to compose bitmasks for function metadata. */\nvar WRAP_BIND_FLAG = 1,\n WRAP_BIND_KEY_FLAG = 2,\n WRAP_CURRY_BOUND_FLAG = 4,\n WRAP_CURRY_FLAG = 8,\n WRAP_PARTIAL_FLAG = 32,\n WRAP_PARTIAL_RIGHT_FLAG = 64;\n\n/**\n * Creates a function that wraps `func` to continue currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {Function} wrapFunc The function to create the `func` wrapper.\n * @param {*} placeholder The placeholder value.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\nfunction createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {\n var isCurry = bitmask & WRAP_CURRY_FLAG,\n newHolders = isCurry ? holders : undefined,\n newHoldersRight = isCurry ? undefined : holders,\n newPartials = isCurry ? partials : undefined,\n newPartialsRight = isCurry ? undefined : partials;\n\n bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);\n bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);\n\n if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {\n bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);\n }\n var newData = [\n func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,\n newHoldersRight, argPos, ary, arity\n ];\n\n var result = wrapFunc.apply(undefined, newData);\n if (isLaziable(func)) {\n setData(result, newData);\n }\n result.placeholder = placeholder;\n return setWrapToString(result, func, bitmask);\n}\n\nmodule.exports = createRecurry;\n\n\n/***/ }),\n/* 220 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar metaMap = __webpack_require__(215),\n noop = __webpack_require__(221);\n\n/**\n * Gets metadata for `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {*} Returns the metadata for `func`.\n */\nvar getData = !metaMap ? noop : function(func) {\n return metaMap.get(func);\n};\n\nmodule.exports = getData;\n\n\n/***/ }),\n/* 221 */\n/***/ (function(module, exports) {\n\n/**\n * This method returns `undefined`.\n *\n * @static\n * @memberOf _\n * @since 2.3.0\n * @category Util\n * @example\n *\n * _.times(2, _.noop);\n * // => [undefined, undefined]\n */\nfunction noop() {\n // No operation performed.\n}\n\nmodule.exports = noop;\n\n\n/***/ }),\n/* 222 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseCreate = __webpack_require__(86),\n baseLodash = __webpack_require__(133);\n\n/**\n * The base constructor for creating `lodash` wrapper objects.\n *\n * @private\n * @param {*} value The value to wrap.\n * @param {boolean} [chainAll] Enable explicit method chain sequences.\n */\nfunction LodashWrapper(value, chainAll) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__chain__ = !!chainAll;\n this.__index__ = 0;\n this.__values__ = undefined;\n}\n\nLodashWrapper.prototype = baseCreate(baseLodash.prototype);\nLodashWrapper.prototype.constructor = LodashWrapper;\n\nmodule.exports = LodashWrapper;\n\n\n/***/ }),\n/* 223 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseSetData = __webpack_require__(214),\n shortOut = __webpack_require__(191);\n\n/**\n * Sets metadata for `func`.\n *\n * **Note:** If this function becomes hot, i.e. is invoked a lot in a short\n * period of time, it will trip its breaker and transition to an identity\n * function to avoid garbage collection pauses in V8. See\n * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)\n * for more details.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\nvar setData = shortOut(baseSetData);\n\nmodule.exports = setData;\n\n\n/***/ }),\n/* 224 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar getWrapDetails = __webpack_require__(479),\n insertWrapDetails = __webpack_require__(480),\n setToString = __webpack_require__(121),\n updateWrapDetails = __webpack_require__(481);\n\n/**\n * Sets the `toString` method of `wrapper` to mimic the source of `reference`\n * with wrapper details in a comment at the top of the source body.\n *\n * @private\n * @param {Function} wrapper The function to modify.\n * @param {Function} reference The reference function.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Function} Returns `wrapper`.\n */\nfunction setWrapToString(wrapper, reference, bitmask) {\n var source = (reference + '');\n return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));\n}\n\nmodule.exports = setWrapToString;\n\n\n/***/ }),\n/* 225 */\n/***/ (function(module, exports) {\n\n/**\n * Gets the argument placeholder value for `func`.\n *\n * @private\n * @param {Function} func The function to inspect.\n * @returns {*} Returns the placeholder value.\n */\nfunction getHolder(func) {\n var object = func;\n return object.placeholder;\n}\n\nmodule.exports = getHolder;\n\n\n/***/ }),\n/* 226 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseTrim = __webpack_require__(486),\n isObject = __webpack_require__(8),\n isSymbol = __webpack_require__(70);\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = baseTrim(value);\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = toNumber;\n\n\n/***/ }),\n/* 227 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseIsEqualDeep = __webpack_require__(494),\n isObjectLike = __webpack_require__(14);\n\n/**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Unordered comparison\n * 2 - Partial comparison\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\nfunction baseIsEqual(value, other, bitmask, customizer, stack) {\n if (value === other) {\n return true;\n }\n if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {\n return value !== value && other !== other;\n }\n return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);\n}\n\nmodule.exports = baseIsEqual;\n\n\n/***/ }),\n/* 228 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar SetCache = __webpack_require__(92),\n arraySome = __webpack_require__(229),\n cacheHas = __webpack_require__(94);\n\n/** Used to compose bitmasks for value comparisons. */\nvar COMPARE_PARTIAL_FLAG = 1,\n COMPARE_UNORDERED_FLAG = 2;\n\n/**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\nfunction equalArrays(array, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n arrLength = array.length,\n othLength = other.length;\n\n if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n return false;\n }\n // Check that cyclic values are equal.\n var arrStacked = stack.get(array);\n var othStacked = stack.get(other);\n if (arrStacked && othStacked) {\n return arrStacked == other && othStacked == array;\n }\n var index = -1,\n result = true,\n seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;\n\n stack.set(array, other);\n stack.set(other, array);\n\n // Ignore non-index properties.\n while (++index < arrLength) {\n var arrValue = array[index],\n othValue = other[index];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, arrValue, index, other, array, stack)\n : customizer(arrValue, othValue, index, array, other, stack);\n }\n if (compared !== undefined) {\n if (compared) {\n continue;\n }\n result = false;\n break;\n }\n // Recursively compare arrays (susceptible to call stack limits).\n if (seen) {\n if (!arraySome(other, function(othValue, othIndex) {\n if (!cacheHas(seen, othIndex) &&\n (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n return seen.push(othIndex);\n }\n })) {\n result = false;\n break;\n }\n } else if (!(\n arrValue === othValue ||\n equalFunc(arrValue, othValue, bitmask, customizer, stack)\n )) {\n result = false;\n break;\n }\n }\n stack['delete'](array);\n stack['delete'](other);\n return result;\n}\n\nmodule.exports = equalArrays;\n\n\n/***/ }),\n/* 229 */\n/***/ (function(module, exports) {\n\n/**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\nfunction arraySome(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (predicate(array[index], index, array)) {\n return true;\n }\n }\n return false;\n}\n\nmodule.exports = arraySome;\n\n\n/***/ }),\n/* 230 */\n/***/ (function(module, exports) {\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n\n map.forEach(function(value, key) {\n result[++index] = [key, value];\n });\n return result;\n}\n\nmodule.exports = mapToArray;\n\n\n/***/ }),\n/* 231 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isObject = __webpack_require__(8);\n\n/**\n * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` if suitable for strict\n * equality comparisons, else `false`.\n */\nfunction isStrictComparable(value) {\n return value === value && !isObject(value);\n}\n\nmodule.exports = isStrictComparable;\n\n\n/***/ }),\n/* 232 */\n/***/ (function(module, exports) {\n\n/**\n * A specialized version of `matchesProperty` for source values suitable\n * for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\nfunction matchesStrictComparable(key, srcValue) {\n return function(object) {\n if (object == null) {\n return false;\n }\n return object[key] === srcValue &&\n (srcValue !== undefined || (key in Object(object)));\n };\n}\n\nmodule.exports = matchesStrictComparable;\n\n\n/***/ }),\n/* 233 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar memoizeCapped = __webpack_require__(499);\n\n/** Used to match property names within property paths. */\nvar rePropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g;\n\n/** Used to match backslashes in property paths. */\nvar reEscapeChar = /\\\\(\\\\)?/g;\n\n/**\n * Converts `string` to a property path array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the property path array.\n */\nvar stringToPath = memoizeCapped(function(string) {\n var result = [];\n if (string.charCodeAt(0) === 46 /* . */) {\n result.push('');\n }\n string.replace(rePropName, function(match, number, quote, subString) {\n result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));\n });\n return result;\n});\n\nmodule.exports = stringToPath;\n\n\n/***/ }),\n/* 234 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseHasIn = __webpack_require__(501),\n hasPath = __webpack_require__(502);\n\n/**\n * Checks if `path` is a direct or inherited property of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = _.create({ 'a': _.create({ 'b': 2 }) });\n *\n * _.hasIn(object, 'a');\n * // => true\n *\n * _.hasIn(object, 'a.b');\n * // => true\n *\n * _.hasIn(object, ['a', 'b']);\n * // => true\n *\n * _.hasIn(object, 'b');\n * // => false\n */\nfunction hasIn(object, path) {\n return object != null && hasPath(object, path, baseHasIn);\n}\n\nmodule.exports = hasIn;\n\n\n/***/ }),\n/* 235 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayPush = __webpack_require__(116),\n isFlattenable = __webpack_require__(508);\n\n/**\n * The base implementation of `_.flatten` with support for restricting flattening.\n *\n * @private\n * @param {Array} array The array to flatten.\n * @param {number} depth The maximum recursion depth.\n * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n * @param {Array} [result=[]] The initial result value.\n * @returns {Array} Returns the new flattened array.\n */\nfunction baseFlatten(array, depth, predicate, isStrict, result) {\n var index = -1,\n length = array.length;\n\n predicate || (predicate = isFlattenable);\n result || (result = []);\n\n while (++index < length) {\n var value = array[index];\n if (depth > 0 && predicate(value)) {\n if (depth > 1) {\n // Recursively flatten arrays (susceptible to call stack limits).\n baseFlatten(value, depth - 1, predicate, isStrict, result);\n } else {\n arrayPush(result, value);\n }\n } else if (!isStrict) {\n result[result.length] = value;\n }\n }\n return result;\n}\n\nmodule.exports = baseFlatten;\n\n\n/***/ }),\n/* 236 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar createBaseFor = __webpack_require__(515);\n\n/**\n * The base implementation of `baseForOwn` which iterates over `object`\n * properties returned by `keysFunc` and invokes `iteratee` for each property.\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\nvar baseFor = createBaseFor();\n\nmodule.exports = baseFor;\n\n\n/***/ }),\n/* 237 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\nvar punycode = __webpack_require__(516);\nvar util = __webpack_require__(517);\n\nexports.parse = urlParse;\nexports.resolve = urlResolve;\nexports.resolveObject = urlResolveObject;\nexports.format = urlFormat;\n\nexports.Url = Url;\n\nfunction Url() {\n this.protocol = null;\n this.slashes = null;\n this.auth = null;\n this.host = null;\n this.port = null;\n this.hostname = null;\n this.hash = null;\n this.search = null;\n this.query = null;\n this.pathname = null;\n this.path = null;\n this.href = null;\n}\n\n// Reference: RFC 3986, RFC 1808, RFC 2396\n\n// define these here so at least they only have to be\n// compiled once on the first module load.\nvar protocolPattern = /^([a-z0-9.+-]+:)/i,\n portPattern = /:[0-9]*$/,\n\n // Special case for a simple path URL\n simplePathPattern = /^(\\/\\/?(?!\\/)[^\\?\\s]*)(\\?[^\\s]*)?$/,\n\n // RFC 2396: characters reserved for delimiting URLs.\n // We actually just auto-escape these.\n delims = ['<', '>', '\"', '`', ' ', '\\r', '\\n', '\\t'],\n\n // RFC 2396: characters not allowed for various reasons.\n unwise = ['{', '}', '|', '\\\\', '^', '`'].concat(delims),\n\n // Allowed by RFCs, but cause of XSS attacks. Always escape these.\n autoEscape = ['\\''].concat(unwise),\n // Characters that are never ever allowed in a hostname.\n // Note that any invalid chars are also handled, but these\n // are the ones that are *expected* to be seen, so we fast-path\n // them.\n nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),\n hostEndingChars = ['/', '?', '#'],\n hostnameMaxLen = 255,\n hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,\n hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,\n // protocols that can allow \"unsafe\" and \"unwise\" chars.\n unsafeProtocol = {\n 'javascript': true,\n 'javascript:': true\n },\n // protocols that never have a hostname.\n hostlessProtocol = {\n 'javascript': true,\n 'javascript:': true\n },\n // protocols that always contain a // bit.\n slashedProtocol = {\n 'http': true,\n 'https': true,\n 'ftp': true,\n 'gopher': true,\n 'file': true,\n 'http:': true,\n 'https:': true,\n 'ftp:': true,\n 'gopher:': true,\n 'file:': true\n },\n querystring = __webpack_require__(518);\n\nfunction urlParse(url, parseQueryString, slashesDenoteHost) {\n if (url && util.isObject(url) && url instanceof Url) return url;\n\n var u = new Url;\n u.parse(url, parseQueryString, slashesDenoteHost);\n return u;\n}\n\nUrl.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {\n if (!util.isString(url)) {\n throw new TypeError(\"Parameter 'url' must be a string, not \" + typeof url);\n }\n\n // Copy chrome, IE, opera backslash-handling behavior.\n // Back slashes before the query string get converted to forward slashes\n // See: https://code.google.com/p/chromium/issues/detail?id=25916\n var queryIndex = url.indexOf('?'),\n splitter =\n (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',\n uSplit = url.split(splitter),\n slashRegex = /\\\\/g;\n uSplit[0] = uSplit[0].replace(slashRegex, '/');\n url = uSplit.join(splitter);\n\n var rest = url;\n\n // trim before proceeding.\n // This is to support parse stuff like \" http://foo.com \\n\"\n rest = rest.trim();\n\n if (!slashesDenoteHost && url.split('#').length === 1) {\n // Try fast path regexp\n var simplePath = simplePathPattern.exec(rest);\n if (simplePath) {\n this.path = rest;\n this.href = rest;\n this.pathname = simplePath[1];\n if (simplePath[2]) {\n this.search = simplePath[2];\n if (parseQueryString) {\n this.query = querystring.parse(this.search.substr(1));\n } else {\n this.query = this.search.substr(1);\n }\n } else if (parseQueryString) {\n this.search = '';\n this.query = {};\n }\n return this;\n }\n }\n\n var proto = protocolPattern.exec(rest);\n if (proto) {\n proto = proto[0];\n var lowerProto = proto.toLowerCase();\n this.protocol = lowerProto;\n rest = rest.substr(proto.length);\n }\n\n // figure out if it's got a host\n // user@server is *always* interpreted as a hostname, and url\n // resolution will treat //foo/bar as host=foo,path=bar because that's\n // how the browser resolves relative URLs.\n if (slashesDenoteHost || proto || rest.match(/^\\/\\/[^@\\/]+@[^@\\/]+/)) {\n var slashes = rest.substr(0, 2) === '//';\n if (slashes && !(proto && hostlessProtocol[proto])) {\n rest = rest.substr(2);\n this.slashes = true;\n }\n }\n\n if (!hostlessProtocol[proto] &&\n (slashes || (proto && !slashedProtocol[proto]))) {\n\n // there's a hostname.\n // the first instance of /, ?, ;, or # ends the host.\n //\n // If there is an @ in the hostname, then non-host chars *are* allowed\n // to the left of the last @ sign, unless some host-ending character\n // comes *before* the @-sign.\n // URLs are obnoxious.\n //\n // ex:\n // http://a@b@c/ => user:a@b host:c\n // http://a@b?@c => user:a host:c path:/?@c\n\n // v0.12 TODO(isaacs): This is not quite how Chrome does things.\n // Review our test case against browsers more comprehensively.\n\n // find the first instance of any hostEndingChars\n var hostEnd = -1;\n for (var i = 0; i < hostEndingChars.length; i++) {\n var hec = rest.indexOf(hostEndingChars[i]);\n if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))\n hostEnd = hec;\n }\n\n // at this point, either we have an explicit point where the\n // auth portion cannot go past, or the last @ char is the decider.\n var auth, atSign;\n if (hostEnd === -1) {\n // atSign can be anywhere.\n atSign = rest.lastIndexOf('@');\n } else {\n // atSign must be in auth portion.\n // http://a@b/c@d => host:b auth:a path:/c@d\n atSign = rest.lastIndexOf('@', hostEnd);\n }\n\n // Now we have a portion which is definitely the auth.\n // Pull that off.\n if (atSign !== -1) {\n auth = rest.slice(0, atSign);\n rest = rest.slice(atSign + 1);\n this.auth = decodeURIComponent(auth);\n }\n\n // the host is the remaining to the left of the first non-host char\n hostEnd = -1;\n for (var i = 0; i < nonHostChars.length; i++) {\n var hec = rest.indexOf(nonHostChars[i]);\n if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))\n hostEnd = hec;\n }\n // if we still have not hit it, then the entire thing is a host.\n if (hostEnd === -1)\n hostEnd = rest.length;\n\n this.host = rest.slice(0, hostEnd);\n rest = rest.slice(hostEnd);\n\n // pull out port.\n this.parseHost();\n\n // we've indicated that there is a hostname,\n // so even if it's empty, it has to be present.\n this.hostname = this.hostname || '';\n\n // if hostname begins with [ and ends with ]\n // assume that it's an IPv6 address.\n var ipv6Hostname = this.hostname[0] === '[' &&\n this.hostname[this.hostname.length - 1] === ']';\n\n // validate a little.\n if (!ipv6Hostname) {\n var hostparts = this.hostname.split(/\\./);\n for (var i = 0, l = hostparts.length; i < l; i++) {\n var part = hostparts[i];\n if (!part) continue;\n if (!part.match(hostnamePartPattern)) {\n var newpart = '';\n for (var j = 0, k = part.length; j < k; j++) {\n if (part.charCodeAt(j) > 127) {\n // we replace non-ASCII char with a temporary placeholder\n // we need this to make sure size of hostname is not\n // broken by replacing non-ASCII by nothing\n newpart += 'x';\n } else {\n newpart += part[j];\n }\n }\n // we test again with ASCII char only\n if (!newpart.match(hostnamePartPattern)) {\n var validParts = hostparts.slice(0, i);\n var notHost = hostparts.slice(i + 1);\n var bit = part.match(hostnamePartStart);\n if (bit) {\n validParts.push(bit[1]);\n notHost.unshift(bit[2]);\n }\n if (notHost.length) {\n rest = '/' + notHost.join('.') + rest;\n }\n this.hostname = validParts.join('.');\n break;\n }\n }\n }\n }\n\n if (this.hostname.length > hostnameMaxLen) {\n this.hostname = '';\n } else {\n // hostnames are always lower case.\n this.hostname = this.hostname.toLowerCase();\n }\n\n if (!ipv6Hostname) {\n // IDNA Support: Returns a punycoded representation of \"domain\".\n // It only converts parts of the domain name that\n // have non-ASCII characters, i.e. it doesn't matter if\n // you call it with a domain that already is ASCII-only.\n this.hostname = punycode.toASCII(this.hostname);\n }\n\n var p = this.port ? ':' + this.port : '';\n var h = this.hostname || '';\n this.host = h + p;\n this.href += this.host;\n\n // strip [ and ] from the hostname\n // the host field still retains them, though\n if (ipv6Hostname) {\n this.hostname = this.hostname.substr(1, this.hostname.length - 2);\n if (rest[0] !== '/') {\n rest = '/' + rest;\n }\n }\n }\n\n // now rest is set to the post-host stuff.\n // chop off any delim chars.\n if (!unsafeProtocol[lowerProto]) {\n\n // First, make 100% sure that any \"autoEscape\" chars get\n // escaped, even if encodeURIComponent doesn't think they\n // need to be.\n for (var i = 0, l = autoEscape.length; i < l; i++) {\n var ae = autoEscape[i];\n if (rest.indexOf(ae) === -1)\n continue;\n var esc = encodeURIComponent(ae);\n if (esc === ae) {\n esc = escape(ae);\n }\n rest = rest.split(ae).join(esc);\n }\n }\n\n\n // chop off from the tail first.\n var hash = rest.indexOf('#');\n if (hash !== -1) {\n // got a fragment string.\n this.hash = rest.substr(hash);\n rest = rest.slice(0, hash);\n }\n var qm = rest.indexOf('?');\n if (qm !== -1) {\n this.search = rest.substr(qm);\n this.query = rest.substr(qm + 1);\n if (parseQueryString) {\n this.query = querystring.parse(this.query);\n }\n rest = rest.slice(0, qm);\n } else if (parseQueryString) {\n // no query string, but parseQueryString still requested\n this.search = '';\n this.query = {};\n }\n if (rest) this.pathname = rest;\n if (slashedProtocol[lowerProto] &&\n this.hostname && !this.pathname) {\n this.pathname = '/';\n }\n\n //to support http.request\n if (this.pathname || this.search) {\n var p = this.pathname || '';\n var s = this.search || '';\n this.path = p + s;\n }\n\n // finally, reconstruct the href based on what has been validated.\n this.href = this.format();\n return this;\n};\n\n// format a parsed object into a url string\nfunction urlFormat(obj) {\n // ensure it's an object, and not a string url.\n // If it's an obj, this is a no-op.\n // this way, you can call url_format() on strings\n // to clean up potentially wonky urls.\n if (util.isString(obj)) obj = urlParse(obj);\n if (!(obj instanceof Url)) return Url.prototype.format.call(obj);\n return obj.format();\n}\n\nUrl.prototype.format = function() {\n var auth = this.auth || '';\n if (auth) {\n auth = encodeURIComponent(auth);\n auth = auth.replace(/%3A/i, ':');\n auth += '@';\n }\n\n var protocol = this.protocol || '',\n pathname = this.pathname || '',\n hash = this.hash || '',\n host = false,\n query = '';\n\n if (this.host) {\n host = auth + this.host;\n } else if (this.hostname) {\n host = auth + (this.hostname.indexOf(':') === -1 ?\n this.hostname :\n '[' + this.hostname + ']');\n if (this.port) {\n host += ':' + this.port;\n }\n }\n\n if (this.query &&\n util.isObject(this.query) &&\n Object.keys(this.query).length) {\n query = querystring.stringify(this.query);\n }\n\n var search = this.search || (query && ('?' + query)) || '';\n\n if (protocol && protocol.substr(-1) !== ':') protocol += ':';\n\n // only the slashedProtocols get the //. Not mailto:, xmpp:, etc.\n // unless they had them to begin with.\n if (this.slashes ||\n (!protocol || slashedProtocol[protocol]) && host !== false) {\n host = '//' + (host || '');\n if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;\n } else if (!host) {\n host = '';\n }\n\n if (hash && hash.charAt(0) !== '#') hash = '#' + hash;\n if (search && search.charAt(0) !== '?') search = '?' + search;\n\n pathname = pathname.replace(/[?#]/g, function(match) {\n return encodeURIComponent(match);\n });\n search = search.replace('#', '%23');\n\n return protocol + host + pathname + search + hash;\n};\n\nfunction urlResolve(source, relative) {\n return urlParse(source, false, true).resolve(relative);\n}\n\nUrl.prototype.resolve = function(relative) {\n return this.resolveObject(urlParse(relative, false, true)).format();\n};\n\nfunction urlResolveObject(source, relative) {\n if (!source) return relative;\n return urlParse(source, false, true).resolveObject(relative);\n}\n\nUrl.prototype.resolveObject = function(relative) {\n if (util.isString(relative)) {\n var rel = new Url();\n rel.parse(relative, false, true);\n relative = rel;\n }\n\n var result = new Url();\n var tkeys = Object.keys(this);\n for (var tk = 0; tk < tkeys.length; tk++) {\n var tkey = tkeys[tk];\n result[tkey] = this[tkey];\n }\n\n // hash is always overridden, no matter what.\n // even href=\"\" will remove it.\n result.hash = relative.hash;\n\n // if the relative url is empty, then there's nothing left to do here.\n if (relative.href === '') {\n result.href = result.format();\n return result;\n }\n\n // hrefs like //foo/bar always cut to the protocol.\n if (relative.slashes && !relative.protocol) {\n // take everything except the protocol from relative\n var rkeys = Object.keys(relative);\n for (var rk = 0; rk < rkeys.length; rk++) {\n var rkey = rkeys[rk];\n if (rkey !== 'protocol')\n result[rkey] = relative[rkey];\n }\n\n //urlParse appends trailing / to urls like http://www.example.com\n if (slashedProtocol[result.protocol] &&\n result.hostname && !result.pathname) {\n result.path = result.pathname = '/';\n }\n\n result.href = result.format();\n return result;\n }\n\n if (relative.protocol && relative.protocol !== result.protocol) {\n // if it's a known url protocol, then changing\n // the protocol does weird things\n // first, if it's not file:, then we MUST have a host,\n // and if there was a path\n // to begin with, then we MUST have a path.\n // if it is file:, then the host is dropped,\n // because that's known to be hostless.\n // anything else is assumed to be absolute.\n if (!slashedProtocol[relative.protocol]) {\n var keys = Object.keys(relative);\n for (var v = 0; v < keys.length; v++) {\n var k = keys[v];\n result[k] = relative[k];\n }\n result.href = result.format();\n return result;\n }\n\n result.protocol = relative.protocol;\n if (!relative.host && !hostlessProtocol[relative.protocol]) {\n var relPath = (relative.pathname || '').split('/');\n while (relPath.length && !(relative.host = relPath.shift()));\n if (!relative.host) relative.host = '';\n if (!relative.hostname) relative.hostname = '';\n if (relPath[0] !== '') relPath.unshift('');\n if (relPath.length < 2) relPath.unshift('');\n result.pathname = relPath.join('/');\n } else {\n result.pathname = relative.pathname;\n }\n result.search = relative.search;\n result.query = relative.query;\n result.host = relative.host || '';\n result.auth = relative.auth;\n result.hostname = relative.hostname || relative.host;\n result.port = relative.port;\n // to support http.request\n if (result.pathname || result.search) {\n var p = result.pathname || '';\n var s = result.search || '';\n result.path = p + s;\n }\n result.slashes = result.slashes || relative.slashes;\n result.href = result.format();\n return result;\n }\n\n var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),\n isRelAbs = (\n relative.host ||\n relative.pathname && relative.pathname.charAt(0) === '/'\n ),\n mustEndAbs = (isRelAbs || isSourceAbs ||\n (result.host && relative.pathname)),\n removeAllDots = mustEndAbs,\n srcPath = result.pathname && result.pathname.split('/') || [],\n relPath = relative.pathname && relative.pathname.split('/') || [],\n psychotic = result.protocol && !slashedProtocol[result.protocol];\n\n // if the url is a non-slashed url, then relative\n // links like ../.. should be able\n // to crawl up to the hostname, as well. This is strange.\n // result.protocol has already been set by now.\n // Later on, put the first path part into the host field.\n if (psychotic) {\n result.hostname = '';\n result.port = null;\n if (result.host) {\n if (srcPath[0] === '') srcPath[0] = result.host;\n else srcPath.unshift(result.host);\n }\n result.host = '';\n if (relative.protocol) {\n relative.hostname = null;\n relative.port = null;\n if (relative.host) {\n if (relPath[0] === '') relPath[0] = relative.host;\n else relPath.unshift(relative.host);\n }\n relative.host = null;\n }\n mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');\n }\n\n if (isRelAbs) {\n // it's absolute.\n result.host = (relative.host || relative.host === '') ?\n relative.host : result.host;\n result.hostname = (relative.hostname || relative.hostname === '') ?\n relative.hostname : result.hostname;\n result.search = relative.search;\n result.query = relative.query;\n srcPath = relPath;\n // fall through to the dot-handling below.\n } else if (relPath.length) {\n // it's relative\n // throw away the existing file, and take the new path instead.\n if (!srcPath) srcPath = [];\n srcPath.pop();\n srcPath = srcPath.concat(relPath);\n result.search = relative.search;\n result.query = relative.query;\n } else if (!util.isNullOrUndefined(relative.search)) {\n // just pull out the search.\n // like href='?foo'.\n // Put this after the other two cases because it simplifies the booleans\n if (psychotic) {\n result.hostname = result.host = srcPath.shift();\n //occationaly the auth can get stuck only in host\n //this especially happens in cases like\n //url.resolveObject('mailto:local1@domain1', 'local2@domain2')\n var authInHost = result.host && result.host.indexOf('@') > 0 ?\n result.host.split('@') : false;\n if (authInHost) {\n result.auth = authInHost.shift();\n result.host = result.hostname = authInHost.shift();\n }\n }\n result.search = relative.search;\n result.query = relative.query;\n //to support http.request\n if (!util.isNull(result.pathname) || !util.isNull(result.search)) {\n result.path = (result.pathname ? result.pathname : '') +\n (result.search ? result.search : '');\n }\n result.href = result.format();\n return result;\n }\n\n if (!srcPath.length) {\n // no path at all. easy.\n // we've already handled the other stuff above.\n result.pathname = null;\n //to support http.request\n if (result.search) {\n result.path = '/' + result.search;\n } else {\n result.path = null;\n }\n result.href = result.format();\n return result;\n }\n\n // if a url ENDs in . or .., then it must get a trailing slash.\n // however, if it ends in anything else non-slashy,\n // then it must NOT get a trailing slash.\n var last = srcPath.slice(-1)[0];\n var hasTrailingSlash = (\n (result.host || relative.host || srcPath.length > 1) &&\n (last === '.' || last === '..') || last === '');\n\n // strip single dots, resolve double dots to parent dir\n // if the path tries to go above the root, `up` ends up > 0\n var up = 0;\n for (var i = srcPath.length; i >= 0; i--) {\n last = srcPath[i];\n if (last === '.') {\n srcPath.splice(i, 1);\n } else if (last === '..') {\n srcPath.splice(i, 1);\n up++;\n } else if (up) {\n srcPath.splice(i, 1);\n up--;\n }\n }\n\n // if the path is allowed to go above the root, restore leading ..s\n if (!mustEndAbs && !removeAllDots) {\n for (; up--; up) {\n srcPath.unshift('..');\n }\n }\n\n if (mustEndAbs && srcPath[0] !== '' &&\n (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {\n srcPath.unshift('');\n }\n\n if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {\n srcPath.push('');\n }\n\n var isAbsolute = srcPath[0] === '' ||\n (srcPath[0] && srcPath[0].charAt(0) === '/');\n\n // put the host back\n if (psychotic) {\n result.hostname = result.host = isAbsolute ? '' :\n srcPath.length ? srcPath.shift() : '';\n //occationaly the auth can get stuck only in host\n //this especially happens in cases like\n //url.resolveObject('mailto:local1@domain1', 'local2@domain2')\n var authInHost = result.host && result.host.indexOf('@') > 0 ?\n result.host.split('@') : false;\n if (authInHost) {\n result.auth = authInHost.shift();\n result.host = result.hostname = authInHost.shift();\n }\n }\n\n mustEndAbs = mustEndAbs || (result.host && srcPath.length);\n\n if (mustEndAbs && !isAbsolute) {\n srcPath.unshift('');\n }\n\n if (!srcPath.length) {\n result.pathname = null;\n result.path = null;\n } else {\n result.pathname = srcPath.join('/');\n }\n\n //to support request.http\n if (!util.isNull(result.pathname) || !util.isNull(result.search)) {\n result.path = (result.pathname ? result.pathname : '') +\n (result.search ? result.search : '');\n }\n result.auth = relative.auth || result.auth;\n result.slashes = result.slashes || relative.slashes;\n result.href = result.format();\n return result;\n};\n\nUrl.prototype.parseHost = function() {\n var host = this.host;\n var port = portPattern.exec(host);\n if (port) {\n port = port[0];\n if (port !== ':') {\n this.port = port.substr(1);\n }\n host = host.substr(0, host.length - port.length);\n }\n if (host) this.hostname = host;\n};\n\n\n/***/ }),\n/* 238 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, no-shadow */\nconst isFunction = __webpack_require__(13);\n\nconst uuid = __webpack_require__(17);\n\nconst logging = __webpack_require__(0)('guidStorage');\n\nconst guidStorage = {};\nmodule.exports = guidStorage;\nlet currentGuidStorage;\nlet currentGuid;\n\nconst isInvalidStorage = function isInvalidStorage(storageInterface) {\n return !(isFunction(storageInterface.get) && isFunction(storageInterface.set));\n};\n\nconst getClientGuid = function getClientGuid(completion) {\n if (currentGuid) {\n completion(null, currentGuid);\n return;\n } // It's the first time that getClientGuid has been called\n // in this page lifetime. Attempt to load any existing Guid\n // from the storage\n\n\n currentGuidStorage.get(completion);\n};\n/*\n* Sets the methods for storing and retrieving client GUIDs persistently\n* across sessions. By default, OpenTok.js attempts to use browser cookies to\n* store GUIDs.\n*
\n* Pass in an object that has a get()
method and\n* a set()
method.\n*
\n* The get()
method must take one parameter: the callback\n* method to invoke. The callback method is passed two parameters —\n* the first parameter is an error object or null if the call is successful;\n* and the second parameter is the GUID (a string) if successful.\n*
\n* The set()
method must include two parameters: the GUID to set\n* (a string) and the callback method to invoke. The callback method is\n* passed an error object on error, or it is passed no parameter if the call is\n* successful.\n*
\n* Here is an example:\n*
\n*
\n* var ComplexStorage = function() {\n* this.set = function(guid, completion) {\n* AwesomeBackendService.set(guid, function(response) {\n* completion(response.error || null);\n* });\n* };\n* this.get = function(completion) {\n* AwesomeBackendService.get(function(response, guid) {\n* completion(response.error || null, guid);\n* });\n* };\n* };\n*\n* OT.overrideGuidStorage(new ComplexStorage());\n*
\n*/\n\n\nguidStorage.override = storageInterface => {\n if (isInvalidStorage(storageInterface)) {\n throw new Error('The storageInterface argument does not seem to be valid, ' + 'it must implement get and set methods');\n }\n\n if (currentGuidStorage === storageInterface) {\n return;\n }\n\n currentGuidStorage = storageInterface; // If a client Guid has already been assigned to this client then\n // let the new storage know about it so that it's in sync.\n\n if (currentGuid) {\n currentGuidStorage.set(currentGuid, error => {\n if (error) {\n logging.error(`Failed to send initial Guid value (${currentGuid}) to the newly assigned Guid Storage. The error was: ${error}`); // @todo error\n }\n });\n }\n};\n\nguidStorage.get = completion => {\n getClientGuid((error, guid) => {\n if (error) {\n completion(error);\n return;\n }\n\n if (!guid) {\n // Nothing came back, this client is entirely new.\n // generate a new Guid and persist it\n guid = uuid();\n currentGuidStorage.set(guid, error => {\n if (error) {\n completion(error);\n return;\n }\n\n currentGuid = guid;\n });\n } else if (!currentGuid) {\n currentGuid = guid;\n }\n\n completion(null, currentGuid);\n });\n}; // Implement our default storage mechanism, which sets/gets a cookie\n// called 'opentok_client_id'\n\n\nguidStorage.override({\n get(completion) {\n let clientId;\n\n try {\n clientId = (typeof window !== undefined ? window : global).localStorage.getItem('opentok_client_id');\n } catch (err) {\n // will get Uncaught DOMException: Failed to read the 'localStorage' property from 'Window':\n // The document is sandboxed and lacks the 'allow-same-origin' flag.\n if (!clientId) {\n clientId = uuid();\n }\n }\n\n completion(null, clientId);\n },\n\n set(guid, completion) {\n try {\n (typeof window !== undefined ? window : global).localStorage.setItem('opentok_client_id', guid);\n } catch (err) {// will get Uncaught DOMException: Failed to read the 'localStorage' property from 'Window':\n // The document is sandboxed and lacks the 'allow-same-origin' flag.\n }\n\n completion(null);\n }\n\n}); // Test only\n\nguidStorage.set = guid => {\n currentGuid = guid;\n};\n\n/***/ }),\n/* 239 */\n/***/ (function(module, exports) {\n\n// Unique ID creation requires a high quality random # generator. In the\n// browser this is a little complicated due to unknown quality of Math.random()\n// and inconsistent support for the `crypto` API. We do the best we can via\n// feature-detection\n\n// getRandomValues needs to be invoked in a context where \"this\" is a Crypto\n// implementation. Also, find the complete implementation of crypto on IE11.\nvar getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||\n (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));\n\nif (getRandomValues) {\n // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto\n var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef\n\n module.exports = function whatwgRNG() {\n getRandomValues(rnds8);\n return rnds8;\n };\n} else {\n // Math.random()-based (RNG)\n //\n // If all else fails, use Math.random(). It's fast, but is of unspecified\n // quality.\n var rnds = new Array(16);\n\n module.exports = function mathRNG() {\n for (var i = 0, r; i < 16; i++) {\n if ((i & 0x03) === 0) r = Math.random() * 0x100000000;\n rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;\n }\n\n return rnds;\n };\n}\n\n\n/***/ }),\n/* 240 */\n/***/ (function(module, exports) {\n\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nvar byteToHex = [];\nfor (var i = 0; i < 256; ++i) {\n byteToHex[i] = (i + 0x100).toString(16).substr(1);\n}\n\nfunction bytesToUuid(buf, offset) {\n var i = offset || 0;\n var bth = byteToHex;\n // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4\n return ([\n bth[buf[i++]], bth[buf[i++]],\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]], '-',\n bth[buf[i++]], bth[buf[i++]],\n bth[buf[i++]], bth[buf[i++]],\n bth[buf[i++]], bth[buf[i++]]\n ]).join('');\n}\n\nmodule.exports = bytesToUuid;\n\n\n/***/ }),\n/* 241 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst assign = __webpack_require__(7);\n\nconst logging = __webpack_require__(0)('analytics');\n\nconst QueueRunner = __webpack_require__(523);\n\nconst uuid = __webpack_require__(17);\n\nconst defaultAjax = __webpack_require__(242);\n\nconst _require = __webpack_require__(138),\n prependProxyToUrlIfNeeded = _require.prependProxyToUrlIfNeeded;\n\nconst _require2 = __webpack_require__(139),\n getProxyUrl = _require2.getProxyUrl;\n\nconst clientInstanceId = uuid();\nconst defaultQueue = new QueueRunner();\n\nmodule.exports = function Analytics(_ref) {\n var _this = this;\n\n let loggingUrl = _ref.loggingUrl,\n _ref$queue = _ref.queue,\n queue = _ref$queue === void 0 ? defaultQueue : _ref$queue,\n _ref$ajax = _ref.ajax,\n ajax = _ref$ajax === void 0 ? defaultAjax : _ref$ajax;\n this.loggingUrl = loggingUrl;\n\n this.updateEndPoints = () => {\n if (!this.proxyUrl) {\n this.proxyUrl = getProxyUrl();\n const prependedUrl = prependProxyToUrlIfNeeded(this.loggingUrl, this.proxyUrl);\n this.loggingUrl = prependedUrl;\n }\n\n this.endPoint = `${this.loggingUrl}/logging/ClientEvent`;\n this.endPointQos = `${this.loggingUrl}/logging/ClientQos`;\n };\n\n this.updateEndPoints();\n const reportedErrors = {};\n\n const post = function post(data, onComplete, isQos) {\n if (onComplete === void 0) {\n onComplete = () => {};\n }\n\n queue.add(callback => {\n logging.debug('sending analytics:', _this.loggingUrl, data);\n ajax.post(isQos ? _this.endPointQos : _this.endPoint, {\n body: data,\n overrideMimeType: 'text/plain',\n headers: {\n Accept: 'text/plain',\n 'Content-Type': 'application/json'\n }\n }, err => {\n if (err) {\n logging.debug('Failed to send ClientEvent, moving on to the next item.');\n }\n\n onComplete(err || undefined);\n callback();\n });\n });\n };\n\n const shouldThrottleError = (code, type, partnerId) => {\n if (!partnerId) {\n return false;\n }\n\n const errKey = [partnerId, type, code].join('_');\n const msgLimit = 100;\n return (reportedErrors[errKey] || 0) >= msgLimit;\n }; // Log an error via ClientEvents.\n //\n // @param [String] code\n // @param [String] type\n // @param [String] message\n // @param [Hash] details additional error details\n //\n // @param [Hash] options the options to log the client event with.\n // @option options [String] action The name of the Event that we are logging. E.g.\n // 'TokShowLoaded'. Required.\n // @option options [String] variation Usually used for Split A/B testing, when you\n // have multiple variations of the +_action+.\n // @option options [String] payload The payload. Required.\n // @option options [String] sessionId The active OpenTok session, if there is one\n // @option options [String] connectionId The active OpenTok connectionId, if there is one\n // @option options [String] partnerId\n // @option options [String] guid ...\n // @option options [String] streamId ...\n // @option options [String] section ...\n // @option options [String] clientVersion ...\n //\n // Reports will be throttled to X reports (see exceptionLogging.messageLimitPerPartner\n // from the dynamic config for X) of each error type for each partner. Reports can be\n // disabled/enabled globally or on a per partner basis (per partner settings\n // take precedence) using exceptionLogging.enabled.\n //\n\n\n this.logError = function (code, type, message, details, options) {\n if (details === void 0) {\n details = null;\n }\n\n if (options === void 0) {\n options = {};\n }\n\n const _options = options,\n partnerId = _options.partnerId;\n\n if (shouldThrottleError(code, type, partnerId)) {\n // OT.log('ClientEvents.error has throttled an error of type ' + type + '.' +\n // code + ' for partner ' + (partnerId || 'No Partner Id'));\n return;\n }\n\n const errKey = [partnerId, type, code].join('_');\n const payload = details;\n reportedErrors[errKey] = typeof reportedErrors[errKey] !== 'undefined' ? reportedErrors[errKey] + 1 : 1;\n\n _this.logEvent(assign(options, {\n action: `${type}.${code}`,\n payload\n }), false);\n }; // Log a client event to the analytics backend.\n //\n // @example Logs a client event called 'foo'\n // this.logEvent({\n // action: 'foo',\n // payload: 'bar',\n // sessionId: sessionId,\n // connectionId: connectionId\n // }, false)\n //\n // @param [Hash] data the data to log the client event with.\n // @param [Boolean] qos Whether this is a QoS event.\n // @param [Boolean] throttle A number specifying the ratio of events to be sent\n // out of the total number of events (other events are not ignored). If not\n // set to a number, all events are sent.\n // @param [Number] completionHandler A completion handler function to call when the\n // client event POST request succeeds or fails. If it fails, an error\n // object is passed into the function. (See throttledPost().)\n //\n\n\n this.logEvent = function (data, qos, throttle, completionHandler) {\n if (qos === void 0) {\n qos = false;\n }\n\n if (completionHandler === void 0) {\n completionHandler = () => {};\n }\n\n _this.updateEndPoints();\n\n data.clientInstanceId = clientInstanceId; // eslint-disable-line no-param-reassign\n\n if (throttle && !isNaN(throttle)) {\n if (Math.random() > throttle) {\n logging.debug('skipping sending analytics due to throttle:', data);\n return;\n }\n }\n\n logging.debug('queueing analytics:', _this.loggingUrl, data);\n let serializedData;\n\n try {\n serializedData = JSON.stringify(data);\n } catch (err) {\n completionHandler(err);\n return;\n }\n\n post(serializedData, completionHandler, qos);\n }; // Log a client QOS to the analytics backend.\n // @option options [String] action The name of the Event that we are logging.\n // E.g. 'TokShowLoaded'. Required.\n // @option options [String] variation Usually used for Split A/B testing, when\n // you have multiple variations of the +_action+.\n // @option options [String] payload The payload. Required.\n // @option options [String] sessionId The active OpenTok session, if there is one\n // @option options [String] connectionId The active OpenTok connectionId, if there is one\n // @option options [String] partnerId\n // @option options [String] guid ...\n // @option options [String] streamId ...\n // @option options [String] section ...\n // @option options [String] clientVersion ...\n //\n\n\n this.logQOS = options => this.logEvent(options, true);\n};\n\n/***/ }),\n/* 242 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-void, no-param-reassign */\n\n/* eslint-disable no-restricted-syntax, no-continue */\nconst $ = __webpack_require__(243);\n\nconst env = __webpack_require__(2);\n\nconst makeEverythingAttachToOTHelpers = __webpack_require__(245);\n\nconst assign = __webpack_require__(7);\n\nconst browserAjax = {};\nmodule.exports = browserAjax;\n\nfunction formatPostData(data) {\n // , contentType\n // If it's a string, we assume it's properly encoded\n if (typeof data === 'string') {\n return data;\n }\n\n const queryString = [];\n Object.keys(data).forEach(key => {\n queryString.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);\n });\n return queryString.join('&').replace(/\\+/g, '%20');\n}\n\nif (env.name !== 'Node') {\n browserAjax.request = function (url, options, callback) {\n const request = new (typeof window !== undefined ? window : global).XMLHttpRequest();\n\n const _options = options || {};\n\n const _method = _options.method;\n\n if (!_method) {\n callback(new Error('No HTTP method specified in options'));\n return;\n }\n\n if (options.overrideMimeType) {\n if (request.overrideMimeType) {\n request.overrideMimeType(options.overrideMimeType);\n }\n\n delete options.overrideMimeType;\n } // Setup callbacks to correctly respond to success and error callbacks. This includes\n // interpreting the responses HTTP status, which XmlHttpRequest seems to ignore\n // by default.\n\n\n if (callback) {\n $(request).on('load', event => {\n const status = event.target.status; // We need to detect things that XMLHttpRequest considers a success,\n // but we consider to be failures.\n\n if (status >= 200 && (status < 300 || status === 304)) {\n callback(null, event);\n } else {\n callback(event);\n }\n });\n $(request).on('error', callback);\n }\n\n request.open(options.method, url, true);\n\n if (!_options.headers) {\n _options.headers = {};\n }\n\n for (const name in _options.headers) {\n if (!Object.prototype.hasOwnProperty.call(_options.headers, name)) {\n continue;\n }\n\n request.setRequestHeader(name, _options.headers[name]);\n }\n\n request.send(options.body && formatPostData(options.body));\n };\n\n browserAjax.get = function (url, options, callback) {\n const _options = assign(options || {}, {\n method: 'GET'\n });\n\n browserAjax.request(url, _options, callback);\n };\n\n browserAjax.post = function (url, options, callback) {\n const _options = assign(options || {}, {\n method: 'POST'\n });\n\n browserAjax.request(url, _options, callback);\n };\n\n browserAjax.getJSON = function (url, options, callback) {\n options = options || {};\n\n const done = function done(error, event) {\n if (error) {\n callback(error, event && event.target && event.target.responseText);\n } else {\n let response;\n\n try {\n response = JSON.parse(event.target.responseText);\n } catch (e) {\n // Badly formed JSON\n callback(e, event && event.target && event.target.responseText);\n return;\n }\n\n callback(null, response, event);\n }\n };\n\n const extendedHeaders = assign({\n Accept: 'application/json'\n }, options.headers || {});\n browserAjax.get(url, assign(options || {}, {\n headers: extendedHeaders\n }), done);\n };\n\n makeEverythingAttachToOTHelpers(browserAjax);\n}\n\n/***/ }),\n/* 243 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst ElementCollection = __webpack_require__(244);\n\nmodule.exports = function (selector, context) {\n return new ElementCollection(selector, context);\n};\n\n/***/ }),\n/* 244 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable global-require, no-param-reassign, no-void */\n\n/* eslint-disable no-underscore-dangle, prefer-rest-params, prefer-const */\nconst isFunction = __webpack_require__(13);\n\nconst extensions = {\n attributes: __webpack_require__(524),\n css: __webpack_require__(525),\n classNames: __webpack_require__(526),\n observers: __webpack_require__(528)\n};\nconst idRegex = /^#([\\w-]*)$/; // A helper for converting a NodeList to a JS Array\n\nconst nodeListToArray = function nodeListToArray(nodes) {\n return Array.prototype.slice.call(nodes);\n};\n\nconst selectorToElementArray = function selectorToElementArray(selector, context) {\n let elements;\n\n if (typeof selector === 'undefined') {\n return [];\n }\n\n if (typeof selector === 'string') {\n elements = [];\n const idSelector = idRegex.exec(selector);\n context = context || document;\n\n if (idSelector && idSelector[1]) {\n const element = context.getElementById(idSelector[1]);\n\n if (element) {\n elements.push(element);\n }\n } else {\n elements = context.querySelectorAll(selector);\n }\n } 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))) {\n // allow OTHelpers(DOMNode) and OTHelpers(xmlHttpRequest)\n elements = [selector];\n context = selector;\n } else if (Array.isArray(selector)) {\n elements = selector.slice();\n context = null;\n } else {\n elements = nodeListToArray(elements);\n }\n\n return elements;\n}; // ElementCollection contains the result of calling OTHelpers.\n//\n// It has the following properties:\n// length\n// first\n// last\n//\n// It also has a get method that can be used to access elements in the collection\n//\n// var videos = OTHelpers('video');\n// var firstElement = videos.get(0); // identical to videos.first\n// var lastElement = videos.get(videos.length-1); // identical to videos.last\n// var allVideos = videos.get();\n//\n//\n// The collection also implements the following helper methods:\n// some, forEach, map, filter, find,\n// appendTo, after, before, remove, empty,\n// attr, center, width, height,\n// addClass, removeClass, hasClass, toggleClass,\n// on, off, once,\n// observeStyleChanges, observeNodeOrChildNodeRemoval\n//\n// Mostly the usage should be obvious. When in doubt, assume it functions like\n// the jQuery equivalent.\n//\n\n\nconst ElementCollection = function ElementCollection(selector, context) {\n const elements = selectorToElementArray(selector, context);\n this.context = context;\n\n this.toArray = function () {\n return elements;\n };\n\n this.length = elements.length;\n this.first = elements[0];\n this.last = elements[elements.length - 1];\n\n this.get = function (index) {\n if (index === void 0) {\n return elements;\n }\n\n return elements[index];\n };\n};\n\nmodule.exports = ElementCollection;\nElementCollection._attachToOTHelpers = {}; // @remove\n\nElementCollection._attachToOTHelpers.removeElement = function (element) {\n return new ElementCollection(element).remove();\n};\n\nElementCollection.prototype.getAsArray = function () {\n let _collection = this.get();\n\n if (!isFunction(_collection.forEach)) {\n // It's possibly something Array-ish that isn't quite an\n // Array. Something like arguments or a NodeList\n _collection = nodeListToArray(_collection);\n }\n\n return _collection;\n};\n\nElementCollection.prototype.some = function (iter, context) {\n return this.getAsArray().some(iter, context);\n};\n\nElementCollection.prototype.forEach = function (fn, context) {\n this.getAsArray().forEach(fn, context);\n return this;\n};\n\nElementCollection.prototype.map = function (fn, context) {\n return new ElementCollection(this.getAsArray().map(fn, context), this.context);\n};\n\nElementCollection.prototype.filter = function (fn, context) {\n return new ElementCollection(this.getAsArray().filter(fn, context), this.context);\n};\n\nElementCollection.prototype.find = function (selector) {\n return new ElementCollection(selector, this.first);\n}; // Helper function for adding event listeners to dom elements.\n// WARNING: This doesn't preserve event types, your handler could\n// be getting all kinds of different parameters depending on the browser.\n// You also may have different scopes depending on the browser and bubbling\n// and cancelable are not supported.\n\n\nElementCollection.prototype.on = function (eventName, handler) {\n return this.forEach(element => {\n element.addEventListener(eventName, handler, false);\n });\n}; // Helper function for removing event listeners from dom elements.\n\n\nElementCollection.prototype.off = function (eventName, handler) {\n return this.forEach(element => {\n element.removeEventListener(eventName, handler, false);\n });\n};\n\nElementCollection.prototype.once = function (eventName, handler) {\n const removeAfterTrigger = function () {\n this.off(eventName, removeAfterTrigger);\n handler(...arguments);\n }.bind(this);\n\n return this.on(eventName, removeAfterTrigger);\n};\n\nElementCollection.prototype.appendTo = function (parentElement) {\n if (!parentElement) {\n throw new Error('appendTo requires a DOMElement to append to.');\n }\n\n return this.forEach(child => {\n parentElement.appendChild(child);\n });\n};\n\nElementCollection.prototype.append = function () {\n const parentElement = this.first;\n\n if (!parentElement) {\n return this;\n }\n\n Array.prototype.forEach.call(arguments, child => {\n parentElement.appendChild(child);\n });\n return this;\n};\n\nElementCollection.prototype.prepend = function () {\n if (arguments.length === 0) {\n return this;\n }\n\n const parentElement = this.first;\n let elementsToPrepend;\n\n if (!parentElement) {\n return this;\n }\n\n elementsToPrepend = Array.prototype.slice.call(arguments);\n\n if (!parentElement.firstElementChild) {\n parentElement.appendChild(elementsToPrepend.shift());\n }\n\n elementsToPrepend.forEach(element => {\n parentElement.insertBefore(element, parentElement.firstElementChild);\n });\n return this;\n};\n\nElementCollection.prototype.after = function (prevElement) {\n if (!prevElement) {\n throw new Error('after requires a DOMElement to insert after');\n }\n\n return this.forEach(element => {\n if (element.parentElement) {\n if (prevElement !== element.parentNode.lastChild) {\n element.parentElement.insertBefore(element, prevElement);\n } else {\n element.parentElement.appendChild(element);\n }\n }\n });\n};\n\nElementCollection.prototype.before = function (nextElement) {\n if (!nextElement) {\n throw new Error('before requires a DOMElement to insert before');\n }\n\n return this.forEach(element => {\n if (element.parentElement) {\n element.parentElement.insertBefore(element, nextElement);\n }\n });\n};\n\nElementCollection.prototype.remove = function () {\n return this.forEach(element => {\n if (element.parentNode) {\n element.parentNode.removeChild(element);\n }\n });\n};\n\nElementCollection.prototype.empty = function () {\n return this.forEach(element => {\n // elements is a \"live\" NodesList collection. Meaning that the collection\n // itself will be mutated as we remove elements from the DOM. This means\n // that \"while there are still elements\" is safer than \"iterate over each\n // element\" as the collection length and the elements indices will be modified\n // with each iteration.\n while (element.firstChild) {\n element.removeChild(element.firstChild);\n }\n });\n}; // Detects when an element is not part of the document flow because\n// it or one of it's ancesters has display:none.\n\n\nElementCollection.prototype.isDisplayNone = function () {\n return this.some(element => {\n if ((element.offsetWidth === 0 || element.offsetHeight === 0) && new ElementCollection(element).css('display') === 'none') {\n return true;\n }\n\n if (element.parentNode && element.parentNode.style) {\n return new ElementCollection(element.parentNode).isDisplayNone();\n }\n\n return false;\n });\n};\n\nconst findElementWithDisplayNone = function findElementWithDisplayNone(element) {\n if ((element.offsetWidth === 0 || element.offsetHeight === 0) && new ElementCollection(element).css('display') === 'none') {\n return element;\n }\n\n if (element.parentNode && element.parentNode.style) {\n return findElementWithDisplayNone(element.parentNode);\n }\n\n return null;\n}; // @remove\n\n\nElementCollection._attachToOTHelpers.emptyElement = function (element) {\n return new ElementCollection(element).empty();\n};\n\nElementCollection._attachToOTHelpers._findElementWithDisplayNone = findElementWithDisplayNone;\nextensions.css(ElementCollection, findElementWithDisplayNone);\nextensions.attributes(ElementCollection);\nextensions.classNames(ElementCollection);\nextensions.observers(ElementCollection); // TODO: Deprecation logging?\n// @remove\n\n['on', 'off', 'isDisplayNone', 'show', 'hide', 'css', 'makeVisibleAndYield', 'addClass', 'removeClass'].forEach(methodName => {\n ElementCollection._attachToOTHelpers[methodName] = function ()\n /* arguments */\n {\n const args = Array.prototype.slice.apply(arguments);\n const element = args.shift();\n const collection = new ElementCollection(element);\n return ElementCollection.prototype[methodName].apply(collection, args);\n };\n});\n\n/***/ }),\n/* 245 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-param-reassign */\nmodule.exports = function (mod) {\n const attachments = {};\n Object.keys(mod).forEach(key => {\n attachments[key] = mod[key];\n });\n mod._attachToOTHelpers = attachments;\n};\n\n/***/ }),\n/* 246 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-void, no-restricted-syntax, no-prototype-builtins */\n\n/* eslint-disable no-continue, prefer-spread, prefer-rest-params */\nconst isFunction = __webpack_require__(13);\n\nconst eventing = __webpack_require__(5);\n\nconst logging = __webpack_require__(0)('Collection');\n\nmodule.exports = function Collection(idField) {\n let _models = [];\n let _byId = {};\n\n const _idField = idField || 'id';\n\n eventing(this);\n\n const modelProperty = function modelProperty(model, property) {\n if (isFunction(model[property])) {\n return model[property]();\n }\n\n return model[property];\n };\n\n const onModelUpdate = function onModelUpdate(event) {\n this.trigger('update', event);\n this.trigger(`update:${event.target.id}`, event);\n }.bind(this);\n\n const onModelDestroy = function onModelDestroyed(event) {\n this.remove(event.target, event.reason);\n }.bind(this);\n\n this.reset = function () {\n // Stop listening on the models, they are no longer our problem\n _models.forEach(function (model) {\n model.off('updated', onModelUpdate, this);\n model.off('destroyed', onModelDestroy, this);\n }, this);\n\n _models = [];\n _byId = {};\n };\n\n this.destroy = function (reason) {\n _models.forEach(model => {\n if (model && typeof model.destroy === 'function') {\n model.destroy(reason, true);\n }\n });\n\n this.reset();\n this.off();\n };\n\n this.get = function (id) {\n return id && _byId[id] !== void 0 ? _models[_byId[id]] : void 0;\n };\n\n this.has = function (id) {\n return id && _byId[id] !== void 0;\n };\n\n this.toString = function () {\n return _models.toString();\n }; // Return only models filtered by either a dict of properties\n // or a filter function.\n //\n // @example Return all publishers with a streamId of 1\n // OT.publishers.where({streamId: 1})\n //\n // @example The same thing but filtering using a filter function\n // OT.publishers.where(function(publisher) {\n // return publisher.stream.id === 4;\n // });\n //\n // @example The same thing but filtering using a filter function\n // executed with a specific this\n // OT.publishers.where(function(publisher) {\n // return publisher.stream.id === 4;\n // }, self);\n //\n\n\n this.where = function (attrsOrFilterFn, context) {\n if (isFunction(attrsOrFilterFn)) {\n return _models.filter(attrsOrFilterFn, context);\n }\n\n return _models.filter(model => {\n for (const key in attrsOrFilterFn) {\n if (!attrsOrFilterFn.hasOwnProperty(key)) {\n continue;\n }\n\n if (modelProperty(model, key) !== attrsOrFilterFn[key]) {\n return false;\n }\n }\n\n return true;\n });\n }; // Similar to where in behaviour, except that it only returns\n // the first match.\n\n\n this.find = function (attrsOrFilterFn, context) {\n let filterFn;\n\n if (isFunction(attrsOrFilterFn)) {\n filterFn = attrsOrFilterFn;\n } else {\n filterFn = function filterFn(model) {\n for (const key in attrsOrFilterFn) {\n if (!attrsOrFilterFn.hasOwnProperty(key)) {\n continue;\n }\n\n if (modelProperty(model, key) !== attrsOrFilterFn[key]) {\n return false;\n }\n }\n\n return true;\n };\n }\n\n filterFn = filterFn.bind(context);\n\n for (let i = 0; i < _models.length; ++i) {\n if (filterFn(_models[i]) === true) {\n return _models[i];\n }\n }\n\n return null;\n };\n\n this.forEach = function (fn, context) {\n _models.forEach(fn, context);\n\n return this;\n };\n\n this.map = function (fn) {\n return _models.map(fn);\n };\n\n this.add = function (model) {\n const id = modelProperty(model, _idField);\n\n if (this.has(id)) {\n logging.warn(`Model ${id} is already in the collection`, _models);\n return this;\n }\n\n _byId[id] = _models.push(model) - 1;\n model.on('updated', onModelUpdate, this);\n model.on('destroyed', onModelDestroy, this);\n this.trigger('add', model);\n this.trigger(`add:${id}`, model);\n return this;\n };\n\n this.remove = function (model, reason) {\n const id = modelProperty(model, _idField);\n\n _models.splice(_byId[id], 1); // Shuffle everyone down one\n\n\n for (let i = _byId[id]; i < _models.length; ++i) {\n _byId[modelProperty(_models[i], _idField)] = i;\n }\n\n delete _byId[id];\n model.off('updated', onModelUpdate, this);\n model.off('destroyed', onModelDestroy, this);\n this.trigger('remove', model, reason);\n this.trigger(`remove:${id}`, model, reason);\n return this;\n }; // Retrigger the add event behaviour for each model. You can also\n // select a subset of models to trigger using the same arguments\n // as the #where method.\n\n\n this._triggerAddEvents = function () {\n this.where.apply(this, arguments).forEach(function (model) {\n this.trigger('add', model);\n this.trigger(`add:${modelProperty(model, _idField)}`, model);\n }, this);\n };\n\n this.length = function () {\n return _models.length;\n };\n};\n\n/***/ }),\n/* 247 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst logging = __webpack_require__(0)('deprecation');\n\nmodule.exports = function setDeprecatedProperty(object, key, _temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n defaultValue = _ref.value,\n _ref$name = _ref.name,\n name = _ref$name === void 0 ? `${key}` : _ref$name,\n _ref$getWarning = _ref.getWarning,\n getWarning = _ref$getWarning === void 0 ? '' : _ref$getWarning,\n _ref$setWarning = _ref.setWarning,\n setWarning = _ref$setWarning === void 0 ? '' : _ref$setWarning,\n _ref$canSet = _ref.canSet,\n canSet = _ref$canSet === void 0 ? false : _ref$canSet,\n _ref$warnOnSet = _ref.warnOnSet,\n warnOnSet = _ref$warnOnSet === void 0 ? false : _ref$warnOnSet;\n\n let value = defaultValue;\n let hasWarnedOnGet = false;\n let hasWarnedOnSet = !warnOnSet;\n const propertyDescriptor = {\n get() {\n if (!hasWarnedOnGet) {\n hasWarnedOnGet = true;\n logging.warn(`${name} is deprecated, and will be removed in the future. ${getWarning}`.trim());\n }\n\n return value;\n },\n\n set(newValue) {\n if (canSet) {\n value = newValue;\n }\n\n if (!hasWarnedOnSet) {\n hasWarnedOnSet = true;\n\n if (canSet) {\n logging.warn(`${name} is deprecated. ${setWarning}`.trim());\n } else {\n logging.warn(`${name} is deprecated, and will be removed in the future. ${setWarning}`.trim());\n }\n }\n }\n\n };\n Object.defineProperty(object, key, propertyDescriptor);\n};\n\n/***/ }),\n/* 248 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-param-reassign, no-void */\n\n/* eslint-disable no-restricted-syntax, no-prototype-builtins, no-shadow, one-var, vars-on-top */\n\n/* eslint-disable no-var */\nconst env = __webpack_require__(2);\n\nconst isObject = __webpack_require__(8);\n\nlet getErrorLocation; // Properties that we'll acknowledge from the JS Error object\n\nconst safeErrorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; // OTHelpers.Error\n//\n// A construct to contain error information that also helps with extracting error\n// context, such as stack trace.\n//\n// @constructor\n// @memberof OTHelpers\n// @method Error\n//\n// @param {String} message\n// Optional. The error message\n//\n// @param {Object} props\n// Optional. A dictionary of properties containing extra Error info.\n//\n//\n// @example Create a simple error with juts a custom message\n// var error = new OTHelpers.Error('Something Broke!');\n// error.message === 'Something Broke!';\n//\n// @example Create an Error with a message and a name\n// var error = new OTHelpers.Error('Something Broke!', 'FooError');\n// error.message === 'Something Broke!';\n// error.name === 'FooError';\n//\n// @example Create an Error with a message, name, and custom properties\n// var error = new OTHelpers.Error('Something Broke!', 'FooError', {\n// foo: 'bar',\n// listOfImportantThings: [1,2,3,4]\n// });\n// error.message === 'Something Broke!';\n// error.name === 'FooError';\n// error.foo === 'bar';\n// error.listOfImportantThings == [1,2,3,4];\n//\n// @example Create an Error from a Javascript Error\n// var error = new OTHelpers.Error(domSyntaxError);\n// error.message === domSyntaxError.message;\n// error.name === domSyntaxError.name === 'SyntaxError';\n// // ...continues for each properties of domSyntaxError\n//\n// @references\n// * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi\n// * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack\n// * http://www.w3.org/TR/dom/#interface-domerror\n//\n//\n// @todo\n// * update usage in OTMedia\n// * replace error handling in OT.js\n// * normalise stack behaviour under Chrome/Node/Safari with other browsers\n// * unit test for stack parsing\n//\n\nconst Error_ = function Error_(message, name, props) {\n switch (arguments.length) {\n case 1:\n if (isObject(message)) {\n props = message;\n name = void 0;\n message = void 0;\n } // Otherwise it's the message\n\n\n break;\n\n case 2:\n if (isObject(name)) {\n props = name;\n name = void 0;\n } // Otherwise name is actually the name\n\n\n break;\n\n default: // FIXME: Should something go here?\n\n }\n\n if (props instanceof Error) {\n // Special handling of this due to Chrome weirdness. It seems that\n // properties of the Error object, and it's children, are not\n // enumerable in Chrome?\n for (let i = 0, num = safeErrorProps.length; i < num; ++i) {\n this[safeErrorProps[i]] = props[safeErrorProps[i]];\n }\n } else if (isObject(props)) {\n // Use an custom properties that are provided\n for (const key in props) {\n if (props.hasOwnProperty(key)) {\n this[key] = props[key];\n }\n }\n } // If any of the fundamental properties are missing then try and\n // extract them.\n\n\n if (!(this.fileName && this.lineNumber && this.columnNumber && this.stack)) {\n const err = getErrorLocation();\n\n if (!this.fileName && err.fileName) {\n this.fileName = err.fileName;\n }\n\n if (!this.lineNumber && err.lineNumber) {\n this.lineNumber = err.lineNumber;\n }\n\n if (!this.columnNumber && err.columnNumber) {\n this.columnNumber = err.columnNumber;\n }\n\n if (!this.stack && err.stack) {\n this.stack = err.stack;\n }\n }\n\n if (!this.message && message) {\n this.message = message;\n }\n\n if ((!this.name || this.name === 'Error') && name) {\n this.name = name;\n }\n};\n\nmodule.exports = Error_;\nError_.prototype = Object.create(Error.prototype);\n\nError_.prototype.toString = function () {\n let locationDetails = '';\n\n if (this.fileName) {\n locationDetails += ` ${this.fileName}`;\n }\n\n if (this.lineNumber) {\n locationDetails += ` ${this.lineNumber}`;\n\n if (this.columnNumber) {\n locationDetails += `:${this.columnNumber}`;\n }\n }\n\n return `<${this.name ? `${this.name} ` : ''}${this.message}${locationDetails}>`;\n};\n\nError_.prototype.valueOf = Error_.prototype.toString; // Normalise err.stack so that it is the same format as the other browsers\n// We skip the first two frames so that we don't capture getErrorLocation() and\n// the callee.\n//\n// Used by Environments that support the StackTrace API. (Chrome, Node, Opera)\n//\n\nconst prepareStackTrace = function prepareStackTrace(_, stack) {\n return stack.slice(2).map(frame => {\n const _f = {\n fileName: frame.getFileName(),\n linenumber: frame.getLineNumber(),\n columnNumber: frame.getColumnNumber()\n };\n\n if (frame.getFunctionName()) {\n _f.functionName = frame.getFunctionName();\n }\n\n if (frame.getMethodName()) {\n _f.methodName = frame.getMethodName();\n }\n\n if (frame.getThis()) {\n _f.self = frame.getThis();\n }\n\n return _f;\n });\n}; // Black magic to retrieve error location info for various environments\n\n\ngetErrorLocation = function getErrorLocation() {\n const info = {};\n let callstack, errLocation, err;\n\n switch (env.name) {\n case 'Firefox':\n case 'Safari':\n try {\n (typeof window !== undefined ? window : global).call.js.is.explody();\n } catch (e) {\n err = e;\n }\n\n callstack = (err.stack || '').split('\\n'); // Remove call to getErrorLocation() and the callee\n\n callstack.shift();\n callstack.shift();\n info.stack = callstack;\n errLocation = /@(.+?):([0-9]+)(:([0-9]+))?$/.exec(callstack[0]);\n\n if (errLocation) {\n info.fileName = errLocation[1];\n info.lineNumber = parseInt(errLocation[2], 10);\n\n if (errLocation.length > 3) {\n info.columnNumber = parseInt(errLocation[4], 10);\n }\n }\n\n break;\n\n case 'Chrome':\n case 'Node':\n case 'Opera':\n var currentPST = Error.prepareStackTrace;\n Error.prepareStackTrace = prepareStackTrace;\n err = new Error();\n info.stack = err.stack;\n Error.prepareStackTrace = currentPST;\n var topFrame = info.stack[0];\n info.lineNumber = topFrame.lineNumber;\n info.columnNumber = topFrame.columnNumber;\n info.fileName = topFrame.fileName;\n\n if (topFrame.functionName) {\n info.functionName = topFrame.functionName;\n }\n\n if (topFrame.methodName) {\n info.methodName = topFrame.methodName;\n }\n\n if (topFrame.self) {\n info.self = topFrame.self;\n }\n\n break;\n\n default:\n err = new Error();\n\n if (err.stack) {\n info.stack = err.stack.split('\\n');\n }\n\n break;\n }\n\n if (err.message) {\n info.message = err.message;\n }\n\n return info;\n};\n\n/***/ }),\n/* 249 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* eslint-env node */\n\n\n// SDP helpers.\nvar SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(function(line) {\n return line.trim();\n });\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n var parts = blob.split('\\nm=');\n return parts.map(function(part, index) {\n return (index > 0 ? 'm=' + part : part).trim() + '\\r\\n';\n });\n};\n\n// returns the session description.\nSDPUtils.getDescription = function(blob) {\n var sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n var sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(function(line) {\n return line.indexOf(prefix) === 0;\n });\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\nSDPUtils.parseCandidate = function(line) {\n var parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n var candidate = {\n foundation: parts[0],\n component: parseInt(parts[1], 10),\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7]\n };\n\n for (var i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compability.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag\n candidate[parts[i]] = parts[i + 1];\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\nSDPUtils.writeCandidate = function(candidate) {\n var sdp = [];\n sdp.push(candidate.foundation);\n sdp.push(candidate.component);\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n var type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substr(14).split(' ');\n};\n\n// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n var parts = line.substr(9).split(' ');\n var parsed = {\n payloadType: parseInt(parts.shift(), 10) // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generate an a=rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n var pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n var channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses an a=extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n var parts = line.substr(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1]\n };\n};\n\n// Generates a=extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses an ftmp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n var parsed = {};\n var kv;\n var parts = line.substr(line.indexOf(' ') + 1).split(';');\n for (var j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n var line = '';\n var pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n var params = [];\n Object.keys(codec.parameters).forEach(function(param) {\n if (codec.parameters[param]) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n var parts = line.substr(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' ')\n };\n};\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n var lines = '';\n var pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(function(fb) {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses an RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n var sp = line.indexOf(' ');\n var parts = {\n ssrc: parseInt(line.substr(7, sp - 7), 10)\n };\n var colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substr(sp + 1, colon - sp - 1);\n parts.value = line.substr(colon + 1);\n } else {\n parts.attribute = line.substr(sp + 1);\n }\n return parts;\n};\n\nSDPUtils.parseSsrcGroup = function(line) {\n var parts = line.substr(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(function(ssrc) {\n return parseInt(ssrc, 10);\n })\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substr(6);\n }\n};\n\nSDPUtils.parseFingerprint = function(line) {\n var parts = line.substr(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1]\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role.\n // Note2: 'algorithm' is not case sensitive except in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint)\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n var sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(function(fp) {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n var parts = line.substr(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n var parts = keyParams.substr(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES paramters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substr(12),\n password: pwd.substr(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n return 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n var description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: []\n };\n var lines = SDPUtils.splitLines(mediaSection);\n var mline = lines[0].split(' ');\n for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n var pt = mline[i];\n var rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n var codec = SDPUtils.parseRtpMap(rtpmapline);\n var fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n var sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' UDP/TLS/RTP/SAVPF ';\n sdp += caps.codecs.map(function(codec) {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(function(codec) {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n var maxptime = 0;\n caps.codecs.forEach(function(codec) {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n sdp += 'a=rtcp-mux\\r\\n';\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(function(extension) {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n var encodingParameters = [];\n var description = SDPUtils.parseRtpParameters(mediaSection);\n var hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(function(line) {\n return SDPUtils.parseSsrcMedia(line);\n })\n .filter(function(parts) {\n return parts.attribute === 'cname';\n });\n var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n var secondarySsrc;\n\n var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(function(line) {\n var parts = line.substr(17).split(' ');\n return parts.map(function(part) {\n return parseInt(part, 10);\n });\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(function(codec) {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n var encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10)\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red'\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substr(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(function(params) {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n var rtcpParameters = {};\n\n // Gets the first SSRC. Note tha with RTX there might be multiple\n // SSRCs.\n var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(function(line) {\n return SDPUtils.parseSsrcMedia(line);\n })\n .filter(function(obj) {\n return obj.attribute === 'cname';\n })[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n var parts;\n var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substr(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(function(line) {\n return SDPUtils.parseSsrcMedia(line);\n })\n .filter(function(msidParts) {\n return msidParts.attribute === 'msid';\n });\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n var mline = SDPUtils.parseMLine(mediaSection);\n var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n var maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substr(12), 10),\n protocol: mline.fmt,\n maxMessageSize: maxMessageSize\n };\n }\n var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0]\n .substr(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize: maxMessageSize\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n var output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n'\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n'\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 21);\n};\n\n// Write boilder plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n var sessionId;\n var version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n var user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\nSDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {\n var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n // Map ICE parameters (ufrag, pwd) to SDP.\n sdp += SDPUtils.writeIceParameters(\n transceiver.iceGatherer.getLocalParameters());\n\n // Map DTLS parameters to SDP.\n sdp += SDPUtils.writeDtlsParameters(\n transceiver.dtlsTransport.getLocalParameters(),\n type === 'offer' ? 'actpass' : 'active');\n\n sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n if (transceiver.direction) {\n sdp += 'a=' + transceiver.direction + '\\r\\n';\n } else if (transceiver.rtpSender && transceiver.rtpReceiver) {\n sdp += 'a=sendrecv\\r\\n';\n } else if (transceiver.rtpSender) {\n sdp += 'a=sendonly\\r\\n';\n } else if (transceiver.rtpReceiver) {\n sdp += 'a=recvonly\\r\\n';\n } else {\n sdp += 'a=inactive\\r\\n';\n }\n\n if (transceiver.rtpSender) {\n // spec.\n var msid = 'msid:' + stream.id + ' ' +\n transceiver.rtpSender.track.id + '\\r\\n';\n sdp += 'a=' + msid;\n\n // for Chrome.\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n ' ' + msid;\n if (transceiver.sendEncodingParameters[0].rtx) {\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n ' ' + msid;\n sdp += 'a=ssrc-group:FID ' +\n transceiver.sendEncodingParameters[0].ssrc + ' ' +\n transceiver.sendEncodingParameters[0].rtx.ssrc +\n '\\r\\n';\n }\n }\n // FIXME: this should be written by writeRtpDescription.\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n ' cname:' + SDPUtils.localCName + '\\r\\n';\n if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n ' cname:' + SDPUtils.localCName + '\\r\\n';\n }\n return sdp;\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n var lines = SDPUtils.splitLines(mediaSection);\n for (var i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substr(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n var lines = SDPUtils.splitLines(mediaSection);\n var mline = lines[0].split(' ');\n return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n var lines = SDPUtils.splitLines(mediaSection);\n var parts = lines[0].substr(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' ')\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n var parts = line.substr(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5]\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n var lines = SDPUtils.splitLines(blob);\n for (var i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (true) {\n module.exports = SDPUtils;\n}\n\n\n/***/ }),\n/* 250 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function calculateCapableSimulcastStreams(opt) {\n const supportedBrowsers = ['Chrome', 'Safari'];\n\n if (supportedBrowsers.indexOf(opt.browserName) === -1 || opt.isScreenSharing || opt.sessionInfo.p2pEnabled || !opt.constraints.video && !opt.isCustomVideoTrack) {\n // We only support simulcast on Chrome, and when not using\n // screensharing.\n return 1;\n } // HD and above gets three streams. Otherwise they get 2.\n\n\n if (opt.videoDimensions.width > 640 && opt.videoDimensions.height > 480) {\n return 3;\n }\n\n return 2;\n};\n\n/***/ }),\n/* 251 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _defineProperty2 = _interopRequireDefault(__webpack_require__(16));\n\nvar _temp;\n\n/* eslint-disable no-underscore-dangle */\nconst now = __webpack_require__(47);\n/*\n * A RTCPeerConnection.getStats
based audio level sampler.\n *\n * It uses the getStats
method to get the audioOutputLevel
.\n * This implementation expects the single parameter version of the getStats
method.\n *\n * Currently the audioOutputLevel
stats is only supported in Chrome.\n *\n * @param {function} \"getStats\" function or \"getSynchronizationSources\" function\n * @constructor\n */\n\n\nmodule.exports = (_temp = /*#__PURE__*/function () {\n function GetAudioLevelSampler(getAudioLevel, _temp2) {\n var _context;\n\n let _ref = _temp2 === void 0 ? {} : _temp2,\n _ref$requestAnimation = _ref.requestAnimationFrame,\n requestAnimationFrame = _ref$requestAnimation === void 0 ? (_context = window).requestAnimationFrame.bind(_context) : _ref$requestAnimation;\n\n (0, _defineProperty2.default)(this, \"_lastCalled\", now());\n (0, _defineProperty2.default)(this, \"_audioLevel\", undefined);\n (0, _defineProperty2.default)(this, \"_running\", false);\n this._requestAnimationFrame = requestAnimationFrame;\n this._getAudioLevel = getAudioLevel;\n\n this._startLoop();\n }\n\n var _proto = GetAudioLevelSampler.prototype;\n\n _proto._startLoop = function _startLoop() {\n if (this._running) {\n return;\n }\n\n this._running = true;\n\n this._requestAnimationFrame(this._loop.bind(this));\n };\n\n _proto._loop = function _loop() {\n this._getAudioLevel((error, stats) => {\n // @todo we should measure how long the getStats so that we can determine\n // our impact on the render loop, and skip frames if need to reduce our\n // CPU utilization\n // Stat entries without audio can have audioLevel 0 so we just look for the max level\n let maxLevel = null;\n\n if (!error) {\n for (let idx = 0; idx < stats.length; idx += 1) {\n const stat = stats[idx];\n let level = null;\n\n if (stat.audioOutputLevel !== undefined) {\n // the max value delivered by getStats via audioOutputLevel is 2^15\n const audioOutputLevel = parseFloat(stat.audioOutputLevel) / 32768;\n\n if (!isNaN(audioOutputLevel)) {\n level = audioOutputLevel;\n }\n } else if (stat.audioLevel !== undefined) {\n level = stat.audioLevel;\n }\n\n if (level !== null && (maxLevel === null || level > maxLevel)) {\n maxLevel = level;\n }\n }\n\n this._audioLevel = maxLevel;\n\n if (this._running && now() - this._lastCalled < 10000) {\n this._requestAnimationFrame(this._loop.bind(this));\n } else {\n this._running = false;\n }\n }\n });\n };\n\n _proto.destroy = function destroy() {\n this._running = false;\n };\n\n _proto.sample = function sample(done) {\n if (typeof done === 'function') {\n throw new Error('sample no longer takes a callback');\n }\n\n this._startLoop();\n\n this._lastCalled = now();\n return this._audioLevel;\n };\n\n return GetAudioLevelSampler;\n}(), _temp);\n\n/***/ }),\n/* 252 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst merge = __webpack_require__(147);\n\nconst normalizeConstraintInput = __webpack_require__(555);\n\nconst isScreenSharingSource = __webpack_require__(262);\n\nfunction createSourceConstraint(media, enableRenegotiation, usingOptionalMandatoryStyle) {\n if (!media.publish && enableRenegotiation) {\n // FIXME: I'm still not quite happy with how this works. With renegotiation, when publish\n // changes to true we shouldn't have to recalculate constraints. I think Publisher should handle\n // this override instead and this component shouldn't know anything about renegotiation.\n // Related: OPENTOK-31082\n return false;\n }\n\n if (typeof media.source === 'boolean') {\n return media.source;\n }\n\n if (usingOptionalMandatoryStyle) {\n return {\n mandatory: {\n sourceId: media.source\n }\n };\n } // getDisplayMedia doesn't support exact constraints\n\n\n if (isScreenSharingSource(media.source)) {\n return {\n deviceId: media.source\n };\n }\n\n return {\n deviceId: {\n exact: media.source\n }\n };\n}\n\nfunction generateAudioConstraints(opt) {\n let constraints = createSourceConstraint(opt.audio, opt.enableRenegotiation, opt.usingOptionalMandatoryStyle);\n\n if (constraints === false) {\n return false;\n }\n\n const usingOptionalMandatoryStyle = opt.usingOptionalMandatoryStyle,\n enableStereo = opt.enableStereo,\n echoCancellation = opt.echoCancellation,\n noiseSuppression = opt.noiseSuppression,\n autoGainControl = opt.autoGainControl,\n disableAudioProcessing = opt.disableAudioProcessing;\n\n const addConstraint = constraint => {\n if (typeof constraints !== 'object') return; // Chrome<61 uses optional/mandatory style because a boolean is equivalent to an ideal value\n // See https://bugs.chromium.org/p/chromium/issues/detail?id=700223#c3\n\n if (usingOptionalMandatoryStyle) {\n merge(constraints, {\n optional: []\n });\n constraints.optional.push(constraint);\n } else {\n merge(constraints, constraint);\n }\n };\n\n if ((enableStereo || disableAudioProcessing || !echoCancellation || !noiseSuppression || !autoGainControl) && constraints === true) {\n constraints = {};\n }\n\n if (enableStereo) {\n addConstraint({\n channelCount: 2\n });\n }\n\n ['echoCancellation', 'autoGainControl', 'noiseSuppression'].forEach(audioConstraintName => {\n const constraintValue = !disableAudioProcessing && opt[audioConstraintName];\n addConstraint({\n [audioConstraintName]: constraintValue\n });\n });\n return constraints;\n}\n\nfunction generateVideoConstraints(opt) {\n let constraints = createSourceConstraint(opt.video, opt.enableRenegotiation, opt.usingOptionalMandatoryStyle);\n\n if (constraints === false) {\n return false;\n }\n\n const videoDimensions = opt.videoDimensions,\n frameRate = opt.frameRate,\n maxResolution = opt.maxResolution,\n facingMode = opt.facingMode,\n usingOptionalMandatoryStyle = opt.usingOptionalMandatoryStyle;\n\n if ((videoDimensions || frameRate || maxResolution || facingMode) && constraints === true) {\n constraints = {};\n }\n\n if (videoDimensions) {\n const width = videoDimensions.width;\n const height = videoDimensions.height;\n\n if (usingOptionalMandatoryStyle) {\n merge(constraints, {\n optional: []\n });\n constraints.optional.push({\n minWidth: width\n }, {\n maxWidth: width\n }, {\n minHeight: height\n }, {\n maxHeight: height\n });\n } else {\n merge(constraints, {\n width: {\n ideal: width\n },\n height: {\n ideal: height\n }\n });\n }\n }\n\n if (frameRate) {\n if (usingOptionalMandatoryStyle) {\n merge(constraints, {\n optional: []\n });\n constraints.optional.push({\n minFrameRate: frameRate\n }, {\n maxFrameRate: frameRate\n });\n } else {\n merge(constraints, {\n frameRate: {\n ideal: frameRate\n }\n });\n }\n }\n\n if (maxResolution) {\n if (usingOptionalMandatoryStyle) {\n merge(constraints, {\n mandatory: {\n maxWidth: maxResolution.width,\n maxHeight: maxResolution.height\n }\n });\n } else {\n merge(constraints, {\n width: {\n max: maxResolution.width\n },\n height: {\n max: maxResolution.height\n }\n });\n }\n }\n\n if (facingMode) {\n if (usingOptionalMandatoryStyle) {\n merge(constraints, {\n optional: []\n });\n constraints.optional.push({\n facingMode\n });\n } else {\n merge(constraints, {\n facingMode: {\n ideal: facingMode\n }\n });\n }\n }\n\n return constraints;\n}\n\nfunction generateConstraints(opt) {\n return {\n audio: generateAudioConstraints(opt),\n video: generateVideoConstraints(opt)\n };\n}\n\nmodule.exports = function generateConstraintInfo(opt) {\n const normOpt = normalizeConstraintInput(opt);\n const constraints = generateConstraints(normOpt);\n return {\n constraints,\n publishAudio: normOpt.audio.publish,\n publishVideo: normOpt.video.publish,\n frameRate: normOpt.frameRate,\n videoDimensions: normOpt.videoDimensions,\n audioDeviceId: typeof normOpt.audio.source === 'string' ? normOpt.audio.source : undefined,\n videoDeviceId: typeof normOpt.video.source === 'string' ? normOpt.video.source : undefined\n };\n};\n\n/***/ }),\n/* 253 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseAssignValue = __webpack_require__(62),\n eq = __webpack_require__(50);\n\n/**\n * This function is like `assignValue` except that it doesn't assign\n * `undefined` values.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction assignMergeValue(object, key, value) {\n if ((value !== undefined && !eq(object[key], value)) ||\n (value === undefined && !(key in object))) {\n baseAssignValue(object, key, value);\n }\n}\n\nmodule.exports = assignMergeValue;\n\n\n/***/ }),\n/* 254 */\n/***/ (function(module, exports) {\n\n/**\n * Gets the value at `key`, unless `key` is \"__proto__\" or \"constructor\".\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction safeGet(object, key) {\n if (key === 'constructor' && typeof object[key] === 'function') {\n return;\n }\n\n if (key == '__proto__') {\n return;\n }\n\n return object[key];\n}\n\nmodule.exports = safeGet;\n\n\n/***/ }),\n/* 255 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst _require = __webpack_require__(2),\n name = _require.name,\n version = _require.version,\n isChromiumEdge = _require.isChromiumEdge;\n\nmodule.exports = isChromiumEdge || name === 'Chrome' && version >= 72 || name === 'Firefox' && version >= 66 || name === 'Opera' && version >= 60;\n\n/***/ }),\n/* 256 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar toString = __webpack_require__(57),\n upperFirst = __webpack_require__(257);\n\n/**\n * Converts the first character of `string` to upper case and the remaining\n * to lower case.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to capitalize.\n * @returns {string} Returns the capitalized string.\n * @example\n *\n * _.capitalize('FRED');\n * // => 'Fred'\n */\nfunction capitalize(string) {\n return upperFirst(toString(string).toLowerCase());\n}\n\nmodule.exports = capitalize;\n\n\n/***/ }),\n/* 257 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar createCaseFirst = __webpack_require__(560);\n\n/**\n * Converts the first character of `string` to upper case.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the converted string.\n * @example\n *\n * _.upperFirst('fred');\n * // => 'Fred'\n *\n * _.upperFirst('FRED');\n * // => 'FRED'\n */\nvar upperFirst = createCaseFirst('toUpperCase');\n\nmodule.exports = upperFirst;\n\n\n/***/ }),\n/* 258 */\n/***/ (function(module, exports) {\n\n/**\n * The base implementation of `_.slice` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\nfunction baseSlice(array, start, end) {\n var index = -1,\n length = array.length;\n\n if (start < 0) {\n start = -start > length ? 0 : (length + start);\n }\n end = end > length ? length : end;\n if (end < 0) {\n end += length;\n }\n length = start > end ? 0 : ((end - start) >>> 0);\n start >>>= 0;\n\n var result = Array(length);\n while (++index < length) {\n result[index] = array[index + start];\n }\n return result;\n}\n\nmodule.exports = baseSlice;\n\n\n/***/ }),\n/* 259 */\n/***/ (function(module, exports) {\n\n/** Used to compose unicode character classes. */\nvar rsAstralRange = '\\\\ud800-\\\\udfff',\n rsComboMarksRange = '\\\\u0300-\\\\u036f',\n reComboHalfMarksRange = '\\\\ufe20-\\\\ufe2f',\n rsComboSymbolsRange = '\\\\u20d0-\\\\u20ff',\n rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,\n rsVarRange = '\\\\ufe0e\\\\ufe0f';\n\n/** Used to compose unicode capture groups. */\nvar rsZWJ = '\\\\u200d';\n\n/** 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/). */\nvar reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');\n\n/**\n * Checks if `string` contains Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a symbol is found, else `false`.\n */\nfunction hasUnicode(string) {\n return reHasUnicode.test(string);\n}\n\nmodule.exports = hasUnicode;\n\n\n/***/ }),\n/* 260 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar asciiToArray = __webpack_require__(562),\n hasUnicode = __webpack_require__(259),\n unicodeToArray = __webpack_require__(563);\n\n/**\n * Converts `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\nfunction stringToArray(string) {\n return hasUnicode(string)\n ? unicodeToArray(string)\n : asciiToArray(string);\n}\n\nmodule.exports = stringToArray;\n\n\n/***/ }),\n/* 261 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2);\n/*\nthe capture screen share with audio is only avaible for Chrome >= 74 & Chromium Edge >= 79, see\nhttps://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#browser_compatibility\n\nAlso note that capture audio is only avaible for the user via the checkmark in tab selection\n*/\n\n\nmodule.exports = Boolean(env.isChrome && env.version >= 74 || env.isEdge && env.version >= 79);\n\n/***/ }),\n/* 262 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst screenSharingSources = __webpack_require__(564);\n\nconst isElectronScreenSharingSource = __webpack_require__(263);\n\nconst env = __webpack_require__(2);\n\nmodule.exports = videoSource => {\n if (env.isElectron) {\n // Electron only supports a subset of `screenSharingSources` available on\n // other user agents\n if (videoSource === 'screen') {\n return true;\n } // Checks if this video source is a custom screensharing source\n\n\n return isElectronScreenSharingSource(videoSource);\n }\n\n return screenSharingSources.indexOf(videoSource) > -1;\n};\n\n/***/ }),\n/* 263 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/* global MediaStreamTrack */\nconst env = __webpack_require__(2);\n\nmodule.exports = videoSource => {\n if (!env.isElectron) {\n return false;\n } // ElectronJS sets the label to the same value for screensharing sources,\n // regardless of the DesktopCapturer source type\n\n\n return videoSource instanceof MediaStreamTrack && videoSource.label === 'Screen';\n};\n\n/***/ }),\n/* 264 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseForOwn = __webpack_require__(99),\n createBaseEach = __webpack_require__(569);\n\n/**\n * The base implementation of `_.forEach` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\nvar baseEach = createBaseEach(baseForOwn);\n\nmodule.exports = baseEach;\n\n\n/***/ }),\n/* 265 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = 15000;\n\n/***/ }),\n/* 266 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/**\n * castToBoolean\n *\n * @param {any} The value to cast\n * @returns {Boolean} True if value === 'true' or value === true, otherwise false.\n */\nmodule.exports = function (value, defaultValue) {\n if (defaultValue === void 0) {\n defaultValue = false;\n }\n\n return value === undefined ? defaultValue : value === 'true' || value === true;\n};\n\n/***/ }),\n/* 267 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2);\n\nmodule.exports = () => {\n const isBrowser = !env.isNode && !env.isElectron;\n\n if (!isBrowser) {\n return false;\n } // More recent browsers don't expose GUM when called in insecure contextso\n // From https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia\n\n\n const isSecureContextRequired = // See https://www.chromestatus.com/feature/4924861776396288\n (env.isChrome || env.isOpera || env.isChromiumEdge) && env.version >= 74 || env.isFirefox && env.version >= 68 || // See https://webkit.org/blog/7763/a-closer-look-into-webrtc/\n env.isSafari && env.version >= 11;\n return isSecureContextRequired;\n};\n\n/***/ }),\n/* 268 */\n/***/ (function(module, exports) {\n\nfunction _setPrototypeOf(o, p) {\n module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\n\n return _setPrototypeOf(o, p);\n}\n\nmodule.exports = _setPrototypeOf;\n\n/***/ }),\n/* 269 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar arrayMap = __webpack_require__(31),\n baseClone = __webpack_require__(77),\n baseUnset = __webpack_require__(585),\n castPath = __webpack_require__(56),\n copyObject = __webpack_require__(37),\n customOmitClone = __webpack_require__(588),\n flatRest = __webpack_require__(137),\n getAllKeysIn = __webpack_require__(182);\n\n/** Used to compose bitmasks for cloning. */\nvar CLONE_DEEP_FLAG = 1,\n CLONE_FLAT_FLAG = 2,\n CLONE_SYMBOLS_FLAG = 4;\n\n/**\n * The opposite of `_.pick`; this method creates an object composed of the\n * own and inherited enumerable property paths of `object` that are not omitted.\n *\n * **Note:** This method is considerably slower than `_.pick`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [paths] The property paths to omit.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omit(object, ['a', 'c']);\n * // => { 'b': '2' }\n */\nvar omit = flatRest(function(object, paths) {\n var result = {};\n if (object == null) {\n return result;\n }\n var isDeep = false;\n paths = arrayMap(paths, function(path) {\n path = castPath(path, object);\n isDeep || (isDeep = path.length > 1);\n return path;\n });\n copyObject(object, getAllKeysIn(object), result);\n if (isDeep) {\n result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);\n }\n var length = paths.length;\n while (length--) {\n baseUnset(result, paths[length]);\n }\n return result;\n});\n\nmodule.exports = omit;\n\n\n/***/ }),\n/* 270 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseFindIndex = __webpack_require__(203),\n baseIteratee = __webpack_require__(32),\n toInteger = __webpack_require__(96);\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max;\n\n/**\n * This method is like `_.find` except that it returns the index of the first\n * element `predicate` returns truthy for instead of the element itself.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.findIndex(users, function(o) { return o.user == 'barney'; });\n * // => 0\n *\n * // The `_.matches` iteratee shorthand.\n * _.findIndex(users, { 'user': 'fred', 'active': false });\n * // => 1\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findIndex(users, ['active', false]);\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.findIndex(users, 'active');\n * // => 2\n */\nfunction findIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseFindIndex(array, baseIteratee(predicate, 3), index);\n}\n\nmodule.exports = findIndex;\n\n\n/***/ }),\n/* 271 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseUniq = __webpack_require__(590);\n\n/**\n * Creates a duplicate-free version of an array, using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons, in which only the first occurrence of each element\n * is kept. The order of result values is determined by the order they occur\n * in the array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniq([2, 1, 2]);\n * // => [2, 1]\n */\nfunction uniq(array) {\n return (array && array.length) ? baseUniq(array) : [];\n}\n\nmodule.exports = uniq;\n\n\n/***/ }),\n/* 272 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst toArray = __webpack_require__(598);\n\nconst getRtcStatsReportAdapter = __webpack_require__(275);\n\nconst shouldUseStandardGetStats = __webpack_require__(102)(); // This returns the array-ified list of stats our users are accustomed to\n\n\nmodule.exports = (peerConnection, completion) => {\n getRtcStatsReportAdapter(peerConnection, (err, rtcStatsReport) => {\n let stats = [];\n\n if (shouldUseStandardGetStats) {\n // Firefox <= 45 can't use rtcStatsReport.values OPENTOK-32755\n if (typeof rtcStatsReport.values === 'function') {\n stats = toArray(rtcStatsReport.values());\n } else if (typeof rtcStatsReport.forEach !== 'function') {\n stats = Object.keys(rtcStatsReport).map(key => rtcStatsReport[key]);\n } else {\n rtcStatsReport.forEach(rtcStats => {\n stats.push(rtcStats);\n });\n }\n } else {\n rtcStatsReport.result().forEach(rtcStat => {\n const stat = {};\n rtcStat.names().forEach(name => {\n stat[name] = rtcStat.stat(name);\n }); // fake the structure of the \"new\" RTC stat object\n\n stat.id = rtcStat.id;\n stat.type = rtcStat.type;\n stat.timestamp = rtcStat.timestamp;\n stats.push(stat);\n });\n }\n\n completion(null, stats);\n });\n};\n\n/***/ }),\n/* 273 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseGetTag = __webpack_require__(27),\n isArray = __webpack_require__(10),\n isObjectLike = __webpack_require__(14);\n\n/** `Object#toString` result references. */\nvar stringTag = '[object String]';\n\n/**\n * Checks if `value` is classified as a `String` primitive or object.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a string, else `false`.\n * @example\n *\n * _.isString('abc');\n * // => true\n *\n * _.isString(1);\n * // => false\n */\nfunction isString(value) {\n return typeof value == 'string' ||\n (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);\n}\n\nmodule.exports = isString;\n\n\n/***/ }),\n/* 274 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar baseValues = __webpack_require__(600),\n keys = __webpack_require__(28);\n\n/**\n * Creates an array of the own enumerable string keyed property values of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property values.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.values(new Foo);\n * // => [1, 2] (iteration order is not guaranteed)\n *\n * _.values('hi');\n * // => ['h', 'i']\n */\nfunction values(object) {\n return object == null ? [] : baseValues(object, keys(object));\n}\n\nmodule.exports = values;\n\n\n/***/ }),\n/* 275 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst shouldUseStandardGetStats = __webpack_require__(102)();\n\nconst getRtcStats = (peerConnection, completion) => {\n peerConnection.getStats(null).then(stats => completion(null, stats)).catch(completion);\n};\n\nconst getRtcStatsLegacy = (peerConnection, completion) => {\n peerConnection.getStats(stats => {\n completion(null, stats);\n }, completion);\n}; // This returns the native or \"raw\" RtcStatsReport object\n\n\nconst getRtcStatsReportAdapter = (peerConnection, completion) => {\n const getRtcStatsReport = shouldUseStandardGetStats ? getRtcStats : getRtcStatsLegacy;\n getRtcStatsReport(peerConnection, completion);\n};\n\nmodule.exports = getRtcStatsReportAdapter;\n\n/***/ }),\n/* 276 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nexports.__esModule = true;\nexports.default = void 0;\n\nvar _assertThisInitialized2 = _interopRequireDefault(__webpack_require__(156));\n\nvar _inheritsLoose2 = _interopRequireDefault(__webpack_require__(58));\n\nvar _defineProperty2 = _interopRequireDefault(__webpack_require__(16));\n\nvar _events = __webpack_require__(39);\n\nvar _clone = _interopRequireDefault(__webpack_require__(34));\n\nvar _parseQOS = _interopRequireDefault(__webpack_require__(610));\n\nvar _TruthyChangeCounter = _interopRequireDefault(__webpack_require__(612));\n\nvar _now = _interopRequireDefault(__webpack_require__(47));\n\nvar _log = _interopRequireDefault(__webpack_require__(0));\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, no-void, no-use-before-define, one-var */\n\n/* eslint-disable no-restricted-syntax, no-prototype-builtins, no-underscore-dangle */\nconst logging = (0, _log.default)('QoS');\n\nlet Qos = /*#__PURE__*/function (_EventEmitter) {\n (0, _inheritsLoose2.default)(Qos, _EventEmitter);\n\n function Qos() {\n var _this;\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n _this = _EventEmitter.call(this, ...args) || this;\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"_peerConnection\", void 0);\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"_isPublisher\", void 0);\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"timeoutId\", void 0);\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"_creationTime\", (0, _now.default)());\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"offerMessagesReceived\", 0);\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"prevStats\", {\n timeStamp: (0, _now.default)()\n });\n (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), \"changeCounters\", {\n videoCodec: new _TruthyChangeCounter.default(),\n audioCodec: new _TruthyChangeCounter.default()\n });\n return _this;\n }\n\n var _proto = Qos.prototype;\n\n _proto.startCollecting = function startCollecting(peerConnection, isPublisher) {\n if (!peerConnection || !peerConnection.getStats) {\n // this peerConnection doesn't support getStats, bail\n return;\n }\n\n this._isPublisher = isPublisher;\n const hadPeerConnection = Boolean(this._peerConnection);\n this._peerConnection = peerConnection;\n\n if (hadPeerConnection) {\n return;\n }\n\n const collectionLoop = interval => {\n const currentTime = (0, _now.default)(); // parseQOS pushes properties onto this object and gives it back\n\n const blankStats = {\n timeStamp: currentTime,\n duration: Math.round((currentTime - this._creationTime) / 1000),\n period: Math.round((currentTime - this.prevStats.timeStamp) / 1000)\n };\n (0, _parseQOS.default)(this._peerConnection, this.prevStats, blankStats, this._isPublisher, (err, parsedStats) => {\n if (err) {\n logging.error('Failed to Parse QOS Stats:', err);\n return;\n }\n\n this.handleParsedStats(parsedStats); // Recalculate the stats\n\n clearTimeout(this.timeoutId);\n this.timeoutId = setTimeout(() => collectionLoop(Qos.INTERVAL), interval);\n });\n };\n\n this.timeoutId = setTimeout(() => collectionLoop(Qos.INTERVAL - Qos.INITIAL_INTERVAL), Qos.INITIAL_INTERVAL);\n };\n\n _proto.stopCollecting = function stopCollecting() {\n if (this.timeoutId) {\n clearTimeout(this.timeoutId);\n this.timeoutId = null;\n }\n\n this._peerConnection = null;\n };\n\n _proto.handleOfferMessageReceived = function handleOfferMessageReceived() {\n this.offerMessagesReceived += 1;\n };\n\n _proto.handleParsedStats = function handleParsedStats(parsedStats) {\n Object.keys(this.changeCounters).forEach(key => {\n this.changeCounters[key].onValue(parsedStats[key]); // audioCodecChangeCount, videoCodecChangeCount\n\n parsedStats[`${key}ChangeCount`] = this.changeCounters[key].changeCount;\n });\n parsedStats.offerMessagesReceived = this.offerMessagesReceived;\n const periodicStats = (0, _clone.default)(parsedStats); // The following stats are reported with cumulative values:\n\n const periodicDataKeys = ['audioSentBytes', 'audioSentPackets', 'audioSentPacketsLost', 'videoSentBytes', 'videoSentPackets', 'videoSentPacketsLost', 'audioRecvBytes', 'audioRecvPackets', 'audioRecvPacketsLost', 'videoRecvBytes', 'videoRecvPackets', 'videoRecvPacketsLost']; // This adjusts the QoS values to be periodic, rather than cumulative:\n\n periodicDataKeys.forEach(dataKey => {\n periodicStats[dataKey] = this.prevStats && this.prevStats[dataKey] ? parsedStats[dataKey] - this.prevStats[dataKey] : parsedStats[dataKey];\n });\n\n if (this.prevStats && this.prevStats.videoFramesReceived) {\n const framesReceived = parsedStats.videoFramesReceived - this.prevStats.videoFramesReceived;\n const period = parsedStats.period;\n periodicStats.videoFrameRateReceived = Math.round(framesReceived / period);\n }\n\n this.prevStats = parsedStats;\n this.emit('stats', periodicStats);\n };\n\n return Qos;\n}(_events.EventEmitter); // Send stats after 1 sec\n\n\nQos.INITIAL_INTERVAL = 1000; // Recalculate the stats every 30 sec\n\nQos.INTERVAL = 30000;\nvar _default = Qos;\nexports.default = _default;\n\n/***/ }),\n/* 277 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = priority => {\n switch (priority >> 24) {\n // eslint-disable-line no-bitwise\n case 0:\n return 'TURN/TLS';\n\n case 1:\n return 'TURN/TCP';\n\n case 2:\n return 'TURN/UDP';\n\n default:\n return '';\n }\n};\n\n/***/ }),\n/* 278 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require, no-void, prefer-rest-params, no-shadow */\nconst uuid = __webpack_require__(17);\n\nconst _require = __webpack_require__(158),\n haveGetDisplayMediaDefault = _require.default;\n\nconst ErrorsDefault = __webpack_require__(6);\n\nconst otErrorDefault = __webpack_require__(11);\n\nmodule.exports = function chromeExtensionHelperFactory(_temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n _ref$haveGetDisplayMe = _ref.haveGetDisplayMedia,\n haveGetDisplayMedia = _ref$haveGetDisplayMe === void 0 ? haveGetDisplayMediaDefault : _ref$haveGetDisplayMe,\n _ref$global = _ref.global,\n context = _ref$global === void 0 ? (typeof window !== undefined ? window : global) : _ref$global,\n _ref$Errors = _ref.Errors,\n Errors = _ref$Errors === void 0 ? ErrorsDefault : _ref$Errors,\n _ref$otError = _ref.otError,\n otError = _ref$otError === void 0 ? otErrorDefault() : _ref$otError;\n\n const isSupportedInThisBrowser = // don't bother supporting extension when we have display media\n !haveGetDisplayMedia && !!context.navigator.webkitGetUserMedia && !context.navigator.userAgent.match(/android/i) && typeof context.chrome !== 'undefined';\n return {\n isSupportedInThisBrowser,\n autoRegisters: false,\n extensionRequired: true,\n getConstraintsShowsPermissionUI: true,\n sources: {\n screen: true,\n application: false,\n window: true,\n browser: true\n },\n\n register(extensionID, version) {\n if (version === 2) {\n return this.registerVersion2(extensionID);\n }\n\n return this.registerVersion1(extensionID);\n },\n\n registerVersion1(extensionID) {\n if (!extensionID) {\n throw new Error('initChromeScreenSharingExtensionHelper: extensionID is required.');\n }\n\n const isChrome = isSupportedInThisBrowser;\n const callbackRegistry = {};\n let isInstalled = void 0;\n const prefix = `com.tokbox.screenSharing.${extensionID}`;\n\n const request = function request(method, payload) {\n const res = {\n payload,\n from: 'jsapi'\n };\n res[prefix] = method;\n return res;\n };\n\n const addCallback = function addCallback(fn, timeToWait) {\n let timeout;\n const requestId = uuid();\n\n callbackRegistry[requestId] = function () {\n clearTimeout(timeout);\n timeout = null;\n fn(...arguments);\n };\n\n if (timeToWait) {\n timeout = setTimeout(() => {\n delete callbackRegistry[requestId];\n fn(otError(Errors.TIMEOUT, new Error('Timeout waiting for response to screensharing request.')));\n }, timeToWait);\n }\n\n return requestId;\n };\n\n const isAvailable = function isAvailable(callback) {\n if (!callback) {\n throw new Error('isAvailable: callback is required.');\n }\n\n if (!isChrome) {\n setTimeout(callback.bind(null, false));\n return;\n }\n\n if (isInstalled !== void 0) {\n setTimeout(callback.bind(null, isInstalled));\n } else {\n const requestId = addCallback(event => {\n if (isInstalled !== true) {\n isInstalled = event === 'extensionLoaded';\n }\n\n callback(isInstalled);\n }, 2000);\n const post = request('isExtensionInstalled', {\n requestId\n });\n context.postMessage(post, '*');\n }\n };\n\n const getConstraints = function getConstraints(source, constraints, callback) {\n if (!callback) {\n throw new Error('getSourceId: callback is required');\n }\n\n isAvailable(isInstalled => {\n if (!isInstalled) {\n return callback(otError(Errors.SCREEN_SHARING_EXTENSION_NOT_INSTALLED, new Error('Extension is not installed')));\n }\n\n const requestId = addCallback((event, payload) => {\n if (typeof event === 'object' && event.name && event.stack) {\n // FIXME: The callback shouldn't be overloaded like this ('event' is actually an\n // error), but I'm adding tests and don't want to refactor right now without testing\n // more thorougly.\n const error = event;\n callback(error);\n } else if (event === 'permissionDenied') {\n callback(otError(Errors.USER_MEDIA_ACCESS_DENIED, new Error('User denied access to screensharing')));\n } else {\n if (!constraints.video) {\n constraints.video = {};\n }\n\n if (!constraints.video.mandatory) {\n constraints.video.mandatory = {};\n }\n\n constraints.video.mandatory.chromeMediaSource = 'desktop';\n constraints.video.mandatory.chromeMediaSourceId = payload.sourceId;\n callback(void 0, constraints);\n }\n });\n context.postMessage(request('getSourceId', {\n requestId,\n source\n }), '*');\n return undefined;\n });\n };\n\n context.addEventListener('message', event => {\n if (event.origin !== context.location.origin) {\n return;\n }\n\n if (!(event.data != null && typeof event.data === 'object')) {\n return;\n }\n\n if (event.data.from !== 'extension') {\n return;\n }\n\n const method = event.data[prefix];\n const payload = event.data.payload;\n\n if (payload && payload.requestId) {\n const callback = callbackRegistry[payload.requestId];\n delete callbackRegistry[payload.requestId];\n\n if (callback) {\n callback(method, payload);\n }\n }\n\n if (method === 'extensionLoaded') {\n isInstalled = true;\n }\n });\n return {\n extensionAPIVersion: 1,\n extensionID,\n isInstalled: isAvailable,\n getConstraints\n };\n },\n\n registerVersion2(extensionID) {\n const isChrome = isSupportedInThisBrowser && typeof context.chrome.runtime !== 'undefined';\n\n const isInstalled = function isInstalled(callback) {\n if (!callback) {\n throw new Error('isAvailable: callback is required.');\n }\n\n if (!isChrome) {\n setTimeout(callback.bind(null, false));\n return;\n }\n\n context.chrome.runtime.sendMessage(extensionID, {\n type: 'isInstalled'\n }, null, response => {\n setTimeout(callback.bind(null, !!response));\n });\n };\n\n const getConstraints = function getConstraints(source, constraints, callback) {\n if (!callback) {\n throw new Error('getSourceId: callback is required');\n }\n\n isInstalled(installed => {\n if (!installed) {\n return callback(otError(Errors.SCREEN_SHARING_EXTENSION_NOT_INSTALLED, new Error('Extension is not installed')));\n }\n\n context.chrome.runtime.sendMessage(extensionID, {\n type: 'getSourceId',\n source\n }, null, data => {\n if (data.error === 'permissionDenied') {\n callback(otError(Errors.USER_MEDIA_ACCESS_DENIED, new Error('User denied access to screensharing')));\n } else if (data.error) {\n callback(new Error(`UnexpectError: ${data.error}`));\n } else {\n if (!constraints.video) {\n constraints.video = {};\n }\n\n if (!constraints.video.mandatory) {\n constraints.video.mandatory = {};\n }\n\n constraints.video.mandatory.chromeMediaSource = 'desktop';\n constraints.video.mandatory.chromeMediaSourceId = data.sourceId;\n callback(void 0, constraints);\n }\n });\n return undefined;\n });\n };\n\n return {\n extensionAPIVersion: 2,\n extensionID,\n isInstalled,\n getConstraints\n };\n }\n\n };\n};\n\n/***/ }),\n/* 279 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable global-require */\nconst createCleanupJobs = __webpack_require__(154);\n\nconst RafRunner = __webpack_require__(103);\n\nmodule.exports = function createChromeMixinFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const Archiving = deps.Archiving || __webpack_require__(616);\n\n const AudioLevelMeter = deps.AudioLevelMeter || __webpack_require__(280);\n\n const AudioLevelTransformer = deps.AudioLevelTransformer || __webpack_require__(143);\n\n const BackingBar = deps.BackingBar || __webpack_require__(281);\n\n const Chrome = deps.Chrome || __webpack_require__(144);\n\n const MuteButton = deps.MuteButton || __webpack_require__(282)();\n\n const NamePanel = deps.NamePanel || __webpack_require__(283);\n\n const StylableComponent = deps.StylableComponent || __webpack_require__(151);\n\n const logging = deps.logging || __webpack_require__(0)('createChromeMixin');\n\n return function createChromeMixin(publisher, _temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n name = _ref.name,\n publishAudio = _ref.publishAudio,\n publishVideo = _ref.publishVideo,\n audioSource = _ref.audioSource,\n showControls = _ref.showControls,\n shouldAllowAudio = _ref.shouldAllowAudio,\n logAnalyticsEvent = _ref.logAnalyticsEvent;\n\n let audioLevelMeter;\n let chrome;\n let widgetView;\n const chromeMixin = {};\n const cleanupJobs = createCleanupJobs(); // If mode is false, then that is the mode. If mode is true then we'll\n // definitely display the button, but we'll defer the model to the\n // Publishers buttonDisplayMode style property.\n\n const chromeButtonMode = mode => {\n if (mode === false) {\n return 'off';\n }\n\n const defaultMode = publisher.getStyle('buttonDisplayMode'); // The default model is false, but it's overridden by +mode+ being true\n\n if (defaultMode === false) {\n return 'on';\n } // defaultMode is either true or auto.\n\n\n return defaultMode;\n };\n\n let uiPromise = new Promise((resolve, reject) => {\n chromeMixin.init = widgetViewParam => {\n if (!publisher.getStyle('showArchiveStatus')) {\n logAnalyticsEvent('showArchiveStatus', 'createChrome', {\n mode: 'off'\n });\n }\n\n const widgets = {\n backingBar: new BackingBar({\n nameMode: !name ? 'off' : publisher.getStyle('nameDisplayMode'),\n muteMode: chromeButtonMode(publisher.getStyle('buttonDisplayMode'))\n }),\n name: new NamePanel({\n name,\n mode: publisher.getStyle('nameDisplayMode')\n }),\n archive: new Archiving({\n show: Boolean(publisher.getStyle('showArchiveStatus')) && publisher.getStyle('archiveStatusDisplayMode') !== 'off',\n archiving: false\n }),\n muteButton: new MuteButton({\n muted: publishAudio === false,\n mode: chromeButtonMode.call(null, publisher.getStyle('buttonDisplayMode'))\n })\n };\n audioLevelMeter = new AudioLevelMeter({\n mode: publisher.getStyle('audioLevelDisplayMode')\n });\n const audioLevelTransformer = new AudioLevelTransformer();\n const audioWatcher = new RafRunner(() => {\n // @FIXME\n // We force the audio level value to be zero here if audio is disabled\n // because the AudioLevelMeter cannot currently differentiate\n // between video being disabled and audio being disabled.\n // To be fixed as part of OPENTOK-29865\n const audioLevel = !publishAudio ? 0 : audioLevelTransformer.transform(publisher.loudness);\n audioLevelMeter.setValue(audioLevel);\n });\n audioLevelMeter.watchVisibilityChanged(visible => {\n if (visible) {\n audioWatcher.start();\n } else {\n audioWatcher.stop();\n }\n });\n audioLevelMeter.audioOnly(!publishVideo && publishAudio);\n widgets.audioLevel = audioLevelMeter;\n\n if (widgetViewParam && widgetViewParam.domElement) {\n widgetView = widgetViewParam;\n chrome = new Chrome({\n parent: widgetView.domElement\n }).set(widgets).on({\n muted: () => publisher.publishAudio(false),\n unmuted: () => publisher.publishAudio(true)\n });\n\n if (audioSource === null || audioSource === false) {\n chromeMixin.removeAudioTrack();\n }\n\n resolve();\n }\n };\n\n chromeMixin.reset = () => {\n // reject in case the chrome creating was still pending\n reject(new Error('Chrome still being created'));\n\n if (chrome) {\n chrome.destroy();\n chrome = null;\n }\n };\n\n chromeMixin.destroy = () => {\n chromeMixin.reset();\n cleanupJobs.releaseAll();\n uiPromise = null;\n };\n });\n uiPromise.catch(err => {\n // Only log unexpected rejections\n if (!err || err.message !== 'Chrome still being created') {\n logging.error('createChromeMixin failed to setup UI', err);\n }\n });\n\n chromeMixin.setAudioOnly = value => {\n if (audioLevelMeter) {\n audioLevelMeter.audioOnly(value);\n }\n };\n\n chromeMixin.setArchivingStatus = status => {\n if (chrome) {\n chrome.archive.setArchiving(status);\n }\n };\n\n chromeMixin.setMuted = muted => {\n if (chrome && chrome.muteButton) {\n chrome.muteButton.muted(muted);\n }\n };\n\n chromeMixin.removeAudioTrack = () => {\n if (chrome && chrome.muteButton) {\n chrome.muteButton.remove();\n }\n };\n\n chromeMixin.addAudioTrack = () => {\n if (chrome && chrome.muteButton) {\n chrome.muteButton.add();\n }\n };\n\n StylableComponent(publisher, {\n showArchiveStatus: true,\n nameDisplayMode: 'auto',\n buttonDisplayMode: 'auto',\n audioLevelDisplayMode: shouldAllowAudio ? 'auto' : 'off',\n archiveStatusDisplayMode: 'auto',\n backgroundImageURI: null\n }, showControls, payload => {\n logAnalyticsEvent('SetStyle', 'Publisher', payload, null, 0.1);\n });\n\n const onStyleValueChanged = (key, value) => {\n // enlist style change for when the the chrome is created\n uiPromise.then(() => {\n switch (key) {\n case 'nameDisplayMode':\n chrome.name.setDisplayMode(value);\n chrome.backingBar.setNameMode(value);\n break;\n\n case 'showArchiveStatus':\n logAnalyticsEvent('showArchiveStatus', 'styleChange', {\n mode: value ? 'on' : 'off'\n });\n chrome.archive.setShowArchiveStatus(Boolean(value));\n break;\n\n case 'archiveStatusDisplayMode':\n chrome.archive.setShowArchiveStatus(value !== 'off');\n break;\n\n case 'buttonDisplayMode':\n chrome.muteButton.setDisplayMode(value);\n chrome.backingBar.setMuteMode(value);\n break;\n\n case 'audioLevelDisplayMode':\n chrome.audioLevel.setDisplayMode(value);\n break;\n\n case 'backgroundImageURI':\n widgetView.setBackgroundImageURI(value);\n break;\n\n default:\n }\n });\n };\n\n publisher.on('styleValueChanged', onStyleValueChanged);\n cleanupJobs.add(() => publisher.off('styleValueChanged', onStyleValueChanged));\n return chromeMixin;\n };\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 280 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, one-var, no-mixed-operators */\nconst defineProperties = __webpack_require__(104);\n\nconst Widget = __webpack_require__(33);\n\nconst OTHelpers = __webpack_require__(4);\n\nconst EventEmitter = __webpack_require__(39);\n\nmodule.exports = function AudioLevelMeter(options) {\n const _audioLevelMeter = this;\n\n const _eventEmitter = new EventEmitter();\n\n let _voiceOnlyIconElement;\n\n let _meterValueElement;\n\n let _value;\n\n let _lastComputedVisibility; // display the widget by default but can be hidden when calling hideWhileLoading\n\n\n let _displayAroundLoading = true;\n let _audioOnly = false;\n let _displayMode = 'auto';\n\n const _maxValue = options.maxValue || 1;\n\n const _minValue = options.minValue || 0;\n\n function onCreate() {\n _meterValueElement = OTHelpers.createElement('div', {\n className: 'OT_audio-level-meter__value'\n }, '');\n _voiceOnlyIconElement = OTHelpers.createElement('div', {\n className: 'OT_audio-level-meter__audio-only-img'\n }, '');\n const domElement = _audioLevelMeter.domElement;\n domElement.appendChild(_voiceOnlyIconElement);\n domElement.appendChild(_meterValueElement);\n\n _audioLevelMeter.watchVisibilityChanged(visible => {\n if (visible) {\n OTHelpers.removeClass(_audioLevelMeter.domElement, 'OT_hide-forced');\n } else {\n OTHelpers.addClass(_audioLevelMeter.domElement, 'OT_hide-forced');\n }\n });\n }\n\n function onDestroy() {\n _eventEmitter.removeAllListeners('visibilityChanged');\n }\n\n function updateView() {\n const percentSize = (_value - _minValue) / (_maxValue - _minValue);\n _meterValueElement.style.transform = `scale(${Math.sqrt(percentSize)})`;\n } // computes the visibility value from the different \"inputs\" variables and asynchronously triggers\n // the internal \"visibilityChanged\" events if the value changed from last time it was computed\n\n\n function computeVisibility() {\n const computedVisibility = (_audioOnly && _displayMode === 'auto' || _displayMode === 'on') && _displayAroundLoading;\n\n if (_lastComputedVisibility !== computedVisibility) {\n _lastComputedVisibility = computedVisibility;\n\n _eventEmitter.emit('visibilityChanged', computedVisibility);\n }\n }\n\n defineProperties(_audioLevelMeter, {\n audioOnly: {\n get() {\n return _audioOnly;\n },\n\n set(audioOnly) {\n _audioOnly = audioOnly;\n computeVisibility();\n }\n\n }\n });\n\n _audioLevelMeter.setValue = function (value) {\n _value = value;\n updateView();\n };\n /**\n * Registers an callback to be executed when the visibility of the audio level meter changes.\n * \"true\" means the widget is shown.\n * The contracts is that the handlers should not try to change the visibility of the widget by\n * changing the value of visibility \"inputs\" (setting \"audioOnly\", \"displayMode\" or calling\n * \"hideWhileLoading\" and \"showAfterLoading\")\n *\n * @param {function(boolean)} cb the callback to be executed when the display value changes.\n * The callback is also executed with the last computed value when registered.\n * @returns {function} a callback to unregister the handler\n */\n\n\n _audioLevelMeter.watchVisibilityChanged = function (cb) {\n _eventEmitter.on('visibilityChanged', cb);\n\n Promise.resolve().then(() => {\n cb(_lastComputedVisibility);\n });\n return function stopWatching() {\n _eventEmitter.removeListener('visibilityChanged', cb);\n };\n }; // Mixin common widget behaviour\n\n\n const widgetOptions = {\n mode: options ? options.mode : 'auto',\n nodeName: 'div',\n htmlAttributes: {\n className: 'OT_audio-level-meter'\n },\n onCreate,\n onDestroy\n };\n Widget(this, widgetOptions); // The methods underneath are mixed in by \"Widget\" but overridden\n // Doing so, we can bypass it and compute the display value ourselves without relying on CSS\n\n _audioLevelMeter.setDisplayMode = function (mode) {\n _displayMode = mode;\n computeVisibility();\n };\n\n _audioLevelMeter.getDisplayMode = function () {\n return _displayMode;\n };\n\n _audioLevelMeter.showAfterLoading = function () {\n _displayAroundLoading = true;\n computeVisibility();\n };\n\n _audioLevelMeter.hideWhileLoading = function () {\n _displayAroundLoading = false;\n computeVisibility();\n }; // compute the initial visibility value\n\n\n computeVisibility();\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 281 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle */\nconst Widget = __webpack_require__(33); // BackingBar Chrome Widget\n//\n// nameMode (String)\n// Whether or not the name panel is being displayed\n// Possible values are: \"auto\" (the name is displayed\n// when the stream is first displayed and when the user mouses over the display),\n// \"off\" (the name is not displayed), and \"on\" (the name is displayed).\n//\n// muteMode (String)\n// Whether or not the mute button is being displayed\n// Possible values are: \"auto\" (the mute button is displayed\n// when the stream is first displayed and when the user mouses over the display),\n// \"off\" (the mute button is not displayed), and \"on\" (the mute button is displayed).\n//\n// displays a backing bar\n// can be shown/hidden\n// can be destroyed\n\n\nmodule.exports = function BackingBar(options) {\n let _nameMode = options.nameMode;\n let _muteMode = options.muteMode;\n\n function getDisplayMode() {\n if (_nameMode === 'on' || _muteMode === 'on') {\n return 'on';\n } else if (_nameMode === 'mini' || _muteMode === 'mini') {\n return 'mini';\n } else if (_nameMode === 'mini-auto' || _muteMode === 'mini-auto') {\n return 'mini-auto';\n } else if (_nameMode === 'auto' || _muteMode === 'auto') {\n return 'auto';\n }\n\n return 'off';\n } // Mixin common widget behaviour\n\n\n Widget(this, {\n mode: getDisplayMode(),\n nodeName: 'div',\n htmlContent: '',\n htmlAttributes: {\n className: 'OT_bar OT_edge-bar-item'\n }\n });\n\n this.setNameMode = function (nameMode) {\n _nameMode = nameMode;\n this.setDisplayMode(getDisplayMode());\n };\n\n this.setMuteMode = function (muteMode) {\n _muteMode = muteMode;\n this.setDisplayMode(getDisplayMode());\n };\n};\n\n/***/ }),\n/* 282 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst globalOTHelpers = __webpack_require__(4);\n\nconst globalWidget = __webpack_require__(33);\n\nconst defineProperties = __webpack_require__(104);\n\nmodule.exports = function MuteButtonFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const OTHelpers = deps.OTHelpers || globalOTHelpers;\n const Widget = deps.Widget || globalWidget;\n return function MuteButton(options) {\n let muted = options.muted || false;\n\n const updateClasses = () => {\n if (muted) {\n OTHelpers.addClass(this.domElement, 'OT_active');\n } else {\n OTHelpers.removeClass(this.domElement, 'OT_active ');\n }\n };\n\n const onClick = () => {\n muted = !muted;\n updateClasses();\n\n if (muted) {\n this.parent.trigger('muted', this);\n } else {\n this.parent.trigger('unmuted', this);\n }\n\n return false;\n }; // Private Event Callbacks\n\n\n const attachEvents = elem => {\n OTHelpers.on(elem, 'click', onClick);\n };\n\n const detachEvents = elem => {\n OTHelpers.off(elem, 'click', onClick);\n };\n\n defineProperties(this, {\n muted: {\n get() {\n return muted;\n },\n\n set(m) {\n muted = m;\n updateClasses();\n }\n\n }\n }); // Mixin common widget behaviour\n\n const classNames = muted ? 'OT_edge-bar-item OT_mute OT_active' : 'OT_edge-bar-item OT_mute';\n Widget(this, {\n mode: options.mode,\n nodeName: 'button',\n htmlContent: 'Mute',\n htmlAttributes: {\n className: classNames,\n type: 'button'\n },\n onCreate: attachEvents,\n onDestroy: detachEvents\n });\n let parentNode;\n\n this.remove = () => {\n if (this.domElement && this.domElement.parentNode) {\n parentNode = this.domElement.parentNode; // store in case we want to add it again\n\n this.domElement.parentNode.removeChild(this.domElement);\n }\n };\n\n this.add = () => {\n if (parentNode && this.domElement && !this.domElement.parentNode) {\n parentNode.appendChild(this.domElement);\n }\n };\n };\n};\n\n/***/ }),\n/* 283 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-underscore-dangle, no-param-reassign */\nconst Widget = __webpack_require__(33); // The \"name\" that we receive (e.g., from RUMOR) may actually be malicious HTML\n// so we need to sanitize it before it gets rendered.\n// See: OPENTOK-41141.\n\n\nconst sanitizeName = name => {\n const temp = document.createElement('div');\n temp.textContent = name;\n return temp.innerHTML;\n}; // NamePanel Chrome Widget\n//\n// mode (String)\n// Whether to display the name. Possible values are: \"auto\" (the name is displayed\n// when the stream is first displayed and when the user mouses over the display),\n// \"off\" (the name is not displayed), and \"on\" (the name is displayed).\n//\n// displays a name\n// can be shown/hidden\n// can be destroyed\n\n\nmodule.exports = function NamePanel(options) {\n let _name = options.name;\n\n if (!_name || _name.trim().length === '') {\n _name = null; // THere's no name, just flip the mode off\n\n options.mode = 'off';\n }\n\n this.setName = function (name) {\n if (!_name) {\n this.setDisplayMode('auto');\n }\n\n _name = sanitizeName(name); // The \"name\" is initially rendered as `innerHTML` when instantiated (as a\n // `Widget`). That's why we don't use `textContent` here, instead.\n\n this.domElement.innerHTML = _name;\n }.bind(this);\n\n _name = sanitizeName(_name); // Mixin common widget behaviour\n\n Widget(this, {\n mode: options.mode,\n nodeName: 'h1',\n htmlContent: _name,\n htmlAttributes: {\n className: 'OT_name OT_edge-bar-item'\n }\n });\n};\n\n/***/ }),\n/* 284 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require */\nmodule.exports = function getNativeEnumerateDevicesFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const windowMock = deps.global || (typeof window !== undefined ? window : global);\n return function getNativeEnumerateDevices() {\n if (windowMock.navigator.mediaDevices) {\n const mediaDevices = windowMock.navigator.mediaDevices;\n return mediaDevices.enumerateDevices && mediaDevices.enumerateDevices.bind(mediaDevices);\n } // @todo perhaps we should throw here?\n\n\n return undefined;\n };\n};\n\n/***/ }),\n/* 285 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2);\n\nconst haveDisplayMedia = __webpack_require__(158).default;\n\nconst isSupportedInThisBrowser = () => // Even though getDisplayMedia is supported in Edge, we haven't gotten it\n// to work with our SDK\n!env.isLegacyEdge && // Even though getDisplayMedia is exposed in Electron, devs need to use\n// desktopCapturer instead\n!env.isElectron && haveDisplayMedia && // getDisplayMedia is not supported in Android\n!env.isAndroid;\n\nmodule.exports = {\n isSupportedInThisBrowser: isSupportedInThisBrowser(),\n autoRegisters: true,\n extensionRequired: false,\n getConstraintsShowsPermissionUI: false,\n sources: {\n screen: true,\n application: false,\n window: env.name !== 'Safari',\n browser: env.name !== 'Safari'\n },\n\n register() {\n return {\n isInstalled(callback) {\n callback(undefined);\n },\n\n getConstraints(source, constraints, callback) {\n callback(undefined, {\n video: constraints.video,\n audio: constraints.audio\n });\n }\n\n };\n }\n\n};\n\n/***/ }),\n/* 286 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst isMediaStreamTrack = source => source && typeof source === 'object' && typeof source.kind === 'string' && typeof source.stop === 'function' && typeof source.enabled === 'boolean';\n\nexports.isMediaStreamTrack = isMediaStreamTrack;\n\n/***/ }),\n/* 287 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n/* WEBPACK VAR INJECTION */(function(Promise) {var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nvar __generator = (undefined && undefined.__generator) || function (thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (_) try {\n if (f = 1, y && (t = y[op[0] & 2 ? \"return\" : op[0] ? \"throw\" : \"next\"]) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [0, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n};\nfunction getGlobal() {\n return typeof window !== 'undefined' ?\n window : /* istanbul ignore next */ (typeof window !== undefined ? window : global);\n}\nvar CALLBACK_INVALID_RETURN = 'onFailure must return boolean, or delay in ms';\n/**\n * Retry a promise\n *\n * Uses promiseFactory to construct promises and calls onFailure when the\n * promise rejects.\n *\n * onFailure is called with error and attempt.\n *\n * You may inspect error to decide whether to retry or not. You may use attempt\n * to control the delay/ decide how many times to retry.\n *\n * To retry: A numeric delay in milliseconds can be returned to control how long\n * between retries.\n *\n * To stop retrying: Return false/ undefined/ null.\n *\n * The returned promise will be the final promise from promiseFactory once\n * fulfilled or in the case of errors, once no longer retrying.\n *\n * @example\n *\n * const promiseEndeavour = require('../dist/promiseEndeavour.js');\n * const fetch = require('node-fetch');\n *\n * async function getDogs() {\n * const response = await fetch('https://dog.ceo/api/breeds/list/all');\n * return response.json();\n * }\n *\n * const retries = [100, 500, 1000];\n *\n * function retry(error, attempt) {\n * return retries[attempt - 1] || false;\n * }\n *\n * promiseEndeavour(getDogs, retry)()\n * .then(dogs => console.log(dogs));\n *\n * @param promiseFactory A promise returning function\n * @param onFailure A function to be called when the promise returned by promiseFactory is rejected\n * @param maxAttempts Maximum number of attempts regardless of onFailure\n * @return A function with the same interface to promiseFactory\n */\nfunction promiseEndeavour(promiseFactory, onFailure, maxAttempts) {\n if (maxAttempts === void 0) { maxAttempts = 10; }\n return function () {\n var args = [];\n for (var _i = 0; _i < arguments.length; _i++) {\n args[_i] = arguments[_i];\n }\n return __awaiter(this, void 0, void 0, function () {\n var attempt, _loop_1, state_1;\n return __generator(this, function (_a) {\n switch (_a.label) {\n case 0:\n attempt = 0;\n _loop_1 = function () {\n var _a, error_1, delay_1;\n return __generator(this, function (_b) {\n switch (_b.label) {\n case 0:\n _b.trys.push([0, 2, , 6]);\n _a = {};\n return [4 /*yield*/, promiseFactory.apply(void 0, args)];\n case 1: return [2 /*return*/, (_a.value = _b.sent(), _a)];\n case 2:\n error_1 = _b.sent();\n delay_1 = onFailure(error_1, attempt);\n if (delay_1 === false || delay_1 == null || attempt >= maxAttempts) {\n throw error_1;\n }\n if (!(typeof delay_1 === 'number')) return [3 /*break*/, 4];\n return [4 /*yield*/, new Promise(function (resolve) { return getGlobal().setTimeout(resolve, delay_1); })];\n case 3:\n _b.sent();\n return [3 /*break*/, 5];\n case 4:\n if (delay_1 !== true) {\n throw new Error(CALLBACK_INVALID_RETURN);\n }\n _b.label = 5;\n case 5: return [3 /*break*/, 6];\n case 6: return [2 /*return*/];\n }\n });\n };\n _a.label = 1;\n case 1:\n if (!(attempt += 1) /* istanbul ignore next: condition is always true */) return [3 /*break*/, 3];\n return [5 /*yield**/, _loop_1()];\n case 2:\n state_1 = _a.sent();\n if (typeof state_1 === \"object\")\n return [2 /*return*/, state_1.value];\n return [3 /*break*/, 1];\n case 3: return [2 /*return*/];\n }\n });\n });\n };\n}\n/* harmony default export */ __webpack_exports__[\"default\"] = (promiseEndeavour);\n//# sourceMappingURL=promiseEndeavour.js.map\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 288 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst logging = __webpack_require__(0)('videoContentHint');\n\nconst videoContentHints = ['', 'text', 'motion', 'detail'];\n\nconst isValidVideoContentHint = hint => videoContentHints.includes(hint);\n\nconst getVideoTrackForStream = mediaStream => {\n var _mediaStream$getVideo;\n\n return mediaStream == null ? void 0 : (_mediaStream$getVideo = mediaStream.getVideoTracks()) == null ? void 0 : _mediaStream$getVideo[0];\n};\n/**\n * Sets contentHint for videoTrack of MediaStream\n * @param {MediaStream} mediaStream\n * @param {String} contentHint, one of \"\", \"motion\", \"detail\" or \"text\"\n */\n\n\nconst setVideoContentHint = (mediaStream, contentHint) => {\n if (!isValidVideoContentHint(contentHint)) {\n logging.warn('Invalid content hint. Valid content hints are \"text\", \"detail\", \"motion\", or \"\"');\n return;\n }\n\n const videoTrack = getVideoTrackForStream(mediaStream);\n\n if (!videoTrack) {\n logging.warn('Tried to set contentHint but no video track is present');\n return;\n }\n\n if (videoTrack.contentHint === undefined) {\n logging.warn('contentHint not supported by this browser');\n return;\n }\n\n videoTrack.contentHint = contentHint;\n};\n/**\n * Returns contentHint for videoTrack of MediaStream\n * @param {MediaStream} mediaStream\n * @returns {String} contentHint, one of \"\", \"motion\", \"detail\" or \"text\"\n\n */\n\n\nconst getVideoContentHint = mediaStream => {\n const videoTrack = getVideoTrackForStream(mediaStream);\n return (videoTrack == null ? void 0 : videoTrack.contentHint) || '';\n};\n\nmodule.exports = {\n isValidVideoContentHint,\n setVideoContentHint,\n getVideoContentHint\n};\n\n/***/ }),\n/* 289 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require, no-underscore-dangle, no-shadow */\nconst defineProperties = __webpack_require__(104);\n\nmodule.exports = function MicrophoneFactory() {\n /*\n * A Publishers Microphone.\n *\n * TODO\n * * bind to changes in mute/unmute/volume/etc and respond to them\n */\n return function Microphone(webRTCStream, muted) {\n let _muted;\n\n defineProperties(this, {\n muted: {\n get() {\n return _muted;\n },\n\n set(muted) {\n if (_muted === muted) {\n return;\n }\n\n _muted = muted;\n const audioTracks = webRTCStream.getAudioTracks();\n\n for (let i = 0, num = audioTracks.length; i < num; ++i) {\n audioTracks[i].enabled = !_muted;\n }\n }\n\n }\n }); // Set the initial value\n\n if (muted !== undefined) {\n this.muted(muted === true);\n } else if (webRTCStream.getAudioTracks().length) {\n this.muted(!webRTCStream.getAudioTracks()[0].enabled);\n } else {\n this.muted(false);\n }\n };\n};\n\n/***/ }),\n/* 290 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require */\nmodule.exports = function videoElementErrorMapFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const Errors = deps.Errors || __webpack_require__(6);\n\n const otError = deps.otError || __webpack_require__(11)(); // See http://www.w3.org/TR/2010/WD-html5-20101019/video.html#error-codes\n\n\n const videoErrorDetails = {}; // Checking for global.MediaError for IE compatibility, just so we don't throw\n // exceptions when the script is included\n\n if ((typeof window !== undefined ? window : global).MediaError) {\n videoErrorDetails[(typeof window !== undefined ? window : global).MediaError.MEDIA_ERR_ABORTED] = {\n name: Errors.MEDIA_ERR_ABORTED,\n message: 'The fetching process for the media resource was aborted by the ' + 'user agent at the user\\'s request.'\n };\n videoErrorDetails[(typeof window !== undefined ? window : global).MediaError.MEDIA_ERR_NETWORK] = {\n name: Errors.MEDIA_ERR_NETWORK,\n message: 'A network error of some description caused the user agent to ' + 'stop fetching the media resource, after the resource was established ' + 'to be usable.'\n };\n videoErrorDetails[(typeof window !== undefined ? window : global).MediaError.MEDIA_ERR_DECODE] = {\n name: Errors.MEDIA_ERR_DECODE,\n message: 'An error of some description occurred while decoding the media ' + 'resource, after the resource was established to be usable.'\n };\n videoErrorDetails[(typeof window !== undefined ? window : global).MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED] = {\n name: Errors.MEDIA_ERR_SRC_NOT_SUPPORTED,\n message: 'The media resource indicated by the src attribute was not ' + 'suitable.'\n };\n }\n\n return function videoElementErrorMap(error) {\n const errorDetails = videoErrorDetails[error.code];\n\n if (!errorDetails) {\n return new Error(`An unknown error occurred${error.message ? `: ${error.message}` : '.'}`);\n }\n\n return otError(errorDetails.name, new Error(`There was an unexpected problem with the Video Stream: ${errorDetails.message}`));\n };\n};\n\n/***/ }),\n/* 291 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\nvar _defineProperty2 = _interopRequireDefault(__webpack_require__(16));\n\nvar _regenerator = _interopRequireDefault(__webpack_require__(18));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require, one-var, no-underscore-dangle */\n\n/* eslint-disable no-cond-assign, max-len, no-void, prefer-const */\nconst eventing = __webpack_require__(5);\n\nconst eventHelper = __webpack_require__(61);\n\nconst destroyObj = __webpack_require__(626);\n\nconst patchSrcObject = __webpack_require__(627);\n\nconst isIos = __webpack_require__(73);\n\nconst isIosSafari = __webpack_require__(628);\n\nconst isDomElementVisible = __webpack_require__(629);\n\nconst promiseDelay = __webpack_require__(106);\n\nconst env = __webpack_require__(2);\n\nconst iOSVersion = __webpack_require__(292);\n\nfunction createDomVideoElement(fallbackText, muted) {\n const videoElement = document.createElement('video');\n patchSrcObject(videoElement);\n videoElement.autoplay = true;\n videoElement.playsinline = true;\n videoElement.innerHTML = fallbackText; // Safari on iOS requires setAttribute OPENTOK-37229\n\n videoElement.setAttribute('autoplay', '');\n videoElement.setAttribute('playsinline', '');\n\n if (muted === true) {\n videoElement.muted = 'true';\n }\n\n return videoElement;\n}\n\nfunction createDomAudioOnlyVideoElement() {\n const audioOnlyVideoElement = createDomVideoElement('');\n audioOnlyVideoElement.setAttribute('style', 'display:none');\n return audioOnlyVideoElement;\n}\n\nconst noop = /*#__PURE__*/function () {\n var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {\n return _regenerator.default.wrap(function _callee$(_context) {\n while (1) switch (_context.prev = _context.next) {\n case 0:\n return _context.abrupt(\"return\", new Promise(resolve => setTimeout(resolve)));\n\n case 1:\n case \"end\":\n return _context.stop();\n }\n }, _callee);\n }));\n\n return function noop() {\n return _ref.apply(this, arguments);\n };\n}();\n/**\n * NativeVideoElementWrapperFactory DI container\n *\n * @package\n * @param {any} [deps={}]\n */\n\n\nfunction NativeVideoElementWrapperFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const audioContextProvider = deps.audioContext || __webpack_require__(157)();\n\n const canBeOrientatedMixin = deps.canBeOrientatedMixin || __webpack_require__(630);\n /** @type {Document} */\n\n\n const document = deps.document || (typeof window !== undefined ? window : global).document;\n\n const listenForTracksEnded = deps.listenForTracksEnded || __webpack_require__(631)();\n\n const createLog = deps.logging || __webpack_require__(0);\n\n const OTHelpers = deps.OTHelpers || __webpack_require__(4);\n\n const videoElementErrorMap = deps.videoElementErrorMap || __webpack_require__(290)();\n\n const WebAudioLevelSampler = deps.WebaudioAudioLevelSampler || __webpack_require__(145);\n\n const windowMock = deps.global || (typeof window !== undefined ? window : global);\n let id = 1;\n /**\n * NativeVideoElementWrapper\n *\n * @package\n * @class\n * @param {Object} options\n * @param {Object} [options._inject] injected variables @todo move to DI\n * @param {Function} [options._inject.createVideoElement] function used to create video element\n * @param {Function} [options._inject.createAudioOnlyVideoElement] function used to create audio only video element\n * @param {Boolean} [options.muted] initial mute state\n * @param {String} [options.fallbackText] text to display when not supported\n * @param {Function} errorHandler callback for when there are errors\n * @param {Number} defaultAudioVolume the initial audio volume\n */\n\n let NativeVideoElementWrapper = /*#__PURE__*/function () {\n /** @type {HTMLVideoElement|undefined} */\n\n /** @type {number|undefined} */\n function NativeVideoElementWrapper(_ref2, defaultAudioVolume) {\n var _this = this;\n\n let _ref2$_inject = _ref2._inject,\n _ref2$_inject2 = _ref2$_inject === void 0 ? {} : _ref2$_inject,\n _ref2$_inject2$create = _ref2$_inject2.createVideoElement,\n _createVideoElement = _ref2$_inject2$create === void 0 ? createDomVideoElement : _ref2$_inject2$create,\n _ref2$_inject2$create2 = _ref2$_inject2.createAudioOnlyVideoElement,\n _createAudioOnlyVideoElement = _ref2$_inject2$create2 === void 0 ? createDomAudioOnlyVideoElement : _ref2$_inject2$create2,\n muted = _ref2.muted,\n fallbackText = _ref2.fallbackText,\n widgetType = _ref2.widgetType;\n\n (0, _defineProperty2.default)(this, \"_domElement\", void 0);\n (0, _defineProperty2.default)(this, \"_domAudioOnlyVideoElement\", void 0);\n (0, _defineProperty2.default)(this, \"_blockedVolume\", void 0);\n (0, _defineProperty2.default)(this, \"_mediaStoppedListener\", void 0);\n (0, _defineProperty2.default)(this, \"_audioLevelSampler\", void 0);\n (0, _defineProperty2.default)(this, \"_playInterrupts\", 0);\n (0, _defineProperty2.default)(this, \"_isVideoLoadedMetaData\", false);\n (0, _defineProperty2.default)(this, \"_audioOnlyVideoElementWatcher\", void 0);\n this.logging = createLog(`NativeVideoElementWrapper:${id}`);\n id += 1;\n eventing(this);\n this._defaultAudioVolume = defaultAudioVolume;\n this._widgetType = widgetType;\n let _videoElementMovedWarning = false; // / Private API\n // The video element pauses itself when it's reparented, this is\n // unfortunate. This function plays the video again and is triggered\n // on the pause event.\n\n const _playVideoOnPause = () => {\n if (!_videoElementMovedWarning) {\n this.logging.warn('Video element paused, auto-resuming. If you intended to do this, ' + 'use publishVideo(false) or subscribeToVideo(false) instead.');\n _videoElementMovedWarning = true;\n }\n\n this.play();\n };\n\n this._domElement = _createVideoElement(fallbackText, muted);\n this.trigger('videoElementCreated', this._domElement);\n this._domElementEvents = eventHelper(this._domElement);\n\n this._domElementEvents.on('timeupdate', function () {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return _this.trigger('timeupdate', ...args);\n });\n\n this._domElementEvents.on('loadedmetadata', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {\n var _len2,\n args,\n _key2,\n hasSrcObject,\n _args2 = arguments;\n\n return _regenerator.default.wrap(function _callee2$(_context2) {\n while (1) switch (_context2.prev = _context2.next) {\n case 0:\n _this._isVideoLoadedMetaData = true;\n\n for (_len2 = _args2.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n args[_key2] = _args2[_key2];\n }\n\n _this.trigger('loadedmetadata', ...args); // We can safely remove the audio only element since the video element\n // could trigger the loadedmetada, meaning it can start playing video.\n // For more info, see OPENTOK-41739.\n\n\n if (!_this._domAudioOnlyVideoElement) {\n _context2.next = 10;\n break;\n }\n\n hasSrcObject = _this._domAudioOnlyVideoElement.srcObject;\n\n _this._removeAudioOnlyVideoElement(); // We need to call play only if _domAudioOnlyVideoElement had\n // a srcObject, meaning it was used for audio.\n\n\n if (!hasSrcObject) {\n _context2.next = 10;\n break;\n }\n\n _context2.next = 9;\n return noop();\n\n case 9:\n _this.play();\n\n case 10:\n case \"end\":\n return _context2.stop();\n }\n }, _callee2);\n })));\n\n const onError = event => {\n this.trigger('error', videoElementErrorMap(event.target.error));\n };\n\n this._domElementEvents.on('error', onError, false);\n\n this._domElementEvents.on('pause', _playVideoOnPause);\n\n this.on('destroyed', () => {\n this._domElementEvents.removeAll();\n\n if (this._domAudioOnlyVideoElementEvents) {\n this._domAudioOnlyVideoElementEvents.removeAll();\n }\n });\n canBeOrientatedMixin(this, () => this._domElement); // Safari subscribers needs special handling for audio-only video\n // https://bugs.webkit.org/show_bug.cgi?id=174152\n\n if (env.name === 'Safari' && this._widgetType === 'subscriber') {\n this._domAudioOnlyVideoElement = _createAudioOnlyVideoElement();\n\n this._bindAudioOnlyVideoElementEvents();\n }\n }\n\n var _proto = NativeVideoElementWrapper.prototype;\n\n _proto._rebindSrcObject = function _rebindSrcObject() {\n if (this._domElement) {\n this._domElement.srcObject = this._domElement.srcObject;\n }\n };\n\n _proto._pauseAndPlay = function _pauseAndPlay() {\n if (this._domElement) {\n this._domElement.pause();\n\n this._domElement.play();\n }\n };\n\n _proto._startAudioOnlyVideoElementWatcher = /*#__PURE__*/function () {\n var _startAudioOnlyVideoElementWatcher2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {\n var watcherIntervalTimeout, shouldPlay;\n return _regenerator.default.wrap(function _callee3$(_context3) {\n while (1) switch (_context3.prev = _context3.next) {\n case 0:\n // In Safari, after setting the srcObject the video element gets paused.\n // Calling play() is not sufficient to unpause it.\n // A workaround for this is to rebind when we detect that the video gets paused.\n // Notice that if it's iOS we need to call play() after the rebind, otherwise\n // it won't play.\n watcherIntervalTimeout = 1000;\n shouldPlay = isIos();\n this._audioOnlyVideoElementWatcher = setInterval(() => {\n if (this._domAudioOnlyVideoElement && this._domAudioOnlyVideoElement.paused) {\n this._domAudioOnlyVideoElement.srcObject = this._domAudioOnlyVideoElement.srcObject;\n\n if (shouldPlay) {\n this._domAudioOnlyVideoElement.play();\n }\n }\n }, 100);\n _context3.next = 5;\n return promiseDelay(watcherIntervalTimeout);\n\n case 5:\n clearInterval(this._audioOnlyVideoElementWatcher);\n this._audioOnlyVideoElementWatcher = null;\n\n case 7:\n case \"end\":\n return _context3.stop();\n }\n }, _callee3, this);\n }));\n\n function _startAudioOnlyVideoElementWatcher() {\n return _startAudioOnlyVideoElementWatcher2.apply(this, arguments);\n }\n\n return _startAudioOnlyVideoElementWatcher;\n }();\n\n _proto._removeAudioOnlyVideoElement = /*#__PURE__*/function () {\n var _removeAudioOnlyVideoElement2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {\n return _regenerator.default.wrap(function _callee4$(_context4) {\n while (1) switch (_context4.prev = _context4.next) {\n case 0:\n this._domAudioOnlyVideoElement.srcObject = null;\n\n this._domAudioOnlyVideoElement.parentNode.removeChild(this._domAudioOnlyVideoElement);\n\n if (this._domAudioOnlyVideoElementEvents) {\n this._domAudioOnlyVideoElementEvents.removeAll();\n }\n\n this._domAudioOnlyVideoElement = null;\n\n case 4:\n case \"end\":\n return _context4.stop();\n }\n }, _callee4, this);\n }));\n\n function _removeAudioOnlyVideoElement() {\n return _removeAudioOnlyVideoElement2.apply(this, arguments);\n }\n\n return _removeAudioOnlyVideoElement;\n }();\n\n _proto.bindAudioTrackOnly = /*#__PURE__*/function () {\n var _bindAudioTrackOnly = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() {\n var videoTracks, audioTracks, _videoTracks$, videoTrack, _audioTracks$, audioTrack, isAudioOnlyVideo;\n\n return _regenerator.default.wrap(function _callee5$(_context5) {\n while (1) switch (_context5.prev = _context5.next) {\n case 0:\n videoTracks = this._stream.getVideoTracks();\n audioTracks = this._stream.getAudioTracks();\n _videoTracks$ = videoTracks[0], videoTrack = _videoTracks$ === void 0 ? {} : _videoTracks$;\n _audioTracks$ = audioTracks[0], audioTrack = _audioTracks$ === void 0 ? {} : _audioTracks$;\n _context5.next = 6;\n return noop();\n\n case 6:\n isAudioOnlyVideo = !videoTrack.enabled && audioTrack.enabled;\n\n if (!this._isVideoLoadedMetaData && isAudioOnlyVideo && this._domAudioOnlyVideoElement) {\n this._domAudioOnlyVideoElement.srcObject = new windowMock.MediaStream(audioTracks);\n\n this._startAudioOnlyVideoElementWatcher();\n }\n\n case 8:\n case \"end\":\n return _context5.stop();\n }\n }, _callee5, this);\n }));\n\n function bindAudioTrackOnly() {\n return _bindAudioTrackOnly.apply(this, arguments);\n }\n\n return bindAudioTrackOnly;\n }();\n\n _proto._bindAudioOnlyVideoElementEvents = function _bindAudioOnlyVideoElementEvents() {\n var _this2 = this;\n\n this._domAudioOnlyVideoElementEvents = eventHelper(this._domAudioOnlyVideoElement); // Safari may stop playing audio if the browser is not visible or losses\n // focus. When it does, continue playing it.\n\n this._domAudioOnlyVideoElementEvents.on('pause', () => this.play());\n\n this._domAudioOnlyVideoElementEvents.on('timeupdate', function () {\n for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {\n args[_key3] = arguments[_key3];\n }\n\n return _this2.trigger('timeupdate', ...args);\n });\n\n this._domAudioOnlyVideoElementEvents.on('loadedmetadata', function () {\n for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n\n return _this2.trigger('loadedmetadata', ...args);\n });\n };\n\n _proto.whenTimeIncrements = function whenTimeIncrements(callback, context) {\n this.once('timeupdate', () => {\n callback.call(context, this);\n });\n }\n /**\n * Get the underlying DOM video element\n * @return {Element}\n */\n ;\n\n _proto.domElement = function domElement() {\n return this._domElement;\n };\n\n _proto.videoWidth = function videoWidth() {\n return this._domElement ? Number(this._domElement[`video${this.isRotated() ? 'Height' : 'Width'}`]) : 0;\n };\n\n _proto.videoHeight = function videoHeight() {\n return this._domElement ? Number(this._domElement[`video${this.isRotated() ? 'Width' : 'Height'}`]) : 0;\n };\n\n _proto.aspectRatio = function aspectRatio() {\n return this.videoWidth() / this.videoHeight();\n };\n\n _proto.imgData = function imgData() {\n const canvas = OTHelpers.createElement('canvas', {\n width: this.videoWidth(),\n height: this.videoHeight(),\n style: {\n display: 'none'\n }\n });\n document.body.appendChild(canvas);\n let imgData = null;\n\n try {\n canvas.getContext('2d').drawImage(this._domElement, 0, 0, canvas.width, canvas.height);\n imgData = canvas.toDataURL('image/png');\n } catch (err) {// Warning emitted for imgData === null below\n }\n\n OTHelpers.removeElement(canvas);\n\n if (imgData === null || imgData === 'data:,') {\n // 'data:,' is sometimes returned by canvas.toDataURL when one cannot be\n // generated.\n this.logging.warn('Cannot get image data yet');\n return null;\n }\n\n return imgData.replace('data:image/png;base64,', '').trim();\n } // Append the Video DOM element to a parent node\n ;\n\n _proto.appendTo = function appendTo(parentDomElement) {\n parentDomElement.appendChild(this._domElement);\n\n if (this._domAudioOnlyVideoElement) {\n parentDomElement.appendChild(this._domAudioOnlyVideoElement);\n }\n\n return this;\n };\n\n _proto.isAudioBlocked = function isAudioBlocked() {\n return this._blockedVolume !== undefined;\n };\n\n _proto.unblockAudio = /*#__PURE__*/function () {\n var _unblockAudio = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {\n var blockedVolume;\n return _regenerator.default.wrap(function _callee6$(_context6) {\n while (1) switch (_context6.prev = _context6.next) {\n case 0:\n if (this.isAudioBlocked()) {\n _context6.next = 3;\n break;\n }\n\n this.logging.warn('Unexpected call to unblockAudio() without blocked audio');\n return _context6.abrupt(\"return\");\n\n case 3:\n blockedVolume = this._blockedVolume;\n this._blockedVolume = undefined;\n this.setAudioVolume(blockedVolume);\n _context6.prev = 6;\n _context6.next = 9;\n return this.play();\n\n case 9:\n _context6.next = 16;\n break;\n\n case 11:\n _context6.prev = 11;\n _context6.t0 = _context6[\"catch\"](6);\n this._blockedVolume = blockedVolume;\n this._domElement.muted = true;\n throw _context6.t0;\n\n case 16:\n this.trigger('audioUnblocked');\n\n case 17:\n case \"end\":\n return _context6.stop();\n }\n }, _callee6, this, [[6, 11]]);\n }));\n\n function unblockAudio() {\n return _unblockAudio.apply(this, arguments);\n }\n\n return unblockAudio;\n }() // useful for some browsers (old chrome) that show black when a peer connection\n // is renegotiated\n ;\n\n _proto.rebind =\n /*#__PURE__*/\n function () {\n var _rebind = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7() {\n return _regenerator.default.wrap(function _callee7$(_context7) {\n while (1) switch (_context7.prev = _context7.next) {\n case 0:\n if (this._domElement) {\n _context7.next = 2;\n break;\n }\n\n throw new Error('Can\\'t rebind because _domElement no longer exists');\n\n case 2:\n this._playInterrupts++;\n this._domElement.srcObject = this._domElement.srcObject;\n\n if (this._hasAudioOnlyVideoElement()) {\n this._domAudioOnlyVideoElement.srcObject = this._domAudioOnlyVideoElement.srcObject;\n }\n\n case 5:\n case \"end\":\n return _context7.stop();\n }\n }, _callee7, this);\n }));\n\n function rebind() {\n return _rebind.apply(this, arguments);\n }\n\n return rebind;\n }();\n\n _proto._createAudioLevelSampler = function _createAudioLevelSampler() {\n this._removeAudioLevelSampler();\n\n if (this._stream.getAudioTracks().length > 0) {\n try {\n this._audioContext = audioContextProvider();\n } catch (e) {\n this.logging.warn('Failed to get AudioContext(), audio level visualisation will not work', e);\n }\n\n if (this._audioContext) {\n this._audioLevelSampler = new WebAudioLevelSampler(this._audioContext);\n\n this._audioLevelSampler.webRTCStream(this._stream);\n }\n }\n };\n\n _proto._removeAudioLevelSampler = function _removeAudioLevelSampler() {\n if (this._audioContext) {\n delete this._audioContext;\n delete this._audioLevelSampler;\n }\n } // For MediaStreams with multiple tracks, video tracks are disabled and\n // then re-enabled\n ;\n\n _proto._toggleVideoTracks =\n /*#__PURE__*/\n function () {\n var _toggleVideoTracks2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() {\n var enabledTracks, hasMultipleTracks, videoTracks;\n return _regenerator.default.wrap(function _callee8$(_context8) {\n while (1) switch (_context8.prev = _context8.next) {\n case 0:\n enabledTracks = this._domElement.srcObject.getTracks().filter(track => track.enabled);\n hasMultipleTracks = enabledTracks.length > 1;\n\n if (!hasMultipleTracks) {\n _context8.next = 8;\n break;\n }\n\n videoTracks = enabledTracks.filter(track => track.kind === 'video');\n videoTracks.forEach(track => {\n track.enabled = false;\n });\n _context8.next = 7;\n return noop();\n\n case 7:\n videoTracks.forEach(track => {\n track.enabled = true;\n });\n\n case 8:\n case \"end\":\n return _context8.stop();\n }\n }, _callee8, this);\n }));\n\n function _toggleVideoTracks() {\n return _toggleVideoTracks2.apply(this, arguments);\n }\n\n return _toggleVideoTracks;\n }();\n\n _proto._hasAudioOnlyVideoElement = function _hasAudioOnlyVideoElement() {\n return this._domAudioOnlyVideoElement && this._domAudioOnlyVideoElement.srcObject;\n };\n\n _proto.play = /*#__PURE__*/function () {\n var _play = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9() {\n var playInterruptsOnStart, isSafari, domElement, version, isBuggediOS, _this$_stream$getVide, _this$_stream$getVide2, videoTrack, _this$_stream$getAudi, _this$_stream$getAudi2, audioTrack, _ref4, errorName, isAbortError;\n\n return _regenerator.default.wrap(function _callee9$(_context9) {\n while (1) switch (_context9.prev = _context9.next) {\n case 0:\n playInterruptsOnStart = this._playInterrupts;\n isSafari = env.name === 'Safari'; // Which element do we try to play? The `audio` or `video` element?\n\n domElement = this._hasAudioOnlyVideoElement() ? this._domAudioOnlyVideoElement : this._domElement; // In Safari, if a HTMLMediaElement property is mutated or if the element is moved,\n // a delay is needed else element may fail to play\n\n if (!isSafari) {\n _context9.next = 6;\n break;\n }\n\n _context9.next = 6;\n return noop();\n\n case 6:\n if (!isIosSafari()) {\n _context9.next = 17;\n break;\n }\n\n if (!(this._widgetType === 'publisher')) {\n _context9.next = 16;\n break;\n }\n\n version = parseFloat(iOSVersion());\n isBuggediOS = version >= 14 && version <= 14.2;\n _this$_stream$getVide = this._stream.getVideoTracks(), _this$_stream$getVide2 = _this$_stream$getVide[0], videoTrack = _this$_stream$getVide2 === void 0 ? {} : _this$_stream$getVide2;\n _this$_stream$getAudi = this._stream.getAudioTracks(), _this$_stream$getAudi2 = _this$_stream$getAudi[0], audioTrack = _this$_stream$getAudi2 === void 0 ? {} : _this$_stream$getAudi2;\n /**\n * OPENTOK-41742: Prevent to call play() on a hidden video element. Otherwise the\n * readyState of the MediaStreamTrack will be ended and the publisher will stop\n * publishing video.\n * This issue happens only on iOS Safari >= 14.0 and <= 14.2\n * */\n\n if (!(isBuggediOS && !isDomElementVisible(domElement) && videoTrack.enabled)) {\n _context9.next = 14;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 14:\n if (!audioTrack.muted) {\n _context9.next = 16;\n break;\n }\n\n return _context9.abrupt(\"return\");\n\n case 16:\n // OPENTOK-39347: Toggle video tracks, else audio maybe garbled\n this._toggleVideoTracks();\n\n case 17:\n _context9.prev = 17;\n _context9.next = 20;\n return domElement.play();\n\n case 20:\n return _context9.abrupt(\"return\");\n\n case 23:\n _context9.prev = 23;\n _context9.t0 = _context9[\"catch\"](17);\n _ref4 = _context9.t0 || {}, errorName = _ref4.name;\n isAbortError = errorName === 'AbortError'; // Play request was interrupted\n\n if (isAbortError) {\n this._playInterrupts++;\n }\n\n if (!(this._playInterrupts > playInterruptsOnStart)) {\n _context9.next = 32;\n break;\n }\n\n _context9.next = 31;\n return this.play();\n\n case 31:\n return _context9.abrupt(\"return\");\n\n case 32:\n throw _context9.t0;\n\n case 33:\n case \"end\":\n return _context9.stop();\n }\n }, _callee9, this, [[17, 23]]);\n }));\n\n function play() {\n return _play.apply(this, arguments);\n }\n\n return play;\n }();\n\n _proto._bindToStream = function _bindToStream(webRtcStream) {\n this._stream = webRtcStream;\n this._domElement.srcObject = webRtcStream; // Audio-only video won't play in Safari since the browser won't dispatch\n // the `loadedmetadata` event (since the video has unknown dimensions)\n // https://bugs.webkit.org/show_bug.cgi?id=174152\n\n if (this._domAudioOnlyVideoElement) {\n this.bindAudioTrackOnly();\n }\n };\n\n _proto.bindToStream = /*#__PURE__*/function () {\n var _bindToStream2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11(webRtcStream) {\n var _this3 = this;\n\n var currentVideoSize, onAllEnded, onSingleEnded;\n return _regenerator.default.wrap(function _callee11$(_context11) {\n while (1) switch (_context11.prev = _context11.next) {\n case 0:\n if (this._domElement) {\n _context11.next = 2;\n break;\n }\n\n throw new Error('Can\\'t bind because _domElement no longer exists');\n\n case 2:\n this._bindToStream(webRtcStream);\n\n (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10() {\n var hasAudio;\n return _regenerator.default.wrap(function _callee10$(_context10) {\n while (1) switch (_context10.prev = _context10.next) {\n case 0:\n _context10.prev = 0;\n _context10.next = 3;\n return _this3.play();\n\n case 3:\n _context10.next = 26;\n break;\n\n case 5:\n _context10.prev = 5;\n _context10.t0 = _context10[\"catch\"](0);\n hasAudio = !!webRtcStream.getAudioTracks().length;\n\n if (!(_this3._hasAudioOnlyVideoElement() && _context10.t0.name === 'NotAllowedError')) {\n _context10.next = 15;\n break;\n }\n\n // Audio-only video failed to play. Mute audio and try again.\n _this3._domAudioOnlyVideoElement.muted = true;\n _context10.next = 12;\n return _this3.play();\n\n case 12:\n // Now that audio-only stream is playing, unmute it.\n _this3._domAudioOnlyVideoElement.muted = false;\n _context10.next = 26;\n break;\n\n case 15:\n if (!hasAudio) {\n _context10.next = 23;\n break;\n }\n\n // Media (with audio) failed to play. Mute audio and try again.\n _this3._blockedVolume = _this3.getAudioVolume();\n _this3._domElement.muted = true;\n _context10.next = 20;\n return _this3.play();\n\n case 20:\n _this3.trigger('audioBlocked');\n\n _context10.next = 26;\n break;\n\n case 23:\n // Media failed to play. Mute audio, even though there's no audio\n // track (e.g., a screenshare), and try again.\n _this3._domElement.muted = true;\n _context10.next = 26;\n return _this3.play();\n\n case 26:\n case \"end\":\n return _context10.stop();\n }\n }, _callee10, null, [[0, 5]]);\n }))().catch(err => {\n this.logging.debug('.play() failed: ', err);\n });\n currentVideoSize = {\n width: this._domElement.videoWidth,\n height: this._domElement.videoHeight\n };\n this.trigger('videoDimensionsChanged', (0, _extends2.default)({}, currentVideoSize), (0, _extends2.default)({}, currentVideoSize));\n\n this._domElementEvents.on('resize', () => {\n const _this$_domElement = this._domElement,\n width = _this$_domElement.videoWidth,\n height = _this$_domElement.videoHeight;\n const widthChanged = width !== currentVideoSize.width;\n const heightChanged = height !== currentVideoSize.height;\n\n if (widthChanged || heightChanged) {\n this.trigger('videoDimensionsChanged', (0, _extends2.default)({}, currentVideoSize), {\n width,\n height\n });\n currentVideoSize.width = width;\n currentVideoSize.height = height;\n }\n });\n\n onAllEnded = () => {\n this._mediaStoppedListener.stop();\n\n if (this._domElement) {\n this._domElement.onended = null;\n }\n\n this.trigger('mediaStopped');\n };\n\n onSingleEnded = track => {\n this.trigger('mediaStopped', track);\n }; // OPENTOK-22428: Firefox doesn't emit the ended event on the webRtcStream when the user\n // stops sharing their camera, but we do get the ended event on the video element.\n // TODO: should this apply to all elements?\n\n\n this._domElement.onended = () => onAllEnded();\n\n this._mediaStoppedListener = listenForTracksEnded(webRtcStream, onAllEnded, onSingleEnded);\n\n this._createAudioLevelSampler();\n\n return _context11.abrupt(\"return\", undefined);\n\n case 13:\n case \"end\":\n return _context11.stop();\n }\n }, _callee11, this);\n }));\n\n function bindToStream(_x) {\n return _bindToStream2.apply(this, arguments);\n }\n\n return bindToStream;\n }() // Unbind the currently bound stream from the video element.\n ;\n\n _proto.unbindStream = function unbindStream() {\n if (this._domElement) {\n this._domElement.srcObject = null;\n }\n\n if (this._domAudioOnlyVideoElement) {\n this._domAudioOnlyVideoElement.srcObject = null;\n }\n\n this._removeAudioLevelSampler();\n\n return this;\n };\n\n _proto.setAudioVolume = function setAudioVolume(rawValue) {\n if (this.isAudioBlocked()) {\n this._blockedVolume = rawValue;\n return;\n }\n\n const value = parseFloat(rawValue) / 100;\n const domElements = [this.domElement()];\n\n if (this._domAudioOnlyVideoElement) {\n domElements.push(this._domAudioOnlyVideoElement);\n }\n\n domElements.forEach(domElement => {\n if (domElement) {\n domElement.volume = value; // In Safari on iOS setting the volume does not work but setting muted does so at\n // least this will mute when you click the mute icon\n // https://bugs.webkit.org/show_bug.cgi?id=176045\n\n domElement.muted = value === 0;\n }\n });\n };\n\n _proto.getAudioVolume = function getAudioVolume() {\n if (this.isAudioBlocked()) {\n return this._blockedVolume;\n } // Return the actual volume of the DOM element\n\n\n const domElement = this.domElement();\n\n if (domElement) {\n return domElement.muted ? 0 : domElement.volume * 100;\n }\n\n return this._defaultAudioVolume;\n } // see https://wiki.mozilla.org/WebAPI/AudioChannels\n // The audioChannelType is currently only available in Firefox. This property returns\n // \"unknown\" in other browser. The related HTML tag attribute is \"mozaudiochannel\"\n ;\n\n _proto.audioChannelType = function audioChannelType(type) {\n const domElement = this.domElement();\n\n if (type !== void 0) {\n domElement.mozAudioChannelType = type;\n }\n\n if ('mozAudioChannelType' in this._domElement) {\n return domElement.mozAudioChannelType;\n }\n\n return 'unknown';\n };\n\n _proto.getAudioInputLevel = function getAudioInputLevel() {\n return this._audioLevelSampler.sample();\n };\n\n _proto.refreshTracks = function refreshTracks() {\n if (this._mediaStoppedListener) {\n this._mediaStoppedListener.refresh();\n }\n\n this._createAudioLevelSampler();\n\n if (this._stream && this._stream.getTracks().length > 0) {\n this._playInterrupts++;\n const domElement = this.domElement();\n domElement.srcObject = new windowMock.MediaStream(this._stream.getTracks());\n }\n };\n\n _proto.destroy = function destroy() {\n // Unbind events first, otherwise 'pause' will trigger when the\n // video element is removed from the DOM.\n if (this._mediaStoppedListener) {\n this._mediaStoppedListener.stop();\n }\n\n this.logging.debug('removing domElementEvents');\n\n this._domElementEvents.removeAll();\n\n if (this._domAudioOnlyVideoElementEvents) {\n this._domAudioOnlyVideoElementEvents.removeAll();\n }\n\n if (this._audioOnlyVideoElementWatcher) {\n clearInterval(this._audioOnlyVideoElementWatcher);\n }\n\n this.unbindStream();\n\n if (this._domElement) {\n OTHelpers.removeElement(this._domElement);\n this._domElement = null;\n }\n\n if (this._domAudioOnlyVideoElement) {\n OTHelpers.removeElement(this._domAudioOnlyVideoElement);\n this._domAudioOnlyVideoElement = null;\n }\n\n this.trigger('destroyed');\n destroyObj('NativeVideoElementWrapper', this);\n return void 0;\n };\n\n return NativeVideoElementWrapper;\n }();\n\n return NativeVideoElementWrapper;\n}\n\nmodule.exports = NativeVideoElementWrapperFactory;\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 292 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function iOSVersion() {\n const match = navigator.userAgent.match(/OS (\\d+_\\d+) like Mac/);\n\n if (match === null || !match[1]) {\n return undefined;\n }\n\n return match[1].replace('_', '.');\n};\n\n/***/ }),\n/* 293 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require, no-underscore-dangle, func-names */\nconst assign = __webpack_require__(7);\n\nconst pick = __webpack_require__(45);\n\nconst find = __webpack_require__(60);\n\nconst eventing = __webpack_require__(5);\n\nmodule.exports = function PublisherPeerConnectionFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const getStatsHelpers = deps.getStatsHelpers || __webpack_require__(75);\n\n const OTHelpers = deps.OTHelpers || __webpack_require__(4);\n\n const PeerConnection = deps.PeerConnection || __webpack_require__(101)();\n\n const setCertificates = deps.setCertificates || __webpack_require__(162)();\n\n const watchAudioAcquisition = deps.watchAudioAcquisition || __webpack_require__(633);\n /**\n * @typedef {object} PublisherPeerConnectionConfig\n * @property {function(string, string=, object=, object=, boolean=)} logAnalyticsEvent\n * @property {Connection} remoteConnection\n * @property {boolean} reconnection\n * @property {MediaStream} webRTCStream\n * @property {object} channels\n * @property {boolean} capableSimulcastStreams\n * @property {boolean} overrideSimulcastEnabled\n * @property {string} subscriberUri\n * @property {object} offerOverrides\n * @property {object} answerOverrides\n * @property {string} sourceStreamId\n * @property {string} isP2pEnabled\n */\n\n /**\n * Abstracts PeerConnection related stuff away from Publisher.\n *\n * Responsible for:\n * * setting up the underlying PeerConnection (delegates to PeerConnections)\n * * triggering a connected event when the Peer connection is opened\n * * triggering a disconnected event when the Peer connection is closed\n * * providing a destroy method\n * * providing a processMessage method\n *\n * Once the PeerConnection is connected and the video element playing it triggers\n * the connected event\n *\n * Triggers the following events\n * * connected\n * * disconnected\n *\n * @class PublisherPeerConnection\n * @constructor\n *\n * @param {PublisherPeerConnectionConfig} config\n */\n\n\n return function PublisherPeerConnection(_ref) {\n let iceConfig = _ref.iceConfig,\n webRTCStream = _ref.webRTCStream,\n channels = _ref.channels,\n sendMessage = _ref.sendMessage,\n capableSimulcastStreams = _ref.capableSimulcastStreams,\n overrideSimulcastEnabled = _ref.overrideSimulcastEnabled,\n logAnalyticsEvent = _ref.logAnalyticsEvent,\n offerOverrides = _ref.offerOverrides,\n answerOverrides = _ref.answerOverrides,\n sourceStreamId = _ref.sourceStreamId,\n isP2pEnabled = _ref.isP2pEnabled;\n\n let _peerConnection;\n\n let _awaitingIceRestart = false;\n\n let _cancelWatchAudioAcquisition; // Private\n\n\n const _onPeerClosed = function _onPeerClosed() {\n this.destroy();\n\n if (_awaitingIceRestart) {\n _awaitingIceRestart = false;\n this.trigger('iceRestartFailure');\n }\n\n this.trigger('disconnected');\n };\n\n const _onPeerError = function _onPeerError(_ref2) {\n let reason = _ref2.reason,\n prefix = _ref2.prefix;\n this.trigger('error', {\n reason,\n prefix\n });\n };\n\n const _onIceConnectionStateChange = function _onIceConnectionStateChange(state) {\n if (_awaitingIceRestart && _peerConnection.iceConnectionStateIsConnected()) {\n _awaitingIceRestart = false;\n this.trigger('iceRestartSuccess');\n } // watch for the Chrome bug where audio can't be acquired\n // can not use iceConnectionStateIsConnected since it is too broad\n\n\n if (state === 'connected') {\n if (OTHelpers.env.name === 'Chrome') {\n // cancel any pending watcher (in case of ice restart for example)\n if (_cancelWatchAudioAcquisition) {\n _cancelWatchAudioAcquisition();\n }\n\n _cancelWatchAudioAcquisition = watchAudioAcquisition(_peerConnection.getStats.bind(_peerConnection), () => this.trigger('audioAcquisitionProblem'));\n }\n\n this.trigger('connected');\n }\n\n this.trigger('iceConnectionStateChange', state);\n };\n\n eventing(this); // / Public API\n\n this.changeMediaDirectionToInactive = () => {\n if (_peerConnection) {\n _peerConnection.changeMediaDirectionToInactive();\n }\n };\n\n this.changeMediaDirectionToRecvOnly = () => {\n if (_peerConnection) {\n _peerConnection.changeMediaDirectionToRecvOnly();\n }\n };\n\n this.getDataChannel = function (label, options, completion) {\n _peerConnection.getDataChannel(label, options, completion);\n };\n\n this.getSourceStreamId = () => _peerConnection.getSourceStreamId();\n\n this.destroy = function () {\n if (_cancelWatchAudioAcquisition) {\n _cancelWatchAudioAcquisition();\n\n _cancelWatchAudioAcquisition = null;\n } // Clean up our PeerConnection\n\n\n if (_peerConnection) {\n _peerConnection.disconnect();\n\n _peerConnection = null;\n }\n\n this.off();\n };\n\n this.processMessage = function (type, message) {\n _peerConnection.processMessage(type, message);\n };\n\n this.addTrack = function (track, stream, callback) {\n return _peerConnection.addTrack(track, stream, callback);\n };\n\n this.removeTrack = function (RTCRtpSender) {\n return _peerConnection.removeTrack(RTCRtpSender);\n };\n\n this.getLocalStreams = function () {\n return _peerConnection.getLocalStreams();\n }; // Init\n\n\n this.init = function (rumorIceServers, completion) {\n const pcConfig = {\n iceConfig: !iceConfig.needRumorIceServersFallback ? iceConfig : assign(iceConfig, {\n servers: [...rumorIceServers, ...iceConfig.servers]\n }),\n channels,\n capableSimulcastStreams,\n overrideSimulcastEnabled\n };\n setCertificates(pcConfig, (err, pcConfigWithCerts) => {\n if (err) {\n completion(err);\n return;\n }\n\n const peerConnectionConfig = assign({\n logAnalyticsEvent,\n isPublisher: true,\n offerOverrides,\n answerOverrides,\n sourceStreamId,\n p2p: isP2pEnabled\n }, pcConfigWithCerts);\n _peerConnection = new PeerConnection(assign({\n sendMessage\n }, peerConnectionConfig));\n\n _peerConnection.on({\n close: _onPeerClosed,\n error: _onPeerError,\n qos: _qos => this.trigger('qos', _qos),\n iceConnectionStateChange: _onIceConnectionStateChange\n }, this);\n\n _peerConnection.addLocalStream(webRTCStream).then(() => {\n completion(undefined);\n }).catch(completion);\n });\n };\n\n this.getSenders = function () {\n return _peerConnection.getSenders();\n };\n\n this.iceRestart = function () {\n if (_peerConnection) {\n _awaitingIceRestart = true;\n\n _peerConnection.iceRestart();\n }\n };\n\n this.hasRelayCandidates = () => _peerConnection.hasRelayCandidates();\n\n this.iceConnectionStateIsConnected = function () {\n return _peerConnection.iceConnectionStateIsConnected();\n };\n\n this.findAndReplaceTrack = (oldTrack, newTrack) => _peerConnection.findAndReplaceTrack(oldTrack, newTrack);\n\n this._testOnlyGetFramesEncoded = () => new Promise((resolve, reject) => {\n _peerConnection.getStats((err, stats) => {\n if (err) {\n reject(err);\n return;\n }\n\n const videoStat = find(stats, stat => getStatsHelpers.isVideoStat(stat, stats) && getStatsHelpers.isOutboundStat(stat));\n\n if (!videoStat) {\n reject(new Error('Could not find framesEncoded in getStats report'));\n return;\n }\n\n resolve(pick(videoStat, ['timestamp', 'framesEncoded']));\n });\n });\n\n this.getStats = callback => _peerConnection.getStats(callback);\n\n this.getRtcStatsReport = callback => _peerConnection.getRtcStatsReport(callback);\n };\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 294 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable global-require, no-underscore-dangle, no-param-reassign, no-void, no-shadow */\n\n/* eslint-disable func-names */\nconst assign = __webpack_require__(7);\n\nconst eventing = __webpack_require__(5);\n\nmodule.exports = function SubscriberPeerConnectionFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const OTHelpers = deps.OTHelpers || __webpack_require__(4);\n\n const PeerConnection = deps.PeerConnection || __webpack_require__(101)();\n\n const setCertificates = deps.setCertificates || __webpack_require__(162)();\n\n const Errors = deps.Errors || __webpack_require__(6);\n\n const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(9);\n\n const OTErrorClass = deps.OTErrorClass || __webpack_require__(20);\n\n const watchSubscriberAudio = deps.watchSubscriberAudio || __webpack_require__(634);\n /*\n * Abstracts PeerConnection related stuff away from Subscriber.\n *\n * Responsible for:\n * * setting up the underlying PeerConnection (delegates to PeerConnections)\n * * triggering a connected event when the Peer connection is opened\n * * triggering a disconnected event when the Peer connection is closed\n * * creating a video element when a stream is added\n * * responding to stream removed intelligently\n * * providing a destroy method\n * * providing a processMessage method\n *\n * Once the PeerConnection is connected and the video element playing it\n * triggers the connected event\n *\n * Triggers the following events\n * * connected\n * * disconnected\n * * remoteStreamAdded\n * * remoteStreamRemoved\n * * error\n *\n */\n\n\n return function SubscriberPeerConnection(_ref) {\n let clientCandidates = _ref.clientCandidates,\n iceConfig = _ref.iceConfig,\n send = _ref.send,\n logAnalyticsEvent = _ref.logAnalyticsEvent,\n p2p = _ref.p2p,\n codecFlags = _ref.codecFlags,\n sourceStreamId = _ref.sourceStreamId;\n\n const _subscriberPeerConnection = this;\n\n let _peerConnection;\n\n let _destroyed = false;\n let _awaitingIceRestart = false;\n let _subscriberAudioWatcher = null; // Private\n\n const _onPeerClosed = function _onPeerClosed() {\n this.destroy();\n\n if (_awaitingIceRestart) {\n this.trigger('iceRestartFailure', this);\n }\n\n this.trigger('disconnected', this);\n };\n\n const _onRemoteStreamAdded = function _onRemoteStreamAdded(remoteRTCStream) {\n this.trigger('remoteStreamAdded', remoteRTCStream, this);\n };\n\n const _onRemoteStreamRemoved = function _onRemoteStreamRemoved(remoteRTCStream) {\n this.trigger('remoteStreamRemoved', remoteRTCStream, this);\n };\n\n const _onRemoteVideoSupported = supported => {\n this.trigger('remoteVideoSupported', supported);\n }; // Note: All Peer errors are fatal right now.\n\n\n const _onPeerError = function _onPeerError(_ref2) {\n let reason = _ref2.reason,\n prefix = _ref2.prefix;\n this.trigger('error', null, reason, this, prefix);\n };\n\n const _onIceConnectionStateChange = function _onIceConnectionStateChange(state) {\n if (_awaitingIceRestart && (state === 'connected' || state === 'completed')) {\n _awaitingIceRestart = false;\n this.trigger('iceRestartSuccess');\n }\n\n if (state === 'connected') {\n this.trigger('connected');\n }\n\n this.trigger('iceConnectionStateChange', state);\n };\n\n const _onsignalingStateChange = function _onsignalingStateChange(state) {\n this.trigger('signalingStateChange', state);\n };\n\n const _onsignalingStateStable = function _onsignalingStateStable(state) {\n this.trigger('signalingStateStable', state);\n };\n\n const _relayMessageToPeer = (type, content) => {\n if (type === 'answer' || type === 'pranswer') {\n this.trigger('connected');\n }\n\n send(type, content);\n };\n\n eventing(this); // Public\n\n this.close = function () {\n if (_destroyed) {\n return;\n }\n\n _destroyed = true;\n\n if (_peerConnection) {\n _peerConnection.disconnect();\n\n _peerConnection = null;\n }\n\n this.off();\n };\n\n this.destroy = function () {\n this.stopAudioStatsWatcher();\n\n if (_destroyed) {\n return;\n }\n\n this.close();\n };\n\n this.getDataChannel = function (label, options, completion) {\n _peerConnection.getDataChannel(label, options, completion);\n };\n\n this.getSourceStreamId = () => _peerConnection.getSourceStreamId();\n\n this.processMessage = function (type, message) {\n _peerConnection.processMessage(type, message);\n };\n\n this.remoteDescription = function () {\n return _peerConnection.remoteDescription();\n };\n\n this.getStats = function (callback) {\n if (_peerConnection) {\n _peerConnection.getStats(callback);\n } else {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(new OTHelpers.Error(OTErrorClass.getTitleByCode(errorCode), Errors.PEER_CONNECTION_NOT_CONNECTED, {\n code: errorCode\n }));\n }\n };\n\n this.getSynchronizationSources = function (callback) {\n if (!_peerConnection) {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(new OTHelpers.Error(OTErrorClass.getTitleByCode(errorCode), Errors.PEER_CONNECTION_NOT_CONNECTED, {\n code: errorCode\n }));\n return;\n }\n\n _peerConnection.getSynchronizationSources(callback);\n };\n\n this.getRtcStatsReport = function (callback) {\n if (_peerConnection) {\n _peerConnection.getRtcStatsReport(callback);\n } else {\n const errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(new OTHelpers.Error(OTErrorClass.getTitleByCode(errorCode), Errors.PEER_CONNECTION_NOT_CONNECTED, {\n code: errorCode\n }));\n }\n };\n\n this.startAudioStatsWatcher = function (disableAudioLevelStuckAt0) {\n if (!_subscriberAudioWatcher) {\n _subscriberAudioWatcher = watchSubscriberAudio(_peerConnection.getStats.bind(_peerConnection), reason => {\n this.stopAudioStatsWatcher();\n this.trigger('audioLevelStuckWarning', reason);\n }, disableAudioLevelStuckAt0);\n }\n };\n\n this.stopAudioStatsWatcher = function () {\n if (_subscriberAudioWatcher) {\n _subscriberAudioWatcher.stop();\n }\n\n _subscriberAudioWatcher = null;\n }; // Helper method used by subscribeToAudio/subscribeToVideo\n\n\n const _createSetEnabledForTracks = function _createSetEnabledForTracks(kind) {\n return function (enabled) {\n if (!_peerConnection) {\n // We haven't created the peer connection yet, so there are no remote streams right now.\n // Subscriber will try again after onRemoteStreamAdded so this works out ok.\n return;\n }\n\n _peerConnection.remoteTracks().forEach(track => {\n if (track.kind === kind && track.enabled !== enabled) {\n track.enabled = enabled;\n }\n });\n };\n };\n\n this.subscribeToAudio = _createSetEnabledForTracks('audio');\n this.subscribeToVideo = _createSetEnabledForTracks('video');\n\n this.hasRelayCandidates = function () {\n return _peerConnection.hasRelayCandidates();\n };\n\n this.iceRestart = function () {\n _awaitingIceRestart = true;\n return _peerConnection.iceRestart();\n };\n\n this.iceConnectionStateIsConnected = function () {\n return _peerConnection.iceConnectionStateIsConnected();\n }; // Init\n\n\n this.init = function (completion) {\n const pcConfig = {\n iceConfig\n };\n setCertificates(pcConfig, (err, pcConfigWithCerts) => {\n if (err) {\n completion(err);\n return;\n }\n\n const peerConnectionConfig = assign({\n logAnalyticsEvent,\n clientCandidates,\n codecFlags,\n sourceStreamId\n }, pcConfigWithCerts);\n _peerConnection = new PeerConnection(assign({\n sendMessage: _relayMessageToPeer,\n p2p\n }, peerConnectionConfig));\n\n _peerConnection.on({\n iceConnected: () => _subscriberPeerConnection.emit('iceConnected'),\n close: _onPeerClosed,\n streamAdded: _onRemoteStreamAdded,\n streamRemoved: _onRemoteStreamRemoved,\n signalingStateChange: _onsignalingStateChange,\n signalingStateStable: _onsignalingStateStable,\n error: _onPeerError,\n qos: _qos => this.trigger('qos', _qos),\n iceConnectionStateChange: _onIceConnectionStateChange,\n remoteVideoSupported: _onRemoteVideoSupported\n }, _subscriberPeerConnection); // If there are already remoteStreams, add them immediately\n // (Using .remoteTracks to avoid deprecated .remoteStreams where possible.\n // FIXME: Is this even possible anyway? How could we already have remote streams in the same\n // tick the peer connection was created?)\n\n\n if (_peerConnection.remoteTracks().length > 0) {\n // @todo i really don't think this branch is ever entered, it might be an artifact of the\n // unit tests\n // @todo ahh maybe reconnections?\n _peerConnection.remoteStreams().forEach(_onRemoteStreamAdded, _subscriberPeerConnection);\n } else {\n completion(undefined, _subscriberPeerConnection);\n }\n });\n };\n };\n};\n\n/***/ }),\n/* 295 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require */\nmodule.exports = function PublishingStateFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const generateSimpleStateMachine = deps.generateSimpleStateMachine || __webpack_require__(159)(); // Models a Publisher's publishing State\n //\n // Valid States:\n // NotPublishing\n // GetUserMedia\n // BindingMedia\n // MediaBound\n // PublishingToSession\n // Publishing\n // Failed\n // Destroyed\n //\n //\n // Valid Transitions:\n // NotPublishing ->\n // GetUserMedia\n //\n // GetUserMedia ->\n // BindingMedia\n // | Failed (Failure Reasons -> stream error, constraints,\n // (permission denied\n // | NotPublishing (destroy()\n //\n //\n // BindingMedia ->\n // MediaBound\n // | Failed (Failure Reasons -> Anything to do with the media\n // (being invalid, the media never plays\n // | NotPublishing (destroy()\n //\n // MediaBound ->\n // PublishingToSession (MediaBound could transition to PublishingToSession\n // (if a stand-alone publish is bound to a session\n // | Failed (Failure Reasons -> media issues with a stand-alone publisher\n // | NotPublishing (destroy()\n //\n // PublishingToSession\n // Publishing\n // | Failed\n // | NotPublishing (destroy()\n // | MediaBound (disconnect()\n //\n //\n // Publishing ->\n // NotPublishing (Unpublish\n // | Failed (Failure Reasons -> loss of network, media error, anything\n // (that causes *all* Peer Connections to fail (less than all\n // (failing is just an error, all is failure)\n // | NotPublishing (destroy()\n //\n // Failed ->\n // Destroyed\n //\n // Destroyed -> (Terminal state\n //\n //\n\n\n const validStates = ['NotPublishing', 'GetUserMedia', 'BindingMedia', 'MediaBound', 'PublishingToSession', 'Publishing', 'Failed', 'Destroyed'];\n const validTransitions = {\n NotPublishing: ['NotPublishing', 'GetUserMedia', 'Destroyed'],\n GetUserMedia: ['BindingMedia', 'Failed', 'NotPublishing', 'Destroyed'],\n BindingMedia: ['MediaBound', 'Failed', 'NotPublishing', 'Destroyed'],\n MediaBound: ['NotPublishing', 'PublishingToSession', 'Failed', 'Destroyed'],\n PublishingToSession: ['NotPublishing', 'Publishing', 'Failed', 'Destroyed', 'MediaBound'],\n Publishing: ['NotPublishing', 'MediaBound', 'Failed', 'Destroyed'],\n Failed: ['Destroyed'],\n Destroyed: []\n };\n const initialState = 'NotPublishing';\n const PublishingState = generateSimpleStateMachine(initialState, validStates, validTransitions);\n\n PublishingState.prototype.isDestroyed = function () {\n return this.current === 'Destroyed';\n };\n\n PublishingState.prototype.isAttemptingToPublish = function () {\n return ['GetUserMedia', 'BindingMedia', 'MediaBound', 'PublishingToSession'].indexOf(this.current) !== -1;\n };\n\n PublishingState.prototype.isPublishing = function () {\n return this.current === 'Publishing';\n };\n\n return PublishingState;\n};\n\n/***/ }),\n/* 296 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = CancellationError;\n// Note: Babel does not support extending builtin types like error:\n// https://phabricator.babeljs.io/T3083\n\n/**\n * The error that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The error message.\n * @extends Error\n */\nfunction CancellationError(message) {\n this.message = message || 'Operation has been canceled.';\n this.name = 'CancellationError';\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\nCancellationError.prototype = Object.create(Error.prototype);\nCancellationError.prototype.constructor = CancellationError;\n\n/***/ }),\n/* 297 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/*** IMPORTS FROM imports-loader ***/\nvar Promise = __webpack_require__(1);\n\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nfunction promiseFinally(value, cb) {\n return Promise.resolve(value).then(value => Promise.resolve(cb()).then(() => value), reason => Promise.resolve(cb()).then(() => Promise.reject(reason)));\n}\nexports.default = promiseFinally;\n//# sourceMappingURL=index.js.map\n\n\n/***/ }),\n/* 298 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\nvar _regenerator = _interopRequireDefault(__webpack_require__(18));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, no-underscore-dangle */\n\n/* eslint-disable no-use-before-define, no-shadow, max-len */\nconst defaults = __webpack_require__(140);\n\nconst merge = __webpack_require__(147);\n\nconst assign = __webpack_require__(7);\n\nconst values = __webpack_require__(274);\n\nconst get = __webpack_require__(55);\n\nconst once = __webpack_require__(48);\n\nconst _require = __webpack_require__(164),\n CancellationError = _require.CancellationError;\n\nconst uuid = __webpack_require__(17);\n\nconst EventEmitter = __webpack_require__(39);\n\nconst now = __webpack_require__(47);\n\nconst castToBoolean = __webpack_require__(266);\n\nconst eventNames = __webpack_require__(24);\n\nconst eventing = __webpack_require__(5);\n\nconst waitUntil = __webpack_require__(644);\n\nconst RafRunner = __webpack_require__(103);\n\nconst eventHelper = __webpack_require__(61);\n\nconst interpretSubscriberCreateError = __webpack_require__(645);\n\nconst Errors = __webpack_require__(6);\n\nconst defaultWidgetView = __webpack_require__(163)();\n\nconst audioLevelBehaviour = __webpack_require__(646);\n\nconst AudioLevelMeterDefault = __webpack_require__(280);\n\nconst AudioLevelTransformerDefault = __webpack_require__(143);\n\nconst BackingBarDefault = __webpack_require__(281);\n\nconst ChromeDefault = __webpack_require__(144);\n\nconst envDefault = __webpack_require__(2);\n\nconst errorsDefault = __webpack_require__(6);\n\nconst EventsDefault = __webpack_require__(21)();\n\nconst ExceptionCodesDefault = __webpack_require__(9);\n\nconst getStatsHelpersDefault = __webpack_require__(75);\n\nconst hasAudioOutputLevelStatCapabilityDefault = __webpack_require__(299);\n\nconst hasRemoteStreamsWithWebAudioDefault = __webpack_require__(300);\n\nconst audioLevelSamplerDefault = __webpack_require__(647);\n\nconst interpretPeerConnectionErrorDefault = __webpack_require__(161)();\n\nconst loggingDefault = __webpack_require__(0)('Subscriber');\n\nconst MuteButtonDefault = __webpack_require__(282)();\n\nconst NamePanelDefault = __webpack_require__(283);\n\nconst otErrorDefault = __webpack_require__(11)();\n\nconst OTErrorClassDefault = __webpack_require__(20);\n\nconst OTHelpersDefault = __webpack_require__(4);\n\nconst StylableComponentDefault = __webpack_require__(151);\n\nconst windowMock = __webpack_require__(142)((typeof window !== undefined ? window : global));\n\nconst PeerConnection = __webpack_require__(101)({\n global: windowMock\n});\n\nconst SubscriberPeerConnectionDefault = __webpack_require__(294)({\n PeerConnection\n});\n\nconst SubscribingStateDefault = __webpack_require__(301);\n\nconst VideoDisabledIndicatorDefault = __webpack_require__(650);\n\nconst AudioBlockedIndicatorDefault = __webpack_require__(651);\n\nconst VideoUnsupportedIndicatorDefault = __webpack_require__(652);\n\nconst watchFrameRateDefault = __webpack_require__(653);\n\nconst createSendMethodDefault = __webpack_require__(302);\n\nconst parseIceServersDefault = __webpack_require__(100).parseIceServers;\n\nconst overallTimeout = __webpack_require__(654);\n\nconst createConnectivityState = __webpack_require__(655);\n\nconst createConnectivityAttemptPinger = __webpack_require__(656);\n\nconst isGetRtcStatsReportSupported = __webpack_require__(304);\n\nconst DEFAULT_AUDIO_VOLUME = 100;\n\nconst isLocalStream = (stream, session) => stream.connection.id === session.connection.id;\n\nconst constructSubscriberUri = (_ref) => {\n let apiKey = _ref.apiKey,\n sessionId = _ref.sessionId,\n streamId = _ref.streamId,\n subscriberId = _ref.subscriberId;\n return ['/v2/partner', apiKey, 'session', sessionId, 'stream', streamId, 'subscriber', subscriberId].join('/');\n};\n\nfunction hasExpectedTracks(mediaStream, oTStream) {\n const isMissingVideo = oTStream && oTStream.hasVideo && mediaStream && mediaStream.getVideoTracks().length === 0;\n const isMissingAudio = oTStream && oTStream.hasAudio && mediaStream && mediaStream.getAudioTracks().length === 0;\n return !(isMissingVideo || isMissingAudio);\n}\n\nfunction normalizeAudioVolume(volume) {\n return Math.max(0, Math.min(100, parseInt(volume, 10)));\n}\n\nfunction determineAudioVolume(props) {\n if (props.audioVolume !== undefined) {\n return props.audioVolume;\n } else if (props.subscribeToAudio === false) {\n return 0;\n }\n\n return DEFAULT_AUDIO_VOLUME;\n}\n\nmodule.exports = function SubscriberFactory(_temp) {\n let _ref2 = _temp === void 0 ? {} : _temp,\n _ref2$AudioLevelMeter = _ref2.AudioLevelMeter,\n AudioLevelMeter = _ref2$AudioLevelMeter === void 0 ? AudioLevelMeterDefault : _ref2$AudioLevelMeter,\n _ref2$AudioLevelTrans = _ref2.AudioLevelTransformer,\n AudioLevelTransformer = _ref2$AudioLevelTrans === void 0 ? AudioLevelTransformerDefault : _ref2$AudioLevelTrans,\n _ref2$BackingBar = _ref2.BackingBar,\n BackingBar = _ref2$BackingBar === void 0 ? BackingBarDefault : _ref2$BackingBar,\n _ref2$Chrome = _ref2.Chrome,\n Chrome = _ref2$Chrome === void 0 ? ChromeDefault : _ref2$Chrome,\n _ref2$env = _ref2.env,\n env = _ref2$env === void 0 ? envDefault : _ref2$env,\n _ref2$Errors = _ref2.Errors,\n errors = _ref2$Errors === void 0 ? errorsDefault : _ref2$Errors,\n _ref2$Events = _ref2.Events,\n Events = _ref2$Events === void 0 ? EventsDefault : _ref2$Events,\n _ref2$ExceptionCodes = _ref2.ExceptionCodes,\n ExceptionCodes = _ref2$ExceptionCodes === void 0 ? ExceptionCodesDefault : _ref2$ExceptionCodes,\n _ref2$audioLevelSampl = _ref2.audioLevelSampler,\n audioLevelSampler = _ref2$audioLevelSampl === void 0 ? audioLevelSamplerDefault : _ref2$audioLevelSampl,\n _ref2$getStatsHelpers = _ref2.getStatsHelpers,\n getStatsHelpers = _ref2$getStatsHelpers === void 0 ? getStatsHelpersDefault : _ref2$getStatsHelpers,\n _ref2$hasAudioOutputL = _ref2.hasAudioOutputLevelStatCapability,\n hasAudioOutputLevelStatCapability = _ref2$hasAudioOutputL === void 0 ? hasAudioOutputLevelStatCapabilityDefault : _ref2$hasAudioOutputL,\n _ref2$hasRemoteStream = _ref2.hasRemoteStreamsWithWebAudio,\n hasRemoteStreamsWithWebAudio = _ref2$hasRemoteStream === void 0 ? hasRemoteStreamsWithWebAudioDefault : _ref2$hasRemoteStream,\n _ref2$interpretPeerCo = _ref2.interpretPeerConnectionError,\n interpretPeerConnectionError = _ref2$interpretPeerCo === void 0 ? interpretPeerConnectionErrorDefault : _ref2$interpretPeerCo,\n _ref2$logging = _ref2.logging,\n logging = _ref2$logging === void 0 ? loggingDefault : _ref2$logging,\n _ref2$MuteButton = _ref2.MuteButton,\n MuteButton = _ref2$MuteButton === void 0 ? MuteButtonDefault : _ref2$MuteButton,\n _ref2$NamePanel = _ref2.NamePanel,\n NamePanel = _ref2$NamePanel === void 0 ? NamePanelDefault : _ref2$NamePanel,\n _ref2$otError = _ref2.otError,\n otError = _ref2$otError === void 0 ? otErrorDefault : _ref2$otError,\n _ref2$OTErrorClass = _ref2.OTErrorClass,\n OTErrorClass = _ref2$OTErrorClass === void 0 ? OTErrorClassDefault : _ref2$OTErrorClass,\n _ref2$OTHelpers = _ref2.OTHelpers,\n OTHelpers = _ref2$OTHelpers === void 0 ? OTHelpersDefault : _ref2$OTHelpers,\n _ref2$StylableCompone = _ref2.StylableComponent,\n StylableComponent = _ref2$StylableCompone === void 0 ? StylableComponentDefault : _ref2$StylableCompone,\n _ref2$SubscriberPeerC = _ref2.SubscriberPeerConnection,\n SubscriberPeerConnection = _ref2$SubscriberPeerC === void 0 ? SubscriberPeerConnectionDefault : _ref2$SubscriberPeerC,\n _ref2$SubscribingStat = _ref2.SubscribingState,\n SubscribingState = _ref2$SubscribingStat === void 0 ? SubscribingStateDefault : _ref2$SubscribingStat,\n _ref2$VideoDisabledIn = _ref2.VideoDisabledIndicator,\n VideoDisabledIndicator = _ref2$VideoDisabledIn === void 0 ? VideoDisabledIndicatorDefault : _ref2$VideoDisabledIn,\n _ref2$AudioBlockedInd = _ref2.AudioBlockedIndicator,\n AudioBlockedIndicator = _ref2$AudioBlockedInd === void 0 ? AudioBlockedIndicatorDefault : _ref2$AudioBlockedInd,\n _ref2$VideoUnsupporte = _ref2.VideoUnsupportedIndicator,\n VideoUnsupportedIndicator = _ref2$VideoUnsupporte === void 0 ? VideoUnsupportedIndicatorDefault : _ref2$VideoUnsupporte,\n _ref2$watchFrameRate = _ref2.watchFrameRate,\n watchFrameRate = _ref2$watchFrameRate === void 0 ? watchFrameRateDefault : _ref2$watchFrameRate,\n _ref2$createSendMetho = _ref2.createSendMethod,\n createSendMethod = _ref2$createSendMetho === void 0 ? createSendMethodDefault : _ref2$createSendMetho,\n _ref2$parseIceServers = _ref2.parseIceServers,\n parseIceServers = _ref2$parseIceServers === void 0 ? parseIceServersDefault : _ref2$parseIceServers,\n _ref2$document = _ref2.document,\n document = _ref2$document === void 0 ? (typeof window !== undefined ? window : global).document : _ref2$document,\n _ref2$WidgetView = _ref2.WidgetView,\n WidgetView = _ref2$WidgetView === void 0 ? defaultWidgetView : _ref2$WidgetView;\n\n const BIND_VIDEO_DELAY_MAX = 30000;\n const BIND_VIDEO_DELAY_WARNING = 15000;\n /**\n * The Subscriber object is a representation of the local video element that is playing back\n * a remote stream. The Subscriber object includes methods that let you disable and enable\n * local audio playback for the subscribed stream. The subscribe()
method of the\n * {@link Session} object returns a Subscriber object.\n *\n * @property {Element} element The HTML DOM element containing the Subscriber.\n * @property {String} id The DOM ID of the Subscriber.\n * @property {Stream} stream The stream to which you are subscribing.\n *\n * @class Subscriber\n * @augments EventDispatcher\n */\n\n const Subscriber = function Subscriber(targetElement, options, completionHandler) {\n var _this = this;\n\n if (options === void 0) {\n options = {};\n }\n\n if (completionHandler === void 0) {\n completionHandler = () => {};\n }\n\n if (options.analytics === undefined) {\n // @todo does anyone instantiate a Subscriber outside of `session.subscribe`?\n // it might be best to instantiate an analytics object and replace it on subscribe like\n // we do in the publisher.\n throw new Error('Subscriber requires an instance of analytics');\n }\n /** @type AnalyticsHelperDefault */\n\n\n const analytics = options.analytics;\n\n const _widgetId = uuid();\n\n const _audioLevelCapable = Subscriber.hasAudioOutputLevelStatCapability() || hasRemoteStreamsWithWebAudio();\n\n const _subscriber = this;\n\n const _pcConnected = {};\n const peerConnectionsAsync = {};\n const webRTCStreams = {};\n const hybridSessionTransitionStartTimes = {};\n const _peerConnectionEvents = {};\n /** @type {defaultWidgetView|null} */\n\n let _widgetView;\n\n let _chrome;\n\n let _muteDisplayMode;\n\n let _audioLevelMeter;\n\n let _subscribeStartTime;\n\n let _state;\n\n let _audioLevelSampler;\n\n let _audioLevelBehaviour;\n\n let _webRTCStream;\n\n let _lastSubscribeToVideoReason;\n\n let _attemptStartTime;\n /** @type IntervalRunnerDefault | undefined */\n\n\n let _streamEventHandlers;\n\n let _isSubscribingToAudio;\n\n let _loaded = false;\n\n let _domId = targetElement || _widgetId;\n\n let _session = options.session;\n let _stream = options.stream;\n\n let _audioVolume;\n\n let _latestPositiveVolume;\n\n let _frameRateRestricted = false;\n\n let _frameRateWatcher;\n\n let _preDisconnectStats = {};\n let _congestionLevel = null;\n let _hasLoadedAtLeastOnce = false;\n let _isVideoSupported = true;\n let fallbackIceServers = [];\n\n let _properties = defaults({}, options, {\n showControls: true,\n testNetwork: false,\n fitMode: _stream.defaultFitMode || 'cover',\n insertDefaultUI: true\n });\n\n let _activeSourceStreamId;\n\n const _lastConnectionStatesMap = {\n P2P: eventNames.SUBSCRIBER_DISCONNECTED,\n MANTIS: eventNames.SUBSCRIBER_DISCONNECTED\n }; // The audio stats watcher is only supported on chromium-based browsers that supports\n // the standard version of the getStats API.\n\n const _supportsAudioStatsWatcher = env.name === 'Chrome' && env.version >= 58 || env.isChromiumEdge || env.isOpera;\n\n const socket = _session._.getSocket();\n\n const getAllPeerConnections = () => values(peerConnectionsAsync);\n\n const getPeerConnectionBySourceStreamId = sourceStreamId => peerConnectionsAsync[sourceStreamId];\n\n const getCurrentPeerConnection = () => peerConnectionsAsync[_activeSourceStreamId];\n\n const removePeerConnectionBySourceStreamId = /*#__PURE__*/function () {\n var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(sourceStreamId) {\n var peerConnection;\n return _regenerator.default.wrap(function _callee$(_context) {\n while (1) switch (_context.prev = _context.next) {\n case 0:\n _context.next = 2;\n return peerConnectionsAsync[sourceStreamId];\n\n case 2:\n peerConnection = _context.sent;\n peerConnection.close();\n delete peerConnectionsAsync[sourceStreamId];\n\n case 5:\n case \"end\":\n return _context.stop();\n }\n }, _callee);\n }));\n\n return function removePeerConnectionBySourceStreamId(_x) {\n return _ref3.apply(this, arguments);\n };\n }();\n\n const removeWebRTCStream = sourceStreamId => {\n delete webRTCStreams[sourceStreamId];\n };\n\n const bindWebRTCStream = /*#__PURE__*/function () {\n var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(webRTCStream) {\n var videoContainerOptions;\n return _regenerator.default.wrap(function _callee2$(_context2) {\n while (1) switch (_context2.prev = _context2.next) {\n case 0:\n videoContainerOptions = {\n error: onVideoError,\n audioVolume: _audioVolume\n };\n _context2.prev = 1;\n _context2.next = 4;\n return _widgetView.bindVideo(webRTCStream, videoContainerOptions);\n\n case 4:\n _context2.next = 12;\n break;\n\n case 6:\n _context2.prev = 6;\n _context2.t0 = _context2[\"catch\"](1);\n\n if (!(_context2.t0 instanceof CancellationError || _state.isDestroyed())) {\n _context2.next = 10;\n break;\n }\n\n return _context2.abrupt(\"return\");\n\n case 10:\n onVideoError(_context2.t0);\n throw _context2.t0;\n\n case 12:\n case \"end\":\n return _context2.stop();\n }\n }, _callee2, null, [[1, 6]]);\n }));\n\n return function bindWebRTCStream(_x2) {\n return _ref4.apply(this, arguments);\n };\n }();\n\n const _getStatsWrapper = /*#__PURE__*/function () {\n var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(reportType, callback) {\n var isRtcStatsReport, errorCode, pc, getStats;\n return _regenerator.default.wrap(function _callee3$(_context3) {\n while (1) switch (_context3.prev = _context3.next) {\n case 0:\n isRtcStatsReport = false;\n\n if (typeof reportType === 'function') {\n /* eslint-disable-next-line no-param-reassign */\n callback = reportType;\n } else {\n isRtcStatsReport = reportType === 'rtcStatsReport';\n }\n\n if (isRtcStatsReport) {\n notifyGetRtcStatsReportCalled();\n } else {\n notifyGetStatsCalled();\n }\n\n if (!(isRtcStatsReport && !isGetRtcStatsReportSupported)) {\n _context3.next = 7;\n break;\n }\n\n errorCode = ExceptionCodes.GET_RTC_STATS_REPORT_NOT_SUPPORTED;\n callback(otError(Errors.GET_RTC_STATS_REPORT_NOT_SUPPORTED, new Error(OTErrorClass.getTitleByCode(errorCode)), errorCode));\n return _context3.abrupt(\"return\");\n\n case 7:\n if (getCurrentPeerConnection()) {\n _context3.next = 11;\n break;\n }\n\n errorCode = ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED;\n callback(otError(errors.PEER_CONNECTION_NOT_CONNECTED, new Error(OTErrorClass.getTitleByCode(errorCode)), errorCode));\n return _context3.abrupt(\"return\");\n\n case 11:\n _context3.next = 13;\n return getCurrentPeerConnection();\n\n case 13:\n pc = _context3.sent;\n getStats = isRtcStatsReport ? pc.getRtcStatsReport : pc.getStats;\n getStats((error, stats) => {\n if (error) {\n if (error.code === ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED) {\n error = otError(errors.PEER_CONNECTION_NOT_CONNECTED, error, ExceptionCodes.PEER_CONNECTION_NOT_CONNECTED);\n }\n\n callback(error);\n } else {\n callback(null, stats);\n }\n });\n\n case 16:\n case \"end\":\n return _context3.stop();\n }\n }, _callee3);\n }));\n\n return function _getStatsWrapper(_x3, _x4) {\n return _ref5.apply(this, arguments);\n };\n }();\n\n const _getStats = callback => _getStatsWrapper(callback);\n\n const _getRtcStatsReport = callback => _getStatsWrapper('rtcStatsReport', callback);\n\n const logAnalyticsEvent = (action, variation, payload, options, throttle) => {\n let stats = assign({\n action,\n variation,\n payload,\n streamId: _stream ? _stream.id : null,\n sessionId: _session ? _session.sessionId : null,\n connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null,\n partnerId: _session && _session.sessionInfo ? _session.sessionInfo.partnerId : null,\n subscriberId: _widgetId,\n widgetType: 'Subscriber'\n }, options);\n\n if (variation === 'Failure' || variation === 'iceconnectionstatechange' && payload === 'closed') {\n stats = assign(stats, _preDisconnectStats);\n }\n\n const args = [stats];\n\n if (throttle) {\n args.push(throttle);\n }\n\n analytics.logEvent(...args);\n };\n\n const logHybridSessionTransition = (action, variation, payload) => {\n if (variation === 'Attempt') {\n hybridSessionTransitionStartTimes[action] = new Date().getTime();\n logAnalyticsEvent(action, variation, payload);\n } else if (variation === 'Failure' || variation === 'Success') {\n logAnalyticsEvent(action, variation, payload, {\n attemptDuration: new Date().getTime() - hybridSessionTransitionStartTimes[action]\n });\n }\n };\n\n const logRoutedToRelayedTransition = function logRoutedToRelayedTransition(variation, payload) {\n if (payload === void 0) {\n payload = {};\n }\n\n logHybridSessionTransition('RoutedToRelayedTransition', variation, payload);\n };\n\n const logRelayedToRoutedTransition = function logRelayedToRoutedTransition(variation, payload) {\n if (payload === void 0) {\n payload = {};\n }\n\n logHybridSessionTransition('RelayedToRoutedTransition', variation, payload);\n };\n\n const connectivityState = createConnectivityState({\n onInvalidTransition(transition, from) {\n const err = `Invalid state transition: Event '${transition}' not possible in state '${from}'`;\n logging.error(err);\n logAnalyticsEvent('Subscriber:InvalidStateTransition', 'Event', {\n details: err\n });\n }\n\n });\n {\n 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\n // amount of time\n\n overallTimeout({\n connectivityState,\n\n onWarning() {\n logConnectivityEvent('Warning', {});\n },\n\n onTimeout: () => {\n if (_widgetView) {\n _widgetView.addError(errMessage);\n }\n\n if (_state.isAttemptingToSubscribe()) {\n _state.set('Failed');\n\n this._disconnect({\n noStateTransition: true\n });\n\n const error = otError(Errors.TIMEOUT, new Error(errMessage), ExceptionCodes.UNABLE_TO_SUBSCRIBE);\n connectivityState.fail({\n options: {\n failureReason: 'Subscribe',\n failureMessage: errMessage,\n failureCode: ExceptionCodes.UNABLE_TO_SUBSCRIBE\n },\n error\n });\n }\n },\n warningMs: BIND_VIDEO_DELAY_WARNING,\n timeoutMs: BIND_VIDEO_DELAY_MAX\n });\n }\n _pcConnected.promise = new Promise((resolve, reject) => {\n _pcConnected.resolve = resolve;\n _pcConnected.reject = reject;\n });\n\n if (_properties.testNetwork && _session.sessionInfo.p2pEnabled) {\n logging.warn('You cannot test your network with a relayed session. Use a routed session.');\n }\n\n this.id = _domId;\n this.widgetId = _widgetId;\n this.session = _session;\n this.stream = _properties.stream;\n _stream = _properties.stream;\n this.streamId = _stream.id;\n\n if (!_session) {\n OTErrorClass.handleJsException({\n errorMsg: 'OT.Subscriber must be passed a session option',\n code: 2000,\n target: this,\n analytics\n });\n return null;\n }\n\n eventing(this);\n\n _subscriber.once('subscribeComplete', function () {\n try {\n completionHandler(...arguments);\n } catch (err) {\n logging.error('Completion handler threw an exception', err);\n }\n });\n\n const onGsmCallEnded = () => {\n // OPENTOK-42152: When a GSM call is ended, the subscribers might have an issue\n // in which a black frame is displayed instead of video.\n // A workaround is to pause and play the subscriber's video element.\n if (_widgetView) {\n _widgetView.pauseAndPlayVideoElement();\n }\n };\n\n this.session.on('gsmCallEnded', onGsmCallEnded);\n\n const getVariationFromState = (_ref6) => {\n let transition = _ref6.transition,\n from = _ref6.from;\n\n if (transition === 'fail') {\n if (from === 'connected') {\n return 'Disconnect';\n }\n\n return 'Failure';\n }\n\n if (transition === 'disconnect' && from === 'connected') {\n return 'Disconnect';\n }\n\n return 'Cancel';\n };\n\n connectivityState.observe({\n onEnterConnecting() {\n logConnectivityEvent('Attempt', null, {});\n },\n\n onEnterConnected() {\n logConnectivityEvent('Success', null, {});\n },\n\n onEnterDisconnected(state, _temp2) {\n let _ref7 = _temp2 === void 0 ? {} : _temp2,\n _ref7$options = _ref7.options,\n options = _ref7$options === void 0 ? {} : _ref7$options,\n _ref7$payload = _ref7.payload,\n payload = _ref7$payload === void 0 ? null : _ref7$payload;\n\n logConnectivityEvent(getVariationFromState(state), payload, options);\n }\n\n });\n connectivityState.observe({\n onEnterConnected: () => {\n this.trigger('subscribeComplete', undefined, this);\n },\n onEnterDisconnected: function onEnterDisconnected(_ref8, _temp3) {\n let from = _ref8.from;\n\n let _ref9 = _temp3 === void 0 ? {} : _temp3,\n error = _ref9.error;\n\n if (from === 'connecting') {\n _this.trigger('subscribeComplete', error || new Error('An unknown error occurred'), _this);\n }\n }\n });\n createConnectivityAttemptPinger({\n connectivityState,\n\n logAttempt() {\n logAnalyticsEvent('Subscribe', 'Attempting', null, {\n connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null\n });\n }\n\n });\n\n const logConnectivityEvent = (variation, payload, options) => {\n if (variation === 'Attempt') {\n _attemptStartTime = new Date().getTime();\n }\n\n if (variation === 'Failure' || variation === 'Success' || variation === 'Cancel') {\n logAnalyticsEvent('Subscribe', variation, payload, (0, _extends2.default)({}, options, {\n attemptDuration: new Date().getTime() - _attemptStartTime\n }));\n } else {\n logAnalyticsEvent('Subscribe', variation, payload, options);\n }\n };\n\n const logResubscribe = (variation, payload) => {\n logAnalyticsEvent('ICERestart', variation, payload);\n };\n\n const recordQOS = (_ref10) => {\n let parsedStats = _ref10.parsedStats,\n remoteConnectionId = _ref10.remoteConnectionId,\n peerId = _ref10.peerId,\n sourceStreamId = _ref10.sourceStreamId;\n const QoSBlob = {\n widgetType: 'Subscriber',\n width: _widgetView.width,\n height: _widgetView.height,\n audioTrack: _webRTCStream && _webRTCStream.getAudioTracks().length > 0,\n hasAudio: _stream && _stream.hasAudio,\n subscribeToAudio: _isSubscribingToAudio,\n audioVolume: this.getAudioVolume(),\n videoTrack: _webRTCStream && _webRTCStream.getVideoTracks().length > 0,\n connectionId: _session ? _session.connection.connectionId : null,\n hasVideo: _stream && _stream.hasVideo,\n subscribeToVideo: _properties.subscribeToVideo,\n congestionLevel: _congestionLevel,\n streamId: _stream.id,\n subscriberId: _widgetId,\n duration: Math.round((now() - _subscribeStartTime) / 1000),\n remoteConnectionId,\n peerId,\n sourceStreamId\n };\n const combinedStats = assign(QoSBlob, parsedStats);\n analytics.logQOS(combinedStats);\n this.trigger('qos', analytics.combineWithCommon(combinedStats));\n };\n\n const stateChangeFailed = changeFailed => {\n logging.error('OT.Subscriber State Change Failed: ', changeFailed.message);\n logging.debug(changeFailed);\n };\n\n const onLoaded = () => {\n if (_state.isSubscribing() || !_widgetView || !_widgetView.video()) {\n return;\n }\n\n _loaded = true;\n\n if (!_hasLoadedAtLeastOnce) {\n connectivityState.connect();\n _hasLoadedAtLeastOnce = true;\n }\n\n logging.debug('OT.Subscriber.onLoaded');\n\n _state.set('Subscribing');\n\n _subscribeStartTime = now();\n\n _widgetView.loading(false);\n\n if (_chrome) {\n _chrome.showAfterLoading();\n }\n\n if (_frameRateRestricted) {\n _stream.setRestrictFrameRate(true);\n }\n\n if (_audioLevelMeter) {\n _audioLevelMeter.audioOnly(_widgetView.audioOnly());\n }\n\n this.setAudioVolume(_audioVolume);\n this.trigger('loaded', this);\n };\n\n const isAudioOn = () => _stream && _stream.hasAudio && _isSubscribingToAudio && this.getAudioVolume() > 0 && !this.isAudioBlocked();\n\n const isVideoOn = () => _stream && _stream.hasVideo;\n\n const onAudioLevelStuckWarning = reason => {\n // `audioLevelStuckAt0` warning will be ignored either because the video is ON or audio is OFF\n const shouldIgnoreWarning = reason === 'audioLevelStuckAt0' && (!isAudioOn() || isVideoOn());\n let disableAudioLevelStuckAt0 = false;\n\n if (!shouldIgnoreWarning) {\n logAnalyticsEvent('subscriber:onAudioWarning', 'Event', {\n reason\n });\n\n if (_widgetView) {\n _widgetView.rebindSrcObject();\n\n logAnalyticsEvent('subscriber:rebindSrcObject', 'Called');\n\n if (reason === 'audioLevelStuck') {\n // Since 'audioLevelStuck' anomally was detected and the rebind already happened,\n // we don't need to do future rebinds. We can do a return here so that the watcher\n // is not restarted.\n return;\n } // This is to ignore future warnings of this type in order to prevent\n // periodical rebindings.\n\n\n disableAudioLevelStuckAt0 = true;\n }\n }\n\n startAudioStatsWatcher(disableAudioLevelStuckAt0);\n };\n\n const startAudioStatsWatcher = /*#__PURE__*/function () {\n var _ref11 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(disableAudioLevelStuckAt0) {\n var peerConnection;\n return _regenerator.default.wrap(function _callee4$(_context4) {\n while (1) switch (_context4.prev = _context4.next) {\n case 0:\n if (disableAudioLevelStuckAt0 === void 0) {\n disableAudioLevelStuckAt0 = false;\n }\n\n if (!_supportsAudioStatsWatcher) {\n _context4.next = 6;\n break;\n }\n\n _context4.next = 4;\n return getCurrentPeerConnection();\n\n case 4:\n peerConnection = _context4.sent;\n peerConnection.startAudioStatsWatcher(disableAudioLevelStuckAt0);\n\n case 6:\n case \"end\":\n return _context4.stop();\n }\n }, _callee4);\n }));\n\n return function startAudioStatsWatcher(_x5) {\n return _ref11.apply(this, arguments);\n };\n }();\n\n const onPeerConnected = peerConnection => {\n const isAdaptiveEnabled = _session.sessionInfo.isAdaptiveEnabled;\n const isAdaptiveP2pSourceStreamId = isAdaptiveEnabled && peerConnection.getSourceStreamId() === 'P2P';\n\n if (isAdaptiveP2pSourceStreamId) {\n logRoutedToRelayedTransition('Success');\n }\n };\n\n const onPeerDisconnected = () => {\n logging.debug('OT.Subscriber has been disconnected from the Publisher\\'s PeerConnection');\n connectivityState.fail({\n options: {\n failureReason: 'PeerConnectionError',\n failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,\n failureMessage: 'PeerConnection disconnected'\n },\n error: otError(errors.DISCONNECTED, new Error('ClientDisconnected'))\n });\n\n this._disconnect({\n noStateTransition: true\n });\n\n if (_state.isAttemptingToSubscribe() || _state.isSubscribing()) {\n _state.set('Failed');\n }\n };\n\n const onVideoError = plainErr => {\n const err = otError(errors.MEDIA_ERR_DECODE, plainErr, plainErr.code || ExceptionCodes.P2P_CONNECTION_FAILED);\n err.message = `OT.Subscriber while playing stream: ${err.message}`;\n logging.error('OT.Subscriber.onVideoError');\n connectivityState.fail({\n options: {\n failureReason: 'VideoElement',\n failureMessage: err.message,\n failureCode: err.code || ExceptionCodes.P2P_CONNECTION_FAILED\n },\n error: err\n });\n\n const isAttemptingToSubscribe = _state.isAttemptingToSubscribe();\n\n _state.set('Failed');\n\n if (!isAttemptingToSubscribe) {\n // FIXME: This emits a string instead of an error here for backwards compatibility despite\n // being undocumented. When possible we should remove access to this and other undocumented\n // events, and restore emitting actual errors here.\n _subscriber.trigger('error', err.message);\n }\n\n OTErrorClass.handleJsException({\n error: err,\n code: ExceptionCodes.UNABLE_TO_SUBSCRIBE,\n // @todo why do we override the code?\n target: _subscriber,\n analytics\n });\n };\n\n const onSubscriberCreateError = rawError => {\n // @todo v3 These errors used to come from the peer connection hence the mention\n // of peer connection in the logs/ errors. However they actually have nothing to do\n // with the peer connection. I have chosen to keep the messages / errors the same for\n // now, but we should consider changing them\n const err = interpretSubscriberCreateError(rawError);\n OTErrorClass.handleJsException({\n error: err,\n code: err.code,\n target: _subscriber,\n analytics\n });\n\n this._disconnect({\n noStateTransition: true\n });\n\n const options = {\n failureReason: 'Subscribe',\n failureMessage: `OT.Subscriber PeerConnection Error: ${err.message}`,\n failureCode: ExceptionCodes.P2P_CONNECTION_FAILED\n };\n\n _showError(err.code);\n\n connectivityState.fail({\n options,\n error: otError(err.name, new Error('Subscribe: Subscriber PeerConnection with connection (not found) ' + `failed: ${err.message}`), err.code)\n });\n\n if (err.name === Errors.STREAM_NOT_FOUND) {\n // Usually, the subscriber should be kept in an error state when the peer connection fails\n // because it still exists in the session. But when we get a 404 that means it doesn't\n // exist, even if the session still thinks it does.\n this._destroy({\n reason: 'streamNotFound',\n noStateTransition: true\n });\n }\n };\n\n const onPeerConnectionFailure = (code, reason, peerConnection, prefix) => {\n if (prefix === 'SetRemoteDescription' && !_isVideoSupported && reason.match(/Unsupported video without audio for fallback/)) {\n if (_widgetView) {\n _widgetView.addError('The stream is unable to be played.', 'Your browser does not support the video format.');\n }\n\n let err;\n\n if (OTHelpers.env.name === 'Safari') {\n 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.');\n } else {\n err = new Error('Video format not supported in this browser.');\n }\n\n err.code = ExceptionCodes.UNSUPPORTED_VIDEO_CODEC;\n onVideoError(err);\n return;\n }\n\n if (prefix === 'ICEWorkflow' && _session.sessionInfo.reconnection && _loaded) {\n logging.debug('Ignoring peer connection failure due to possibility of reconnection.');\n return;\n }\n\n let errorCode;\n\n if (prefix === 'ICEWorkflow') {\n errorCode = ExceptionCodes.SUBSCRIBER_ICE_WORKFLOW_FAILED;\n } else if (code === ExceptionCodes.STREAM_LIMIT_EXCEEDED) {\n errorCode = code;\n } else {\n errorCode = ExceptionCodes.P2P_CONNECTION_FAILED;\n }\n\n const options = {\n failureReason: prefix || 'PeerConnectionError',\n failureMessage: `OT.Subscriber PeerConnection Error: ${reason}`,\n failureCode: errorCode\n };\n const error = interpretPeerConnectionError(code, reason, prefix, '(not found)', 'Subscriber');\n connectivityState.fail({\n options,\n error\n });\n\n if (_state.isAttemptingToSubscribe()) {\n // We weren't subscribing yet so this was a failure in setting\n // up the PeerConnection or receiving the initial stream.\n const payload = {\n hasRelayCandidates: peerConnection && peerConnection.hasRelayCandidates()\n };\n logAnalyticsEvent('createPeerConnection', 'Failure', payload, options);\n\n _state.set('Failed');\n } else if (_state.isSubscribing()) {\n // we were disconnected after we were already subscribing\n _state.set('Failed');\n\n this.trigger('error', reason);\n }\n\n this._disconnect({\n noStateTransition: true\n });\n\n if (Number(code) === 404) {\n // Usually, the subscriber should be kept in an error state when the peer connection fails\n // because it still exists in the session. But when we get a 404 that means it doesn't\n // exist, even if the session still thinks it does.\n this._destroy({\n noStateTransition: true\n });\n }\n\n OTErrorClass.handleJsException({\n errorMsg: `OT.Subscriber PeerConnection Error: ${reason}`,\n errorCode,\n target: this,\n analytics\n });\n\n _showError(code);\n };\n\n const onRemoteStreamAdded = /*#__PURE__*/function () {\n var _ref12 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(webRTCStream, peerConnection) {\n var isSwappingStreams, sourceStreamId, _session2, sessionInfo, video, videoIndicator, videoElementCreated, env;\n\n return _regenerator.default.wrap(function _callee5$(_context5) {\n while (1) switch (_context5.prev = _context5.next) {\n case 0:\n isSwappingStreams = Object.keys(webRTCStreams).length === 1;\n sourceStreamId = peerConnection == null ? void 0 : peerConnection.getSourceStreamId();\n _session2 = _session, sessionInfo = _session2.sessionInfo;\n _webRTCStream = webRTCStream; // save a copy of the WebRTCStream\n\n if (sourceStreamId) {\n webRTCStreams[sourceStreamId] = webRTCStream;\n }\n\n logging.debug('OT.Subscriber.onRemoteStreamAdded with sourceStreamId', sourceStreamId);\n _context5.prev = 6;\n _context5.next = 9;\n return waitUntil(() => hasExpectedTracks(_webRTCStream, _stream));\n\n case 9:\n _context5.next = 18;\n break;\n\n case 11:\n _context5.prev = 11;\n _context5.t0 = _context5[\"catch\"](6);\n\n if (!(_context5.t0.message === 'TIMEOUT')) {\n _context5.next = 17;\n break;\n }\n\n logging.error('The expected tracks never arrived');\n _context5.next = 18;\n break;\n\n case 17:\n throw _context5.t0;\n\n case 18:\n _state.set('BindingRemoteStream'); // Disable the audio/video, if needed\n\n\n _this.subscribeToAudio(_isSubscribingToAudio);\n\n _lastSubscribeToVideoReason = 'loading';\n\n _this.subscribeToVideo(_properties.subscribeToVideo, 'loading'); // setting resolution and frame rate doesn't work in P2P\n\n\n if (!sessionInfo.p2pEnabled && sourceStreamId !== 'P2P') {\n _this.setPreferredResolution(_properties.preferredResolution);\n\n _this.setPreferredFrameRate(_properties.preferredFrameRate);\n } // This is a workaround for a bug in Chrome where a track disabled on\n // the remote end doesn't fire loadedmetadata causing the subscriber to timeout\n // https://jira.tokbox.com/browse/OPENTOK-15605\n // Also https://jira.tokbox.com/browse/OPENTOK-16425\n // Also https://jira.vonage.com/browse/OPENTOK-27112\n\n\n webRTCStream.getVideoTracks().forEach(track => {\n if ((typeof window !== undefined ? window : global).webkitMediaStream) {\n track.enabled = false;\n } else {\n track.enabled = _stream.hasVideo && _properties.subscribeToVideo;\n }\n });\n _context5.next = 26;\n return bindWebRTCStream(webRTCStream);\n\n case 26:\n startAudioStatsWatcher();\n\n if (!_state.isDestroyed()) {\n _context5.next = 29;\n break;\n }\n\n throw new Error('Subscriber destroyed');\n\n case 29:\n if ((typeof window !== undefined ? window : global).webkitMediaStream) {\n // Enable any video streams that we previously disabled for OPENTOK-27112\n webRTCStream.getVideoTracks().forEach(track => {\n track.enabled = _stream.hasVideo && _properties.subscribeToVideo;\n });\n }\n\n video = _widgetView && _widgetView.video();\n\n if (video) {\n video.orientation({\n width: _stream.videoDimensions.width,\n height: _stream.videoDimensions.height,\n videoOrientation: _stream.videoDimensions.orientation\n });\n }\n\n videoIndicator = _chrome && _chrome.videoUnsupportedIndicator;\n\n if (videoIndicator) {\n videoIndicator.setVideoUnsupported(!_isVideoSupported);\n }\n\n videoElementCreated = new Promise((resolve, reject) => {\n const video = _widgetView && _widgetView.video();\n\n if (video && video.domElement()) {\n resolve();\n return;\n }\n\n _widgetView.once('videoElementCreated', resolve);\n\n _subscriber.once('destroyed', reject);\n });\n _context5.next = 37;\n return videoElementCreated;\n\n case 37:\n _context5.next = 39;\n return _pcConnected.promise;\n\n case 39:\n if (!isSwappingStreams) {\n onLoaded();\n } else {\n _state.set('Subscribing');\n } // if the audioLevelSampler implementation requires a stream we need to set it now\n\n\n if (_audioLevelSampler && 'webRTCStream' in _audioLevelSampler && webRTCStream.getAudioTracks().length > 0) {\n _audioLevelSampler.webRTCStream(webRTCStream);\n }\n /**\n * We need frame rate watcher in Safari because framesPerSecond is\n * always zero so we need to rely on calculating the difference of\n * framesDecoded across multiple getStats invocations.\n * See https://bugs.webkit.org/show_bug.cgi?id=172682\n * In addition, we need to calculate it in Chrome v58+ and chromium based browsers\n * since framesPerSecond is not implemented in the standard getStats API.\n */\n\n\n env = OTHelpers.env;\n\n if (!(env.isSafari || env.isChromiumEdge || env.isOpera || env.name === 'Chrome' && env.version >= 58)) {\n _context5.next = 48;\n break;\n }\n\n if (_frameRateWatcher) {\n _frameRateWatcher.destroy();\n\n _frameRateWatcher = null;\n }\n\n _context5.next = 46;\n return getCurrentPeerConnection();\n\n case 46:\n peerConnection = _context5.sent;\n\n if (peerConnection) {\n _frameRateWatcher = watchFrameRate(peerConnection.getStats.bind(peerConnection));\n }\n\n case 48:\n // @todo We should revisit this event that is not public and seems to be not used anywhere.\n // Just to maintain the previous behavior, we'll avoid triggering twice in adaptive sessions.\n if (!isSwappingStreams) {\n _this.trigger('streamAdded', _this);\n }\n\n case 49:\n case \"end\":\n return _context5.stop();\n }\n }, _callee5, null, [[6, 11]]);\n }));\n\n return function onRemoteStreamAdded(_x6, _x7) {\n return _ref12.apply(this, arguments);\n };\n }();\n\n const onRemoteStreamRemoved = webRTCStream => {\n _webRTCStream = null;\n logging.debug('OT.Subscriber.onStreamRemoved');\n\n const video = _widgetView && _widgetView.video();\n\n if (video && video.stream === webRTCStream) {\n _widgetView.destroyVideo();\n }\n\n removeWebRTCStream(_activeSourceStreamId);\n this.trigger('streamRemoved', this);\n };\n\n const onRemoteVideoSupported = supported => {\n // as _isVideoSupported is true by default, this will only ever be hit if we receive\n // a onRemoteVideoSupported false, and then a onRemoteVideoSupported true. In other words\n // it will not trigger an event if video is enabled and is never disabled due to codec issues.\n if (_isVideoSupported !== supported) {\n this.dispatchEvent(new Events.VideoEnabledChangedEvent(supported ? 'videoEnabled' : 'videoDisabled', {\n reason: supported ? 'codecChanged' : 'codecNotSupported'\n }));\n }\n\n _isVideoSupported = supported;\n };\n\n const audioBlockedStateChange = value => {\n _widgetView.setAudioBlockedUi(value);\n\n const indicator = _chrome && _chrome.audioBlockedIndicator;\n\n if (indicator) {\n indicator.setAudioBlocked(value);\n }\n\n const videoIndicator = _chrome && _chrome.videoUnsupportedIndicator;\n\n if (videoIndicator) {\n videoIndicator.setVideoUnsupported(!value && !_isVideoSupported);\n }\n };\n\n this.on('audioBlocked', () => {\n audioBlockedStateChange(true);\n });\n this.on('audioUnblocked', () => {\n audioBlockedStateChange(false);\n });\n const connectionStateMap = {\n new: eventNames.SUBSCRIBER_DISCONNECTED,\n checking: eventNames.SUBSCRIBER_DISCONNECTED,\n connected: eventNames.SUBSCRIBER_CONNECTED,\n completed: eventNames.SUBSCRIBER_CONNECTED,\n disconnected: eventNames.SUBSCRIBER_DISCONNECTED\n };\n\n const onIceConnectionStateChange = (state, peerConnection) => {\n const currentConnectionState = connectionStateMap[state];\n const sourceStreamId = peerConnection.getSourceStreamId();\n const lastConnectionState = _lastConnectionStatesMap[sourceStreamId]; // check if the last mapped state changed\n\n const isConnectionStateChanged = currentConnectionState && currentConnectionState !== lastConnectionState;\n\n if (isConnectionStateChanged) {\n _lastConnectionStatesMap[sourceStreamId] = currentConnectionState;\n\n if (state === 'disconnected' && sourceStreamId !== 'P2P') {\n // This block of code initiates an iceRestart when a peer connection is disconnected\n // and the socket is still connected.\n setTimeout( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {\n var isSocketReconnecting;\n return _regenerator.default.wrap(function _callee6$(_context6) {\n while (1) switch (_context6.prev = _context6.next) {\n case 0:\n isSocketReconnecting = _session._.isSocketReconnecting;\n\n if (!(_lastConnectionStatesMap[sourceStreamId] === 'disconnected' && socket.is('connected') && !isSocketReconnecting())) {\n _context6.next = 4;\n break;\n }\n\n _context6.next = 4;\n return _this._.iceRestart('peer connection disconnected');\n\n case 4:\n case \"end\":\n return _context6.stop();\n }\n }, _callee6);\n })), 2000);\n }\n\n if (_widgetView) {\n _widgetView.loading(currentConnectionState !== eventNames.SUBSCRIBER_CONNECTED);\n }\n\n logging.debug(`OT.Subscriber.connectionStateChanged to ${state}`);\n this.dispatchEvent(new Events.ConnectionStateChangedEvent(currentConnectionState, this));\n }\n };\n\n const onIceRestartSuccess = () => {\n logResubscribe('Success');\n };\n\n const onIceRestartFailure = () => {\n logResubscribe('Failure', {\n reason: 'ICEWorkflow',\n 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.'\n });\n };\n\n const streamUpdated = event => {\n const video = _widgetView && _widgetView.video();\n\n switch (event.changedProperty) {\n case 'videoDimensions':\n if (!video) {\n // Ignore videoDimensions updates before video is created OPENTOK-17253\n break;\n }\n\n video.orientation({\n width: event.newValue.width,\n height: event.newValue.height,\n videoOrientation: event.newValue.orientation\n });\n this.dispatchEvent(new Events.VideoDimensionsChangedEvent(this, event.oldValue, event.newValue));\n break;\n\n case 'videoDisableWarning':\n if (_chrome) {\n _chrome.videoDisabledIndicator.setWarning(event.newValue);\n }\n\n this.dispatchEvent(new Events.VideoDisableWarningEvent(event.newValue ? 'videoDisableWarning' : 'videoDisableWarningLifted'));\n _congestionLevel = event.newValue === 'videoDisableWarning' ? 1 : null;\n break;\n\n case 'hasVideo':\n // @todo setAudioOnly affects peer connections, what happens with new ones?\n setAudioOnly(!_stream.hasVideo || !_properties.subscribeToVideo); // Makes sure that the audio workaround for Safari is executed\n\n if (env.name === 'Safari' && !_stream.hasVideo && _widgetView) {\n _widgetView.bindAudioTrackOnly();\n }\n\n this.dispatchEvent(new Events.VideoEnabledChangedEvent(_stream.hasVideo ? 'videoEnabled' : 'videoDisabled', {\n reason: 'publishVideo'\n }));\n break;\n\n case 'hasAudio':\n _muteDisplayMode.update();\n\n break;\n\n default:\n }\n }; // Use _stream.getChannelsOfType instead of _webRTCStream.getAudioTracks\n // because its available as soon as Subscriber is instantiated.\n\n\n const _hasAudioTracks = () => _stream.getChannelsOfType('audio').length > 0; // / Chrome\n\n\n _muteDisplayMode = {\n get() {\n // Use buttonDisplayMode if we have an audio track, even if its muted\n return _hasAudioTracks() ? _subscriber.getStyle('buttonDisplayMode') : 'off';\n },\n\n update() {\n const mode = _muteDisplayMode.get();\n\n if (_chrome) {\n _chrome.muteButton.setDisplayMode(mode);\n\n _chrome.backingBar.setMuteMode(mode);\n }\n }\n\n };\n\n const updateChromeForStyleChange = (key, value\n /* , oldValue */\n ) => {\n if (!_chrome) {\n return;\n }\n\n switch (key) {\n case 'nameDisplayMode':\n _chrome.name.setDisplayMode(value);\n\n _chrome.backingBar.setNameMode(value);\n\n break;\n\n case 'videoUnsupportedDisplayMode':\n _chrome.videoUnsupportedIndicator.setDisplayMode(value);\n\n break;\n\n case 'videoDisabledDisplayMode':\n _chrome.videoDisabledIndicator.setDisplayMode(value);\n\n break;\n\n case 'audioBlockedDisplayMode':\n _chrome.audioBlockedIndicator.setDisplayMode(value);\n\n break;\n\n case 'showArchiveStatus':\n _chrome.archive.setShowArchiveStatus(value);\n\n break;\n\n case 'buttonDisplayMode':\n _muteDisplayMode.update();\n\n break;\n\n case 'audioLevelDisplayMode':\n _chrome.audioLevel.setDisplayMode(value);\n\n break;\n\n case 'bugDisplayMode':\n // bugDisplayMode can't be updated but is used by some partners\n break;\n\n case 'backgroundImageURI':\n _widgetView.setBackgroundImageURI(value);\n\n break;\n\n default:\n }\n };\n\n const _createChrome = () => {\n const widgets = {\n backingBar: new BackingBar({\n nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'),\n muteMode: _muteDisplayMode.get()\n }),\n name: new NamePanel({\n name: _properties.name,\n mode: this.getStyle('nameDisplayMode')\n }),\n muteButton: new MuteButton({\n muted: _audioVolume === 0,\n mode: _muteDisplayMode.get()\n })\n };\n\n if (_audioLevelCapable) {\n const audioLevelTransformer = new AudioLevelTransformer();\n _audioLevelMeter = new AudioLevelMeter({\n mode: _subscriber.getStyle('audioLevelDisplayMode')\n });\n const updateAudioLevel = new RafRunner(() => {\n _audioLevelMeter.setValue(audioLevelTransformer.transform(this.loudness));\n });\n\n _audioLevelMeter.watchVisibilityChanged(visible => {\n if (visible) {\n updateAudioLevel.start();\n } else {\n updateAudioLevel.stop();\n }\n });\n\n _audioLevelMeter.setDisplayMode(this.getStyle('audioLevelDisplayMode'));\n\n _audioLevelMeter.audioOnly(false);\n\n widgets.audioLevel = _audioLevelMeter;\n }\n\n widgets.videoDisabledIndicator = new VideoDisabledIndicator({\n mode: this.getStyle('videoDisabledDisplayMode')\n });\n widgets.audioBlockedIndicator = new AudioBlockedIndicator({\n mode: this.getStyle('audioBlockedDisplayMode')\n });\n widgets.videoUnsupportedIndicator = new VideoUnsupportedIndicator({\n mode: this.getStyle('videoUnsupportedDisplayMode')\n });\n\n if (_widgetView && _widgetView.domElement) {\n _chrome = new Chrome({\n parent: _widgetView.domElement\n }).set(widgets).on({\n muted() {\n _subscriber.setAudioVolume(0);\n },\n\n unmuted() {\n _subscriber.setAudioVolume(_latestPositiveVolume);\n }\n\n }, this); // Hide the chrome until we explicitly show it\n\n _chrome.hideWhileLoading();\n }\n };\n\n const _showError = code => {\n let errorMessage;\n let helpMessage; // Display the error message inside the container, assuming it's\n // been created by now.\n\n if (_widgetView) {\n if (code === ExceptionCodes.STREAM_LIMIT_EXCEEDED) {\n errorMessage = 'The stream was unable to connect.';\n helpMessage = 'The limit for the number of media streams has been reached.';\n } else {\n errorMessage = 'The stream was unable to connect due to a network error.';\n\n if (_hasLoadedAtLeastOnce) {\n helpMessage = 'Ensure you have a stable connection and try again.';\n } else {\n helpMessage = 'Make sure you have a stable network connection and that it isn\\'t ' + 'blocked by a firewall.';\n }\n }\n\n _widgetView.addError(errorMessage, helpMessage);\n }\n };\n\n StylableComponent(this, {\n nameDisplayMode: 'auto',\n buttonDisplayMode: 'auto',\n audioLevelDisplayMode: 'auto',\n videoDisabledDisplayMode: 'auto',\n audioBlockedDisplayMode: 'auto',\n backgroundImageURI: null,\n showArchiveStatus: true,\n showMicButton: true\n }, _properties.showControls, payload => {\n logAnalyticsEvent('SetStyle', 'Subscriber', payload, null, 0.1);\n });\n\n function setAudioOnly(audioOnly) {\n getAllPeerConnections().forEach( /*#__PURE__*/function () {\n var _ref14 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(peerConnection) {\n return _regenerator.default.wrap(function _callee7$(_context7) {\n while (1) switch (_context7.prev = _context7.next) {\n case 0:\n _context7.next = 2;\n return peerConnection;\n\n case 2:\n _context7.sent.subscribeToVideo(!audioOnly);\n\n case 3:\n case \"end\":\n return _context7.stop();\n }\n }, _callee7);\n }));\n\n return function (_x8) {\n return _ref14.apply(this, arguments);\n };\n }());\n\n if (_widgetView) {\n _widgetView.audioOnly(audioOnly);\n\n _widgetView.showPoster(audioOnly);\n }\n\n if (_audioLevelMeter) {\n _audioLevelMeter.audioOnly(audioOnly);\n }\n } // logs an analytics event for getStats on the first call\n\n\n const notifyGetStatsCalled = once(() => {\n logAnalyticsEvent('GetStats', 'Called');\n });\n const notifyGetRtcStatsReportCalled = once(() => {\n logAnalyticsEvent('GetRtcStatsReport', 'Called');\n });\n\n const createAudioLevelSampler = peerConnection => {\n if (_audioLevelBehaviour) {\n _audioLevelBehaviour.destroy();\n\n _audioLevelBehaviour = undefined;\n } // No need to create an audio level sampler, if there's no audio tracks\n\n\n if (!_webRTCStream.getAudioTracks().length) {\n return;\n } // prefer the audioLevelSampler (more efficient and better responsiveness)\n\n\n _audioLevelSampler = audioLevelSampler(peerConnection);\n\n if (_audioLevelSampler) {\n _audioLevelBehaviour = audioLevelBehaviour({\n subscriber: this,\n audioLevelSampler: _audioLevelSampler\n });\n } else if (!Object.prototype.hasOwnProperty.call(this, 'loudness')) {\n Object.defineProperty(this, 'loudness', {\n value: undefined,\n configurable: true,\n writable: false\n });\n logging.error('No suitable audio level samplers found, audio level visualisation will not work');\n }\n };\n\n const setPeerConnectionEvents = (peerConnection, sourceStreamId) => {\n if (_peerConnectionEvents[sourceStreamId]) {\n _peerConnectionEvents[sourceStreamId].removeAll();\n\n const onDisconnected = () => {\n peerConnection.off('error', onDisconnected);\n peerConnection.off('disconnected', onDisconnected);\n };\n\n peerConnection.on('error', onDisconnected);\n peerConnection.on('disconnected', onDisconnected);\n }\n\n const peerConnectionEvents = eventHelper(peerConnection);\n peerConnectionEvents.once('connected', () => onPeerConnected(peerConnection));\n peerConnectionEvents.on('disconnected', onPeerDisconnected);\n peerConnectionEvents.on('error', onPeerConnectionFailure);\n peerConnectionEvents.on('remoteStreamAdded', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() {\n var _args8 = arguments;\n return _regenerator.default.wrap(function _callee8$(_context8) {\n while (1) switch (_context8.prev = _context8.next) {\n case 0:\n _context8.prev = 0;\n _context8.next = 3;\n return onRemoteStreamAdded(..._args8);\n\n case 3:\n _context8.next = 8;\n break;\n\n case 5:\n _context8.prev = 5;\n _context8.t0 = _context8[\"catch\"](0);\n logging.error(_context8.t0);\n\n case 8:\n case \"end\":\n return _context8.stop();\n }\n }, _callee8, null, [[0, 5]]);\n })));\n peerConnectionEvents.on('audioLevelStuckWarning', onAudioLevelStuckWarning);\n peerConnectionEvents.on('remoteStreamRemoved', onRemoteStreamRemoved);\n peerConnectionEvents.on('signalingStateStable', /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9() {\n var video;\n return _regenerator.default.wrap(function _callee9$(_context9) {\n while (1) switch (_context9.prev = _context9.next) {\n case 0:\n _subscriber.trigger('signalingStateStable');\n\n video = _widgetView && _widgetView.video();\n\n if (video && _webRTCStream) {\n if (_audioLevelSampler && 'webRTCStream' in _audioLevelSampler && _webRTCStream.getAudioTracks().length > 0) {\n _audioLevelSampler.webRTCStream(_webRTCStream);\n }\n }\n\n case 3:\n case \"end\":\n return _context9.stop();\n }\n }, _callee9);\n })));\n peerConnectionEvents.on('iceConnectionStateChange', state => onIceConnectionStateChange(state, peerConnection));\n peerConnectionEvents.on('iceRestartSuccess', onIceRestartSuccess);\n peerConnectionEvents.on('iceRestartFailure', onIceRestartFailure);\n peerConnectionEvents.on('remoteVideoSupported', onRemoteVideoSupported);\n peerConnectionEvents.once('remoteStreamAdded', createAudioLevelSampler.bind(this, peerConnection));\n _peerConnectionEvents[sourceStreamId] = _peerConnectionEvents;\n };\n\n this._destroy = (_ref17) => {\n let _ref17$reason = _ref17.reason,\n reason = _ref17$reason === void 0 ? 'Unknown' : _ref17$reason,\n quiet = _ref17.quiet,\n _ref17$noStateTransit = _ref17.noStateTransition,\n noStateTransition = _ref17$noStateTransit === void 0 ? false : _ref17$noStateTransit;\n\n if (_state.isDestroyed()) {\n return this;\n }\n\n _state.set('Destroyed');\n\n if (_frameRateWatcher) {\n _frameRateWatcher.destroy();\n\n _frameRateWatcher = null;\n }\n\n _preDisconnectStats = {\n sessionId: _session.sessionId,\n connectionId: _session && _session.isConnected() ? _session.connection.connectionId : null,\n partnerId: _session && _session.sessionInfo ? _session.sessionInfo.partnerId : null,\n streamId: _stream && !_stream.destroyed ? _stream.id : null\n };\n\n this._disconnect({\n reason,\n noStateTransition\n });\n\n if (_chrome) {\n _chrome.destroy();\n\n _chrome = null;\n }\n\n if (_widgetView) {\n _widgetView.destroy();\n\n _widgetView.off();\n\n _widgetView = null;\n this.element = null;\n }\n\n if (_stream && !_stream.destroyed) {\n logAnalyticsEvent('unsubscribe', null, {\n streamId: _stream.id\n });\n }\n\n _stream.off(_streamEventHandlers, this);\n\n this.session.off('gsmCallEnded', onGsmCallEnded);\n this.id = null;\n _domId = null;\n this.stream = null;\n _stream = null;\n this.streamId = null;\n this.session = null;\n _session = null;\n _properties = null;\n\n if (quiet !== true) {\n this.dispatchEvent(new Events.DestroyedEvent(eventNames.SUBSCRIBER_DESTROYED, this, reason));\n this.off();\n }\n\n return this;\n };\n\n this.destroy = function (reason, quiet) {\n if (reason === void 0) {\n reason = 'Unsubscribe';\n }\n\n logging.warn('Subscriber#destroy is deprecated and will be removed. Please use Session#unsubscribe instead');\n\n _this._destroy({\n reason,\n quiet\n });\n };\n\n this._disconnect = function (_temp4) {\n let _ref18 = _temp4 === void 0 ? {} : _temp4,\n _ref18$reason = _ref18.reason,\n reason = _ref18$reason === void 0 ? 'Unknown' : _ref18$reason,\n noStateTransition = _ref18.noStateTransition;\n\n // known reasons:\n // forceUnpublished (publisher stream was destroyed by forceUnpublish)\n // clientDisconnected (publisher unpublished)\n // Unsubscribe (when calling session.unsubscribe)\n // Unknown (when reason is not determined)\n if (!noStateTransition && !connectivityState.is('disconnected')) {\n const error = reason === 'Unsubscribe' ? undefined : otError(errors.STREAM_DESTROYED, new Error('Stream was destroyed before it could be subscribed to'));\n connectivityState.disconnect({\n payload: {\n reason\n },\n error\n });\n }\n\n if (!_state.isDestroyed() && !_state.isFailed()) {\n // If we are already in the destroyed state then disconnect\n // has been called after (or from within) destroy.\n _state.set('NotSubscribing');\n }\n\n if (_widgetView) {\n _widgetView.destroyVideo();\n } // @todo check peer connection destroy triggers disconnect, and then gets logged...\n\n\n getAllPeerConnections().forEach( /*#__PURE__*/function () {\n var _ref19 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee10(peerConnection) {\n return _regenerator.default.wrap(function _callee10$(_context10) {\n while (1) switch (_context10.prev = _context10.next) {\n case 0:\n _context10.next = 2;\n return peerConnection;\n\n case 2:\n _context10.sent.destroy();\n\n case 3:\n case \"end\":\n return _context10.stop();\n }\n }, _callee10);\n }));\n\n return function (_x9) {\n return _ref19.apply(this, arguments);\n };\n }());\n Object.keys(peerConnectionsAsync).forEach(key => {\n delete peerConnectionsAsync[key];\n }); // Unsubscribe us from the stream, if it hasn't already been destroyed\n\n if (socket.is('connected') && _stream && !_stream.destroyed) {\n // Notify the server components\n // @todo I assume we don't want to send this message, but is there anything\n // we need to do in p2p->mantis for when a peer conn is destroyed?\n socket.subscriberDestroy(_stream.id, _this.widgetId);\n }\n };\n\n this.disconnect = () => {\n logging.warn('Subscriber#disconnect is deprecated and will be removed. Please use Session#unsubscribe instead');\n\n this._disconnect({\n reason: 'Unsubscribe'\n });\n };\n\n const processOffer = /*#__PURE__*/function () {\n var _ref21 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee11(_ref20) {\n var peerId, fromConnectionId, sourceStreamId, uri, send, log, logQoS;\n return _regenerator.default.wrap(function _callee11$(_context11) {\n while (1) switch (_context11.prev = _context11.next) {\n case 0:\n peerId = _ref20.peerId, fromConnectionId = _ref20.fromConnectionId, sourceStreamId = _ref20.sourceStreamId;\n\n if (getPeerConnectionBySourceStreamId(sourceStreamId)) {\n _context11.next = 8;\n break;\n }\n\n uri = constructSubscriberUri({\n apiKey: _session.apiKey,\n sessionId: _session.sessionId,\n streamId: _stream.id,\n subscriberId: _this.widgetId\n });\n send = createSendMethod({\n socket: _this.session._.getSocket(),\n uri,\n content: {\n peerId,\n sourceStreamId\n }\n });\n\n log = function log(action, variation, payload, options, throttle) {\n if (options === void 0) {\n options = {};\n }\n\n const transformedOptions = (0, _extends2.default)({\n peerId,\n sourceStreamId\n }, options);\n return logAnalyticsEvent(action, variation, payload, transformedOptions, throttle);\n };\n\n logQoS = qos => {\n // We only log data from the active peer connection\n if (_activeSourceStreamId !== sourceStreamId) {\n return;\n }\n\n recordQOS((0, _extends2.default)({}, qos, {\n peerId,\n remoteConnectionId: fromConnectionId,\n sourceStreamId\n }));\n };\n\n _context11.next = 8;\n return _this._setupPeerConnection({\n send,\n log,\n logQoS,\n sourceStreamId\n });\n\n case 8:\n case \"end\":\n return _context11.stop();\n }\n }, _callee11);\n }));\n\n return function processOffer(_x10) {\n return _ref21.apply(this, arguments);\n };\n }();\n\n this.processMessage = /*#__PURE__*/function () {\n var _ref22 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee12(type, fromConnectionId, message) {\n var peerId, sourceStreamId, peerConnection;\n return _regenerator.default.wrap(function _callee12$(_context12) {\n while (1) switch (_context12.prev = _context12.next) {\n case 0:\n logging.debug(`OT.Subscriber.processMessage: Received ${type} message from ${fromConnectionId}`);\n logging.debug(message);\n peerId = get(message, 'content.peerId');\n sourceStreamId = get(message, 'content.sourceStreamId');\n\n if (!(type === 'offer')) {\n _context12.next = 7;\n break;\n }\n\n _context12.next = 7;\n return processOffer({\n peerId,\n fromConnectionId,\n sourceStreamId\n });\n\n case 7:\n _context12.next = 9;\n return getPeerConnectionBySourceStreamId(sourceStreamId);\n\n case 9:\n peerConnection = _context12.sent;\n\n if (peerConnection) {\n peerConnection.processMessage(type, message);\n }\n\n case 11:\n case \"end\":\n return _context12.stop();\n }\n }, _callee12);\n }));\n\n return function (_x11, _x12, _x13) {\n return _ref22.apply(this, arguments);\n };\n }();\n\n this.disableVideo = active => {\n if (!active) {\n logging.warn('Due to high packet loss and low bandwidth, video has been disabled');\n } else if (_lastSubscribeToVideoReason === 'auto') {\n logging.info('Video has been re-enabled');\n } else {\n logging.info('Video was not re-enabled because it was manually disabled');\n return;\n }\n\n this.subscribeToVideo(active, 'auto');\n const payload = active ? {\n videoEnabled: true\n } : {\n videoDisabled: true\n };\n logAnalyticsEvent('updateQuality', 'video', payload);\n };\n /**\n * Returns the base-64-encoded string of PNG data representing the Subscriber video.\n *\n * You can use the string as the value for a data URL scheme passed to the src parameter of\n * an image file, as in the following:
\n *\n * \n * var imgData = subscriber.getImgData();\n *\n * var img = document.createElement(\"img\");\n * img.setAttribute(\"src\", \"data:image/png;base64,\" + imgData);\n * var imgWin = window.open(\"about:blank\", \"Screenshot\");\n * imgWin.document.write(\"<body></body>\");\n * imgWin.document.body.appendChild(img);\n *
\n * @method #getImgData\n * @memberOf Subscriber\n * @return {String} The base-64 encoded string. Returns an empty string if there is no video.\n */\n\n\n this.getImgData = () => {\n if (!this.isSubscribing()) {\n logging.error('OT.Subscriber.getImgData: Cannot getImgData before the Subscriber ' + 'is subscribing.');\n return null;\n }\n\n const video = _widgetView && _widgetView.video();\n\n return video ? video.imgData() : null;\n };\n /**\n * Returns the details on the subscriber stream quality, including the following:\n *\n * \n *\n * - Total audio and video packets lost
\n * - Total audio and video packets received
\n * - Total audio and video bytes received
\n * - Current video frame rate
\n *\n *
\n *\n * You can publish a test stream, subscribe to it (on the publishing client), and use this method\n * to check its quality. Based on the stream's quality, you can determine what video resolution is\n * supported and whether conditions support video or audio. You can then publish an appropriate\n * stream, based on the results. When using this method to test a stream published by your\n * own client, set the testNetwork
property to true
in the options you\n * pass into the Session.subscribe() method. For an example,\n * see the opentok-network-test\n * project on GitHub.\n * \n * You may also use these statistics to have a Subscriber subscribe to audio-only if the audio\n * packet loss reaches a certain threshold. If you choose to do this, you should set the\n * audioFallbackEnabled
setting to false
when you initialize Publisher\n * objects for the session. This prevents the OpenTok Media Router from using its own audio-only\n * toggling implementation. (See the documentation for the\n * OT.initPublisher() method.)\n *\n * @param {Function} completionHandler A function that takes the following\n * parameters:\n *\n *
\n *\n * error
(Error) — The error property is\n * set if the client is not connected. Upon completion of a successful call to the method,\n * this property is undefined. \n *\n * stats
(Object) — An object with the following properties:\n * \n *
\n * audio.bytesReceived
(Number) — The total number of audio bytes\n * received by the subscriber \n * audio.packetsLost
(Number) — Total audio packets that did not reach\n * the subscriber \n * audio.packetsReceived
(Number) — The total number of audio packets\n * received by the subscriber \n * timestamp
(Number) — The timestamp, in milliseconds since the Unix\n * epoch, for when these stats were gathered \n * video.bytesReceived
(Number) — The total video bytes received by\n * the subscriber \n * video.packetsLost
(Number) — The total number of video packets that\n * did not reach the subscriber \n * video.packetsReceived
(Number) — The total number of video packets\n * received by the subscriber \n * video.frameRate
(Number) — The current video frame rate \n *
\n * \n *
\n *\n * @see Publisher.getStats()\n * @see Publisher.getRtcStatsReport()\n *\n * @method #getStats\n * @memberOf Subscriber\n */\n\n\n this.getStats = callback => {\n _getStats((error, stats) => {\n if (error) {\n callback(error);\n return;\n }\n\n const otStats = {\n timestamp: 0\n };\n stats.forEach(stat => {\n if (getStatsHelpers.isInboundStat(stat)) {\n const video = getStatsHelpers.isVideoStat(stat, stats);\n const audio = getStatsHelpers.isAudioStat(stat, stats); // it is safe to override the timestamp of one by another\n // if they are from the same getStats \"batch\" video and audio ts have the same value\n\n if (audio || video) {\n otStats.timestamp = getStatsHelpers.normalizeTimestamp(stat.timestamp);\n }\n\n if (video) {\n merge(otStats, {\n video: getStatsHelpers.parseStatCategory(stat)\n });\n } else if (audio) {\n merge(otStats, {\n audio: getStatsHelpers.parseStatCategory(stat)\n });\n }\n } else if (getStatsHelpers.isVideoTrackStat(stat)) {\n let frameRate = stat.framesPerSecond;\n\n if (_frameRateWatcher) {\n if (frameRate) {\n _frameRateWatcher.destroy();\n\n _frameRateWatcher = null;\n } else {\n frameRate = _frameRateWatcher.getFrameRateFromStats(stats);\n }\n }\n\n merge(otStats, {\n video: {\n frameRate\n }\n });\n }\n });\n callback(null, otStats);\n });\n };\n /**\n * Returns a promise that, on success, resolves with an RtcStatsReport object\n * for the subscribed stream. (See\n * \n * RTCStatsReport.)\n * \n * The Promise will be rejected in the following conditions:\n *
\n * \n *\n * @method #getRtcStatsReport\n * @memberOf Subscriber\n *\n * @see Publisher.getRtcStatsReport()\n *\n * @return {Promise} A promise that resolves when the operation completes successfully.\n * If there is an error, the promise is rejected.\n */\n\n\n this.getRtcStatsReport = () => new Promise((resolve, reject) => {\n _getRtcStatsReport((error, rtcStatsReport) => {\n if (error) {\n reject(error);\n } else {\n resolve(rtcStatsReport);\n }\n });\n });\n\n function setAudioVolume(audioVolume) {\n const video = _widgetView && _widgetView.video();\n\n if (video) {\n try {\n video.setAudioVolume(audioVolume);\n } catch (e) {\n logging.warn(`setAudioVolume: ${e}`);\n\n if (_audioVolume === 0) {\n logging.info('Using subscribeToAudio to mute Audio because setAudioVolume(0) failed'); // If we can't set the audioVolume to 0 then at least mute by setting subscribeToAudio(false)\n\n _subscriber.subscribeToAudio(false);\n }\n }\n }\n\n if (_chrome) {\n _chrome.muteButton.muted(audioVolume === 0);\n }\n }\n /**\n * Sets the audio volume, between 0 and 100, of the Subscriber.\n *\n * You can set the initial volume when you call the Session.subscribe()
\n * method. Pass a audioVolume
property of the properties
parameter\n * of the method.
\n *\n * @param {Number} value The audio volume, between 0 and 100.\n *\n * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the\n * following:\n *\n * mySubscriber.setAudioVolume(50).setStyle(newStyle);
\n *\n * @see getAudioVolume()\n * @see Session.subscribe()\n * @method #setAudioVolume\n * @memberOf Subscriber\n */\n\n\n this.setAudioVolume = requestedVolume => {\n const volume = normalizeAudioVolume(requestedVolume);\n logAnalyticsEvent('setAudioVolume', 'Attempt', {\n audioVolume: volume\n });\n\n if (isNaN(volume)) {\n logging.error('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100');\n logAnalyticsEvent('setAudioVolume', 'Failure', {\n message: 'value should be an integer between 0 and 100'\n });\n return this;\n }\n\n if (volume !== requestedVolume) {\n logging.warn('OT.Subscriber.setAudioVolume: value should be an integer between 0 and 100');\n }\n\n if (volume === _audioVolume) {\n setAudioVolume(_audioVolume);\n logAnalyticsEvent('setAudioVolume', 'Success', {\n audioVolume: _audioVolume,\n message: 'Requested volume is same as already set audioVolume'\n });\n return this;\n }\n\n if (_audioVolume > 0) {\n _latestPositiveVolume = _audioVolume;\n }\n\n _audioVolume = volume;\n setAudioVolume(_audioVolume);\n\n if (_audioVolume > 0 && !_isSubscribingToAudio) {\n // in Firefox (and others) we don't stop subscribing to audio when muted, however if we are 'unmuting' and in\n // the subscribeToAudio: false state we should subscribe to audio again\n // subscribeToAudio is going to call us with _latestPositiveVolume so we'll update it here\n _latestPositiveVolume = _audioVolume;\n this.subscribeToAudio(true);\n }\n\n logAnalyticsEvent('setAudioVolume', 'Success', {\n audioVolume: _audioVolume\n });\n return this;\n };\n /**\n * Returns the audio volume, between 0 and 100, of the Subscriber.\n *\n * Generally you use this method in conjunction with the setAudioVolume()
\n * method.
\n *\n * @return {Number} The audio volume, between 0 and 100, of the Subscriber.\n * @see setAudioVolume()\n * @method #getAudioVolume\n * @memberOf Subscriber\n */\n\n\n this.getAudioVolume = () => {\n const video = _widgetView && _widgetView.video();\n\n if (video) {\n try {\n return video.getAudioVolume();\n } catch (e) {\n logging.warn(`getAudioVolume ${e}`);\n }\n }\n\n return _audioVolume;\n };\n /**\n * Toggles audio on and off. Starts subscribing to audio (if it is available and currently\n * not being subscribed to) when the value
is true
; stops\n * subscribing to audio (if it is currently being subscribed to) when the value
\n * is false
.\n * \n * Note: This method only affects the local playback of audio. It has no impact on the\n * audio for other connections subscribing to the same stream. If the Publisher is not\n * publishing audio, enabling the Subscriber audio will have no practical effect.\n *
\n *\n * @param {Boolean} value Whether to start subscribing to audio (true
) or not\n * (false
).\n *\n * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the\n * following:\n *\n * mySubscriber.subscribeToAudio(true).subscribeToVideo(false);
\n *\n * @see subscribeToVideo()\n * @see Session.subscribe()\n * @see StreamPropertyChangedEvent\n *\n * @method #subscribeToAudio\n * @memberOf Subscriber\n */\n\n\n this.subscribeToAudio = pValue => {\n const value = castToBoolean(pValue, true);\n getAllPeerConnections().forEach( /*#__PURE__*/function () {\n var _ref23 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee13(peerConnection) {\n return _regenerator.default.wrap(function _callee13$(_context13) {\n while (1) switch (_context13.prev = _context13.next) {\n case 0:\n _context13.next = 2;\n return peerConnection;\n\n case 2:\n _context13.sent.subscribeToAudio(value);\n\n case 3:\n case \"end\":\n return _context13.stop();\n }\n }, _callee13);\n }));\n\n return function (_x14) {\n return _ref23.apply(this, arguments);\n };\n }());\n\n if (_stream && getAllPeerConnections().length !== 0) {\n _stream.setChannelActiveState('audio', value);\n }\n\n const changed = _isSubscribingToAudio !== value;\n _isSubscribingToAudio = value;\n\n if (changed) {\n this.setAudioVolume(value ? _latestPositiveVolume : 0);\n }\n\n logAnalyticsEvent('subscribeToAudio', 'Event', {\n subscribeToAudio: value\n });\n return this;\n };\n\n const reasonMap = {\n auto: 'quality',\n publishVideo: 'publishVideo',\n subscribeToVideo: 'subscribeToVideo'\n };\n\n if (env.name === 'Safari') {\n const onVisibilityChange = () => {\n if (!document.hidden && _properties.subscribeToVideo) {\n logging.debug('document visibility restored in Safari - resubscribing to video');\n this.subscribeToVideo(false);\n this.subscribeToVideo(true);\n }\n };\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n this.once('destroyed', () => {\n document.removeEventListener('visibilitychange', onVisibilityChange);\n });\n }\n /**\n * Toggles video on and off. Starts subscribing to video (if it is available and\n * currently not being subscribed to) when the value
is true
;\n * stops subscribing to video (if it is currently being subscribed to) when the\n * value
is false
.\n * \n * Note: This method only affects the local playback of video. It has no impact on\n * the video for other connections subscribing to the same stream. If the Publisher is not\n * publishing video, enabling the Subscriber video will have no practical effect.\n *
\n *\n * @param {Boolean} value Whether to start subscribing to video (true
) or not\n * (false
).\n *\n * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the\n * following:\n *\n * mySubscriber.subscribeToVideo(true).subscribeToAudio(false);
\n *\n * @see subscribeToAudio()\n * @see Session.subscribe()\n * @see StreamPropertyChangedEvent\n *\n * @method #subscribeToVideo\n * @memberOf Subscriber\n */\n\n\n this.subscribeToVideo = (pValue, reason) => {\n const value = castToBoolean(pValue, true);\n logAnalyticsEvent('subscribeToVideo', 'Attempt', {\n subscribeToVideo: value,\n reason\n });\n setAudioOnly(!value || !_stream.hasVideo || !(_webRTCStream && _webRTCStream.getVideoTracks().length > 0));\n\n if (_stream.hasVideo && _webRTCStream && _webRTCStream.getVideoTracks().length === 0) {\n if (value) {\n logging.info('Subscriber is audio-only due to incompatibility, can\\'t subscribeToVideo.');\n }\n\n _properties.subscribeToVideo = false;\n logAnalyticsEvent('subscribeToVideo', 'Failure', {\n message: 'No video tracks available'\n });\n return this;\n }\n\n if (_chrome && _chrome.videoDisabledIndicator) {\n // If this is an auto disableVideo then we want to show the indicator, otherwise hide it again\n _chrome.videoDisabledIndicator.disableVideo(reason === 'auto' && !value);\n }\n\n if (getAllPeerConnections().length > 0) {\n if (_session && _stream && (value !== _properties.subscribeToVideo || reason !== _lastSubscribeToVideoReason)) {\n _stream.setChannelActiveState('video', value, reason);\n }\n }\n\n _properties.subscribeToVideo = value;\n _lastSubscribeToVideoReason = reason;\n logAnalyticsEvent('subscribeToVideo', 'Success', {\n subscribeToVideo: value,\n reason\n });\n\n if (reason !== 'loading') {\n this.dispatchEvent(new Events.VideoEnabledChangedEvent(value ? 'videoEnabled' : 'videoDisabled', {\n reason: reasonMap[reason] || 'subscribeToVideo'\n }));\n\n if (value === 'videoDisabled' && reason === 'auto') {\n _congestionLevel = 2;\n }\n }\n\n return this;\n };\n /**\n * Sets the preferred resolution of the subscriber's video.\n * \n * Lowering the preferred resolution\n * lowers video quality on the subscribing client, but it also reduces network and CPU usage.\n * You may want to use a lower resolution based on the dimensions of subscriber's video on\n * the web page. You may want to use a resolution rate for a subscriber to a stream that is less\n * important (and smaller) than other streams.\n *
\n *
\n * This method only applies when subscribing to a stream that uses the\n * \n * scalable video feature. Scalable video is available:\n *
\n * - \n * Only in sessions that use the OpenTok Media Router (sessions with the\n * media\n * mode set to routed).\n *
\n * - \n * Only for streams published by clients that support scalable video:\n * clients that use the OpenTok iOS SDK (on certain devices), the OpenTok\n * Android SDK (on certain devices), or OpenTok.js in Chrome and Safari.\n *
\n *
\n * \n * In streams that do not use scalable video, calling this method has no effect.\n *
\n * Note: The resolution for scalable video streams automatically adjusts for each\n * subscriber, based on network conditions and CPU usage, even if you do not call this method.\n * Call this method if you want to set a maximum resolution for this subscriber.\n *
\n * In streams that do not use scalable video, calling this method has no effect.\n *
\n * Not every frame rate is available to a subscriber. When you set the preferred resolution for\n * the subscriber, OpenTok.js picks the best resolution available that matches your setting.\n * The resolutions available are based on the value of the Subscriber object's\n * stream.resolution
property, which represents the maximum resolution available for\n * the stream. The actual resolutions available depend, dynamically, on network and CPU resources\n * available to the publisher and subscriber.\n *
\n * You can set the initial preferred resolution used by setting the\n * preferredResolution
property of the options
object you pass into the\n * Session.subscribe()
method.\n *\n * @param {Object} resolution Set this to an object with two properties: width
and\n * height
(both numbers), such as {width: 320, height: 240}
. Set this to\n * null
to remove the preferred resolution, and the client will use the highest\n * resolution available.\n *\n * @see Subscriber.setPreferredFrameRate()\n * @see Session.subscribe()\n *\n * @method #setPreferredResolution\n * @memberOf Subscriber\n */\n\n\n this.setPreferredResolution = preferredResolution => {\n if (_state.isDestroyed() || getAllPeerConnections().length === 0 && !_state.current === 'Connecting') {\n logging.warn('Cannot set the max Resolution when not subscribing to a publisher');\n return;\n }\n\n _properties.preferredResolution = preferredResolution;\n\n if (_session.sessionInfo.p2pEnabled) {\n logging.warn('OT.Subscriber.setPreferredResolution will not work in a P2P Session');\n return;\n } // We can only setPreferredResolution once the remote stream has been added. Otherwise we will save\n // the preferred resolution, and then it will be requested once the stream has been added.\n\n\n if (!_state.isStreamAdded()) {\n return;\n }\n\n const curMaxResolution = _stream.getPreferredResolution();\n\n const isUnchanged = curMaxResolution && preferredResolution && curMaxResolution.width === preferredResolution.width && curMaxResolution.height === preferredResolution.height || !curMaxResolution && !preferredResolution;\n\n if (isUnchanged) {\n return;\n }\n\n _stream.setPreferredResolution(preferredResolution);\n };\n /**\n * Sets the preferred frame rate of the subscriber's video.\n *
\n * Lowering the preferred frame rate\n * lowers video quality on the subscribing client, but it also reduces network and CPU usage.\n * You may want to use a lower frame rate for a subscriber to a stream that is less important\n * than other streams.\n *
\n *
\n * This method only applies when subscribing to a stream that uses the\n * \n * scalable video feature. Scalable video is available:\n *
\n * - \n * Only in sessions that use the OpenTok Media Router (sessions with the\n * media\n * mode set to routed).\n *
\n * - \n * Only for streams published by clients that support scalable video:\n * clients that use the OpenTok iOS SDK (on certain devices), the OpenTok\n * Android SDK (on certain devices), or OpenTok.js in Chrome and Safari.\n *
\n *
\n * \n * In streams that do not use scalable video, calling this method has no effect.\n *
\n * Note: The frame rate for scalable video streams automatically adjusts for each\n * subscriber, based on network conditions and CPU usage, even if you do not call this method.\n * Call this method if you want to set a maximum frame rate for this subscriber.\n *
\n * Not every frame rate is available to a subscriber. When you set the preferred frame rate for\n * the subscriber, OpenTok.js picks the best frame rate available that matches your setting.\n * The frame rates available are based on the value of the Subscriber object's\n * stream.frameRate
property, which represents the maximum value available for the\n * stream. The actual frame rates available depend, dynamically, on network and CPU resources\n * available to the publisher and subscriber.\n *
\n * You can set the initial preferred frame rate used by setting the preferredFrameRate
\n * property of the options
object you pass into the Session.subscribe()
\n * method.\n *\n * @param {Number} frameRate Set this to the desired frame rate (in frames per second). Set this to\n * null
to remove the preferred frame rate, and the client will use the highest\n * frame rate available.\n *\n * @see Subscriber.setPreferredResolution()\n * @see Session.subscribe()\n *\n * @method #setPreferredFrameRate\n * @memberOf Subscriber\n */\n\n\n this.setPreferredFrameRate = preferredFrameRate => {\n if (_state.isDestroyed() || getAllPeerConnections().length === 0 && !_state.current === 'Connecting') {\n logging.warn('Cannot set the max frameRate when not subscribing to a publisher');\n return;\n }\n\n _properties.preferredFrameRate = preferredFrameRate;\n\n if (_session.sessionInfo.p2pEnabled) {\n logging.warn('OT.Subscriber.setPreferredFrameRate will not work in a P2P Session');\n return;\n }\n\n const currentPreferredFrameRate = _stream.getPreferredFrameRate();\n\n const isUnchangedFrameRate = preferredFrameRate && currentPreferredFrameRate && currentPreferredFrameRate === preferredFrameRate || !currentPreferredFrameRate && !preferredFrameRate;\n\n if (isUnchangedFrameRate) {\n return;\n }\n\n _stream.setPreferredFrameRate(preferredFrameRate);\n };\n\n this.isSubscribing = () => _state.isSubscribing();\n\n this.isWebRTC = true;\n\n this.isLoading = () => _widgetView && _widgetView.loading();\n /**\n * Indicates whether the subscriber's audio is blocked because of\n * the browser's audio autoplay policy.\n *\n * @see OT.unblockAudio()\n * @see The audioBlocked\n * and audioUnblocked\n * Subscriber events\n * @see The style.audioBlockedDisplayMode
property of the\n * options
parameter of the\n * Session.subscribe() method\n *\n * @method #isAudioBlocked\n * @memberof Subscriber\n */\n\n\n this.isAudioBlocked = () => Boolean(_widgetView && _widgetView.isAudioBlocked());\n\n this.videoElement = () => {\n const video = _widgetView && _widgetView.video();\n\n return video ? video.domElement() : null;\n };\n /**\n * Returns the width, in pixels, of the Subscriber video.\n *\n * @method #videoWidth\n * @memberOf Subscriber\n * @return {Number} the width, in pixels, of the Subscriber video.\n */\n\n\n this.videoWidth = () => {\n const video = _widgetView && _widgetView.video();\n\n return video ? video.videoWidth() : undefined;\n };\n /**\n * Returns the height, in pixels, of the Subscriber video.\n *\n * @method #videoHeight\n * @memberOf Subscriber\n * @return {Number} the width, in pixels, of the Subscriber video.\n */\n\n\n this.videoHeight = () => {\n const video = _widgetView && _widgetView.video();\n\n return video ? video.videoHeight() : undefined;\n };\n\n this._subscribeToSelf = /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee14() {\n var publisher;\n return _regenerator.default.wrap(function _callee14$(_context14) {\n while (1) switch (_context14.prev = _context14.next) {\n case 0:\n publisher = _session.getPublisherForStream(_stream);\n\n if (publisher && publisher._.webRtcStream()) {\n _context14.next = 5;\n break;\n }\n\n connectivityState.fail({\n payload: {\n reason: 'streamNotFound'\n },\n error: otError(errors.STREAM_DESTROYED, new Error('Tried to subscribe to a local publisher, but its stream no longer exists'))\n });\n _context14.next = 14;\n break;\n\n case 5:\n _pcConnected.resolve();\n\n _context14.prev = 6;\n _context14.next = 9;\n return onRemoteStreamAdded(publisher._.webRtcStream());\n\n case 9:\n _context14.next = 14;\n break;\n\n case 11:\n _context14.prev = 11;\n _context14.t0 = _context14[\"catch\"](6);\n logging.error(_context14.t0);\n\n case 14:\n case \"end\":\n return _context14.stop();\n }\n }, _callee14, null, [[6, 11]]);\n }));\n\n this._setupPeerConnection = /*#__PURE__*/function () {\n var _ref26 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee15(_ref25) {\n var send, log, logQoS, sourceStreamId;\n return _regenerator.default.wrap(function _callee15$(_context15) {\n while (1) switch (_context15.prev = _context15.next) {\n case 0:\n send = _ref25.send, log = _ref25.log, logQoS = _ref25.logQoS, sourceStreamId = _ref25.sourceStreamId;\n\n if (_properties.testNetwork) {\n _this.setAudioVolume(0);\n }\n\n if (getAllPeerConnections().length === 0) {\n // @todo The subscribers states should be something like:\n // disconnected || connecting || reconnecting || connected || destroyed\n // the state of peer connections belong to the peer connection itself\n _state.set('Connecting');\n }\n\n _activeSourceStreamId = sourceStreamId;\n peerConnectionsAsync[sourceStreamId] = new Promise((resolve, reject) => {\n _session._.getIceConfig().then(iceConfig => {\n if (iceConfig.needRumorIceServersFallback) {\n iceConfig.servers = [...(fallbackIceServers || []), ...(iceConfig.servers || [])];\n }\n\n const props = {\n iceConfig,\n subscriberId: _this.widgetId,\n send,\n logAnalyticsEvent: log,\n p2p: _session.sessionInfo.p2pEnabled,\n sourceStreamId\n };\n\n if (Object.prototype.hasOwnProperty.call(_properties, 'codecFlags')) {\n props.codecFlags = _properties.codecFlags;\n }\n\n const peerConnection = new Subscriber.SubscriberPeerConnection(props);\n peerConnection.once('iceConnected', _pcConnected.resolve);\n peerConnection.once('error', _pcConnected.reject);\n peerConnection.on('qos', logQoS);\n setPeerConnectionEvents(peerConnection, sourceStreamId); // initialize the peer connection AFTER we've added the event listeners\n\n peerConnection.init(err => {\n if (err) {\n reject(err);\n } else {\n resolve(peerConnection);\n }\n });\n });\n });\n return _context15.abrupt(\"return\", peerConnectionsAsync[sourceStreamId]);\n\n case 6:\n case \"end\":\n return _context15.stop();\n }\n }, _callee15);\n }));\n\n return function (_x15) {\n return _ref26.apply(this, arguments);\n };\n }();\n /**\n * Restricts the frame rate of the Subscriber's video stream, when you pass in\n * true
. When you pass in false
, the frame rate of the video stream\n * is not restricted.\n *
\n * When the frame rate is restricted, the Subscriber video frame will update once or less per\n * second.\n *
\n * This feature is only available in sessions that use the OpenTok Media Router (sessions with\n * the media mode\n * set to routed), not in sessions with the media mode set to relayed. In relayed sessions,\n * calling this method has no effect.\n *
\n * Restricting the subscriber frame rate has the following benefits:\n *
\n * - It reduces CPU usage.
\n * - It reduces the network bandwidth consumed.
\n * - It lets you subscribe to more streams simultaneously.
\n *
\n * \n * Reducing a subscriber's frame rate has no effect on the frame rate of the video in\n * other clients.\n *\n * @param {Boolean} value Whether to restrict the Subscriber's video frame rate\n * (true
) or not (false
).\n *\n * @return {Subscriber} The Subscriber object. This lets you chain method calls, as in the\n * following:\n *\n *
mySubscriber.restrictFrameRate(false).subscribeToAudio(true);
\n *\n * @method #restrictFrameRate\n * @memberOf Subscriber\n */\n\n\n this.restrictFrameRate = val => {\n logging.debug(`OT.Subscriber.restrictFrameRate(${val})`);\n logAnalyticsEvent('restrictFrameRate', val.toString(), {\n streamId: _stream.id\n });\n\n if (_session.sessionInfo.p2pEnabled) {\n logging.warn('OT.Subscriber.restrictFrameRate: Cannot restrictFrameRate on a P2P session');\n }\n\n if (typeof val !== 'boolean') {\n logging.error(`OT.Subscriber.restrictFrameRate: expected a boolean value got a ${typeof val}`);\n } else {\n _frameRateRestricted = val;\n\n _stream.setRestrictFrameRate(val);\n }\n\n return this;\n };\n\n this.on('styleValueChanged', updateChromeForStyleChange, this);\n this._ = {\n getDataChannel(label, options, completion) {\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee16() {\n return _regenerator.default.wrap(function _callee16$(_context16) {\n while (1) switch (_context16.prev = _context16.next) {\n case 0:\n if (getCurrentPeerConnection()) {\n _context16.next = 3;\n break;\n }\n\n completion(new OTHelpers.Error('Cannot create a DataChannel before there is a publisher connection.'));\n return _context16.abrupt(\"return\");\n\n case 3:\n _context16.next = 5;\n return getCurrentPeerConnection();\n\n case 5:\n _context16.sent.getDataChannel(label, options, completion);\n\n case 6:\n case \"end\":\n return _context16.stop();\n }\n }, _callee16);\n }))();\n },\n\n iceRestart(reason) {\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee17() {\n var peerConnection;\n return _regenerator.default.wrap(function _callee17$(_context17) {\n while (1) switch (_context17.prev = _context17.next) {\n case 0:\n _context17.next = 2;\n return getPeerConnectionBySourceStreamId('MANTIS');\n\n case 2:\n peerConnection = _context17.sent;\n\n if (!peerConnection) {\n logging.debug('Subscriber: Skipping ice restart, we have no peer connection');\n } else {\n logResubscribe('Attempt', {\n reason\n });\n logging.debug('Subscriber: iceRestart attempt');\n peerConnection.iceRestart();\n }\n\n case 4:\n case \"end\":\n return _context17.stop();\n }\n }, _callee17);\n }))();\n },\n\n unblockAudio: () => _widgetView && _widgetView.unblockAudio(),\n webRtcStream: () => _webRTCStream,\n privateEvents: new EventEmitter(),\n\n startRoutedToRelayedTransition() {\n if (_properties.testNetwork || isLocalStream(_stream, _session)) {\n // We avoid transitioning to relayed when doing a network test because we need\n // to subscribe from Mantis, or in case we're subscribing to the local stream.\n return;\n }\n\n logRoutedToRelayedTransition('Attempt');\n socket.subscriberCreate(_stream.id, _widgetId, // subscriberId\n channelsToSubscribeTo, 'P2P', (err, message) => {\n if (err && !connectivityState.is('disconnected')) {\n logRoutedToRelayedTransition('Failure', err);\n }\n\n logging.debug('message received when created a subscriber with source', message);\n });\n },\n\n startRelayedToRoutedTransition() {\n return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee18() {\n var webRTCStream, peerConnection;\n return _regenerator.default.wrap(function _callee18$(_context18) {\n while (1) switch (_context18.prev = _context18.next) {\n case 0:\n if (!(_properties.testNetwork || isLocalStream(_stream, _session))) {\n _context18.next = 2;\n break;\n }\n\n return _context18.abrupt(\"return\");\n\n case 2:\n if (!(_activeSourceStreamId === 'MANTIS')) {\n _context18.next = 4;\n break;\n }\n\n return _context18.abrupt(\"return\");\n\n case 4:\n logRelayedToRoutedTransition('Attempt');\n\n if (_state.isSubscribing()) {\n _context18.next = 8;\n break;\n }\n\n logRelayedToRoutedTransition('Failure', {\n reason: 'Subscriber is not subscribing'\n });\n return _context18.abrupt(\"return\");\n\n case 8:\n webRTCStream = webRTCStreams.MANTIS;\n _webRTCStream = webRTCStream; // bind mantis stream\n\n _context18.next = 12;\n return bindWebRTCStream(webRTCStream);\n\n case 12:\n // Keep the loading spinner hidden so that the transition looks transparent.\n _widgetView.loading(false); // remove and close p2p connection\n\n\n removePeerConnectionBySourceStreamId('P2P');\n _activeSourceStreamId = 'MANTIS'; // remove webRTCStream\n\n removeWebRTCStream('P2P');\n _context18.next = 18;\n return getCurrentPeerConnection();\n\n case 18:\n peerConnection = _context18.sent;\n // Create audioLevelSampler for the current peer connection\n createAudioLevelSampler(peerConnection);\n logRelayedToRoutedTransition('Success');\n\n case 21:\n case \"end\":\n return _context18.stop();\n }\n }, _callee18);\n }))();\n }\n\n };\n _state = new SubscribingState(stateChangeFailed);\n logging.debug(`OT.Subscriber: subscribe to ${_stream.id}`);\n\n _state.set('Init');\n\n if (!_stream) {\n // @todo error\n logging.error('OT.Subscriber: No stream parameter.');\n return false;\n }\n\n _streamEventHandlers = {\n updated: streamUpdated\n };\n\n _stream.on(_streamEventHandlers, this);\n\n _properties.name = _properties.name || _stream.name;\n _properties.classNames = 'OT_root OT_subscriber';\n\n if (_properties.style) {\n this.setStyle(_properties.style, null, true);\n }\n\n _latestPositiveVolume = DEFAULT_AUDIO_VOLUME;\n _properties.subscribeToVideo = castToBoolean(_properties.subscribeToVideo, true);\n _properties.subscribeToAudio = castToBoolean(_properties.subscribeToAudio, true);\n this.subscribeToAudio(_properties.subscribeToAudio);\n this.setAudioVolume(determineAudioVolume(_properties));\n _widgetView = new Subscriber.WidgetView(targetElement, (0, _extends2.default)({}, _properties, {\n widgetType: 'subscriber'\n }));\n\n _widgetView.on('error', onVideoError);\n\n _widgetView.on('audioBlocked', () => this.trigger('audioBlocked'));\n\n _widgetView.on('audioUnblocked', () => this.trigger('audioUnblocked'));\n\n this.id = _widgetView.domId();\n _domId = _widgetView.domId();\n this.element = _widgetView.domElement;\n\n _widgetView.on('videoElementCreated', element => {\n const event = new Events.VideoElementCreatedEvent(element);\n const self = this;\n\n if (!_loaded) {\n this.once('loaded', () => {\n self.dispatchEvent(event);\n });\n } else {\n this.dispatchEvent(event);\n }\n });\n\n if (this.element) {\n // Only create the chrome if there is an element to insert it in\n // for insertDefautlUI:false we don't create the chrome\n _createChrome.call(this);\n }\n\n const audio = _stream.getChannelsOfType('audio');\n\n const video = _stream.getChannelsOfType('video');\n\n const channelsToSubscribeTo = audio.map(channel => ({\n id: channel.id,\n type: channel.type,\n active: _properties.subscribeToAudio\n })).concat(video.map(channel => {\n const props = {\n id: channel.id,\n type: channel.type,\n active: _properties.subscribeToVideo,\n restrictFrameRate: _properties.restrictFrameRate !== undefined ? _properties.restrictFrameRate : false\n };\n\n if (_properties.preferredFrameRate !== undefined) {\n props.preferredFrameRate = parseFloat(_properties.preferredFrameRate);\n }\n\n if (_properties.preferredHeight !== undefined) {\n props.preferredHeight = parseInt(_properties.preferredHeight, 10);\n }\n\n if (_properties.preferredWidth !== undefined) {\n props.preferredWidth = parseInt(_properties.preferredWidth, 10);\n }\n\n return props;\n }));\n const shouldSubscribeToSelf = !_properties.testNetwork && isLocalStream(_stream, _session);\n connectivityState.beginConnect();\n\n if (shouldSubscribeToSelf) {\n // bypass rumor etc and just subscribe locally\n this._subscribeToSelf();\n\n return this;\n }\n\n socket.subscriberCreate(_stream.id, _widgetId, // subscriberId\n channelsToSubscribeTo, null, (err, message) => {\n // when the publisher is destroyed before we subscribe, chances are we have been told about\n // before we get the subscriberCreate error, so this can be ignored.\n if (err && !connectivityState.is('disconnected')) {\n onSubscriberCreateError(err);\n }\n\n fallbackIceServers = parseIceServers(message);\n });\n /**\n * Dispatched when the subscriber's audio is blocked because of the\n * browser's autoplay policy.\n *\n * @see OT.unblockAudio()\n * @see Subscriber.isAudioBlocked()\n * @see The audioUnblocked event\n * @see The style.audioBlockedDisplayMode
property of the\n * options
parameter of the\n * Session.subscribe() method)\n *\n * @name audioBlocked\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Dispatched when the subscriber's audio is unblocked after\n * being paused because of the browser's autoplay policy.\n * \n * Subscriber audio is unblocked when any of the following occurs:\n *
\n * - \n * The user clicks the default Subscriber audio playback icon\n *
\n * - \n * The OT.unblockAudio()\n * method is called in response to an HTML element dispatching\n * a
click
event (if you have disabled the default\n * audio playback icon)\n * \n * - \n * The local client gains access to the camera or microphone\n * (for instance, in response to a successful call to\n *
OT.initPublisher()
).\n * \n *
\n *\n * @see OT.unblockAudio()\n * @see The audioBlocked event\n * @see The style.audioBlockedDisplayMode
property of the\n * options
parameter of the\n * Session#subscribe() method)\n *\n * @name audioUnblocked\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Dispatched periodically to indicate the subscriber's audio level. The event is dispatched\n * up to 60 times per second, depending on the browser. The audioLevel
property\n * of the event is audio level, from 0 to 1.0. See {@link AudioLevelUpdatedEvent} for more\n * information.\n * \n * The following example adjusts the value of a meter element that shows volume of the\n * subscriber. Note that the audio level is adjusted logarithmically and a moving average\n * is applied:\n *
\n * var movingAvg = null;\n * subscriber.on('audioLevelUpdated', function(event) {\n * if (movingAvg === null || movingAvg <= event.audioLevel) {\n * movingAvg = event.audioLevel;\n * } else {\n * movingAvg = 0.7 * movingAvg + 0.3 * event.audioLevel;\n * }\n *\n * // 1.5 scaling to map the -30 - 0 dBm range to [0,1]\n * var logLevel = (Math.log(movingAvg) / Math.LN10) / 1.5 + 1;\n * logLevel = Math.min(Math.max(logLevel, 0), 1);\n * document.getElementById('subscriberMeter').value = logLevel;\n * });\n *
\n * This example shows the algorithm used by the default audio level indicator displayed\n * in an audio-only Subscriber.\n *\n * @name audioLevelUpdated\n * @event\n * @memberof Subscriber\n * @see AudioLevelUpdatedEvent\n */\n\n /**\n * Dispatched when the video for the subscriber is disabled.\n *
\n * The reason
property defines the reason the video was disabled. This can be set to\n * one of the following values:\n *
\n *\n *
\n *\n * \"codecNotSupported\"
— The client's browser does not support the\n * video codec used by the stream. For example, in Safari if you connect to a\n * routed session\n * in a non-Safari OpenTok\n * project, and you try to subscribe to a stream that includes video, the Subscriber\n * will dispatch a videoDisabled
event with the reason
property\n * set to \"codecNotSupported\"
. (In routed sessions in a non-Safari project,\n * streams use the VP8 video codec, which is not supported in Safari.) The subscriber\n * element will also display a \"Video format not supported\" message. (See\n * OT.getSupportedCodecs().) \n *\n * \"publishVideo\"
— The publisher stopped publishing video by calling\n * publishVideo(false)
. \n *\n * \"quality\"
— The OpenTok Media Router stopped sending video\n * to the subscriber based on stream quality changes. This feature of the OpenTok Media\n * Router has a subscriber drop the video stream when connectivity degrades. (The subscriber\n * continues to receive the audio stream, if there is one.)\n * \n * Before sending this event, when the Subscriber's stream quality deteriorates to a level\n * that is low enough that the video stream is at risk of being disabled, the Subscriber\n * dispatches a videoDisableWarning
event.\n *
\n * If connectivity improves to support video again, the Subscriber object dispatches\n * a videoEnabled
event, and the Subscriber resumes receiving video.\n *
\n * By default, the Subscriber displays a video disabled indicator when a\n * videoDisabled
event with this reason is dispatched and removes the indicator\n * when the videoEnabled
event with this reason is dispatched. You can control\n * the display of this icon by calling the setStyle()
method of the Subscriber,\n * setting the videoDisabledDisplayMode
property(or you can set the style when\n * calling the Session.subscribe()
method, setting the style
property\n * of the properties
parameter).\n *
\n * This feature is only available in sessions that use the OpenTok Media Router (sessions with\n * the media mode\n * set to routed), not in sessions with the media mode set to relayed.\n *
\n * You can disable this audio-only fallback feature, by setting the\n * audioFallbackEnabled
property to false
in the options you pass\n * into the OT.initPublisher()
method on the publishing client. (See\n * OT.initPublisher().)\n *
\n *\n * \"subscribeToVideo\"
— The subscriber started or stopped subscribing to\n * video, by calling subscribeToVideo(false)
.\n * \n *\n *
\n *\n * @see VideoEnabledChangedEvent\n * @see videoDisableWarning event\n * @see videoEnabled event\n * @name videoDisabled\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Dispatched when the OpenTok Media Router determines that the stream quality has degraded\n * and the video will be disabled if the quality degrades more. If the quality degrades further,\n * the Subscriber disables the video and dispatches a videoDisabled
event.\n * \n * By default, the Subscriber displays a video disabled warning indicator when this event\n * is dispatched (and the video is disabled). You can control the display of this icon by\n * calling the setStyle()
method and setting the\n * videoDisabledDisplayMode
property (or you can set the style when calling\n * the Session.subscribe()
method and setting the style
property\n * of the properties
parameter).\n *
\n * This feature is only available in sessions that use the OpenTok Media Router (sessions with\n * the media mode\n * set to routed), not in sessions with the media mode set to relayed.\n *\n * @see Event\n * @see videoDisabled event\n * @see videoDisableWarningLifted event\n * @name videoDisableWarning\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Dispatched when the Subscriber's video element is created. Add a listener for this event when\n * you set the insertDefaultUI
option to false
in the call to the\n * Session.subscribe() method. The element
\n * property of the event object is a reference to the Subscriber's video
element\n * (or in Internet Explorer the object
element containing the video). Add it to\n * the HTML DOM to display the video. When you set the insertDefaultUI
option to\n * false
, the video
(or object
) element is not automatically\n * inserted into the DOM.\n *
\n * Add a listener for this event only if you have set the insertDefaultUI
option to\n * false
. If you have not set insertDefaultUI
option to\n * false
, do not move the video
(or object
) element in\n * in the HTML DOM. Doing so causes the Subscriber object to be destroyed.\n *\n * @name videoElementCreated\n * @event\n * @memberof Subscriber\n * @see VideoElementCreatedEvent\n */\n\n /**\n * Dispatched when the OpenTok Media Router determines that the stream quality has improved\n * to the point at which the video being disabled is not an immediate risk. This event is\n * dispatched after the Subscriber object dispatches a videoDisableWarning
event.\n *
\n * This feature is only available in sessions that use the OpenTok Media Router (sessions with\n * the media mode\n * set to routed), not in sessions with the media mode set to relayed.\n *\n * @see Event\n * @see videoDisableWarning event\n * @see videoDisabled event\n * @name videoDisableWarningLifted\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Dispatched when the OpenTok Media Router resumes sending video to the subscriber\n * after video was previously disabled.\n *
\n * The reason
property defines the reason the video was enabled. This can be set to\n * one of the following values:\n *
\n *\n *
\n *\n * \n * To prevent video from resuming, in the videoEnabled
event listener,\n * call subscribeToVideo(false)
on the Subscriber object.\n *\n * @see VideoEnabledChangedEvent\n * @see videoDisabled event\n * @name videoEnabled\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Sent when the subscriber's stream has been interrupted.\n *
\n * In response to this event, you may want to provide a user interface notification, to let the\n * user know that the audio-video stream is temporarily disconnected and that the app is trying\n * to reconnect to it.\n *
\n * If the client reconnects to the stream, the Subscriber object dispatches a\n * connected
event. Otherwise, if the client cannot reconnect to the stream,\n * the Subscriber object dispatches a destroyed
event.\n *\n * @name disconnected\n * @event\n * @memberof Subscriber\n * @see connected event\n * @see Event\n */\n\n /**\n * Sent when the subscriber's stream has resumed, after the Subscriber dispatches a\n * disconnected
event.\n *\n * @name connected\n * @event\n * @memberof Subscriber\n * @see disconnected event\n * @see Event\n */\n\n /**\n * Dispatched when the Subscriber element is removed from the HTML DOM. When this event is\n * dispatched, you may choose to adjust or remove HTML DOM elements related to the subscriber.\n *
\n * To prevent the Subscriber from being removed from the DOM when the stream is destroyed,\n * listen for the streamDestroyed event\n * dispatched by the Session object. The streamDestroyed
event dispatched by the\n * Session object is cancelable, and calling the preventDefault()
method of the\n * event object prevents Subscribers of the stream from being removed from the HTML DOM.\n *\n * @see Event\n * @name destroyed\n * @event\n * @memberof Subscriber\n */\n\n /**\n * Dispatched when the video dimensions of the video change. This can occur when the\n * stream.videoType
property is set to \"screen\"
(for a screen-sharing\n * video stream), when the user resizes the window being captured. It can also occur if the video\n * is being published by a mobile device and the user rotates the device (causing the camera\n * orientation to change). This event object has a newValue
property and an\n * oldValue
property, representing the new and old dimensions of the video.\n * Each of these has a height
property and a width
property,\n * representing the height and width, in pixels.\n * @name videoDimensionsChanged\n * @event\n * @memberof Subscriber\n * @see VideoDimensionsChangedEvent\n */\n\n return this;\n };\n\n Subscriber.hasAudioOutputLevelStatCapability = hasAudioOutputLevelStatCapability;\n Subscriber.WidgetView = WidgetView;\n Subscriber.SubscriberPeerConnection = SubscriberPeerConnection;\n return Subscriber;\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 299 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2); // @todo can we test for this instead?\n\n\nmodule.exports = () => ['Chrome', 'Opera', 'Safari'].indexOf(env.name) > -1;\n\n/***/ }),\n/* 300 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst _require = __webpack_require__(2),\n name = _require.name; // @todo can we test for these?\n\n\nmodule.exports = () => ['Firefox', 'Safari', 'Edge', 'Chrome', 'Opera'].indexOf(name) > -1;\n\n/***/ }),\n/* 301 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst generateSimpleStateMachine = __webpack_require__(159)(); // Models a Subscriber's subscribing State\n//\n// Valid States:\n// NotSubscribing (the initial state\n// Init (basic setup of DOM\n// Connecting (Failure Cases -> No Route, Bad Offer, Bad Answer\n// BindingRemoteStream (Failure Cases -> Anything to do with the media being\n// (invalid, the media never plays\n// Subscribing (this is 'onLoad'\n// Failed (terminal state, with a reason that maps to one of the\n// (failure cases above\n// Destroyed (The subscriber has been cleaned up, terminal state\n//\n//\n// Valid Transitions:\n// NotSubscribing ->\n// Init\n//\n// Init ->\n// Connecting\n// | BindingRemoteStream (if we are subscribing to ourselves and we alreay\n// (have a stream\n// | NotSubscribing (destroy()\n//\n// Connecting ->\n// BindingRemoteStream\n// | NotSubscribing\n// | Failed\n// | NotSubscribing (destroy()\n//\n// BindingRemoteStream ->\n// Subscribing\n// | Failed\n// | NotSubscribing (destroy()\n//\n// Subscribing ->\n// NotSubscribing (unsubscribe\n// | Failed (probably a peer connection failure after we began\n// (subscribing\n//\n// Failed ->\n// Destroyed\n//\n// Destroyed -> (terminal state)\n//\n//\n// @example\n// var state = new SubscribingState(function(change) {\n// console.log(change.message);\n// });\n//\n// state.set('Init');\n// state.current; -> 'Init'\n//\n// state.set('Subscribing'); -> triggers stateChangeFailed and logs out the error message\n//\n//\n\n\nconst initialState = 'NotSubscribing';\nconst validStates = ['NotSubscribing', 'Init', 'Connecting', 'BindingRemoteStream', 'Subscribing', 'Failed', 'Destroyed'];\nconst validTransitions = {\n NotSubscribing: ['NotSubscribing', 'Init', 'Destroyed'],\n Init: ['NotSubscribing', 'Connecting', 'BindingRemoteStream', 'Destroyed'],\n Connecting: ['NotSubscribing', 'BindingRemoteStream', 'Failed', 'Destroyed'],\n BindingRemoteStream: ['NotSubscribing', 'Subscribing', 'Failed', 'Destroyed', 'BindingRemoteStream'],\n Subscribing: ['NotSubscribing', 'Failed', 'Destroyed', 'BindingRemoteStream'],\n Failed: ['Destroyed'],\n Destroyed: []\n};\nconst SubscribingState = generateSimpleStateMachine(initialState, validStates, validTransitions);\n\nSubscribingState.prototype.isDestroyed = function () {\n return this.current === 'Destroyed';\n};\n\nSubscribingState.prototype.isFailed = function () {\n return this.current === 'Failed';\n};\n\nSubscribingState.prototype.isSubscribing = function () {\n return this.current === 'Subscribing';\n};\n\nSubscribingState.prototype.isAttemptingToSubscribe = function () {\n return ['Init', 'Connecting', 'BindingRemoteStream'].indexOf(this.current) !== -1;\n};\n\nSubscribingState.prototype.isStreamAdded = function () {\n return this.current === 'BindingRemoteStream' || this.isSubscribing();\n};\n\nmodule.exports = SubscribingState;\n\n/***/ }),\n/* 302 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\nmodule.exports = (_ref) => {\n let socket = _ref.socket,\n uri = _ref.uri,\n defaultContent = _ref.content;\n return (method, content) => socket.send({\n uri,\n method,\n content: (0, _extends2.default)({}, defaultContent, content)\n });\n};\n\n/***/ }),\n/* 303 */\n/***/ (function(module, exports, __webpack_require__) {\n\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(true)\n\t\tmodule.exports = factory();\n\telse {}\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 5);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(target, sources) {\n var n, source, key;\n for(n = 1 ; n < arguments.length ; n++) {\n source = arguments[n];\n for(key in source) {\n if (source.hasOwnProperty(key))\n target[key] = source[key];\n }\n }\n return target;\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0);\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = {\n\n build: function(target, config) {\n var n, max, plugin, plugins = config.plugins;\n for(n = 0, max = plugins.length ; n < max ; n++) {\n plugin = plugins[n];\n if (plugin.methods)\n mixin(target, plugin.methods);\n if (plugin.properties)\n Object.defineProperties(target, plugin.properties);\n }\n },\n\n hook: function(fsm, name, additional) {\n var n, max, method, plugin,\n plugins = fsm.config.plugins,\n args = [fsm.context];\n\n if (additional)\n args = args.concat(additional)\n\n for(n = 0, max = plugins.length ; n < max ; n++) {\n plugin = plugins[n]\n method = plugins[n][name]\n if (method)\n method.apply(plugin, args);\n }\n }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nfunction camelize(label) {\n\n if (label.length === 0)\n return label;\n\n var n, result, word, words = label.split(/[_-]/);\n\n // single word with first character already lowercase, return untouched\n if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))\n return label;\n\n result = words[0].toLowerCase();\n for(n = 1 ; n < words.length ; n++) {\n result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();\n }\n\n return result;\n}\n\n//-------------------------------------------------------------------------------------------------\n\ncamelize.prepended = function(prepend, label) {\n label = camelize(label);\n return prepend + label[0].toUpperCase() + label.substring(1);\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = camelize;\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0),\n camelize = __webpack_require__(2);\n\n//-------------------------------------------------------------------------------------------------\n\nfunction Config(options, StateMachine) {\n\n options = options || {};\n\n this.options = options; // preserving original options can be useful (e.g visualize plugin)\n this.defaults = StateMachine.defaults;\n this.states = [];\n this.transitions = [];\n this.map = {};\n this.lifecycle = this.configureLifecycle();\n this.init = this.configureInitTransition(options.init);\n this.data = this.configureData(options.data);\n this.methods = this.configureMethods(options.methods);\n\n this.map[this.defaults.wildcard] = {};\n\n this.configureTransitions(options.transitions || []);\n\n this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin);\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(Config.prototype, {\n\n addState: function(name) {\n if (!this.map[name]) {\n this.states.push(name);\n this.addStateLifecycleNames(name);\n this.map[name] = {};\n }\n },\n\n addStateLifecycleNames: function(name) {\n this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name);\n this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name);\n this.lifecycle.on[name] = camelize.prepended('on', name);\n },\n\n addTransition: function(name) {\n if (this.transitions.indexOf(name) < 0) {\n this.transitions.push(name);\n this.addTransitionLifecycleNames(name);\n }\n },\n\n addTransitionLifecycleNames: function(name) {\n this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name);\n this.lifecycle.onAfter[name] = camelize.prepended('onAfter', name);\n this.lifecycle.on[name] = camelize.prepended('on', name);\n },\n\n mapTransition: function(transition) {\n var name = transition.name,\n from = transition.from,\n to = transition.to;\n this.addState(from);\n if (typeof to !== 'function')\n this.addState(to);\n this.addTransition(name);\n this.map[from][name] = transition;\n return transition;\n },\n\n configureLifecycle: function() {\n return {\n onBefore: { transition: 'onBeforeTransition' },\n onAfter: { transition: 'onAfterTransition' },\n onEnter: { state: 'onEnterState' },\n onLeave: { state: 'onLeaveState' },\n on: { transition: 'onTransition' }\n };\n },\n\n configureInitTransition: function(init) {\n if (typeof init === 'string') {\n return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true }));\n }\n else if (typeof init === 'object') {\n return this.mapTransition(mixin({}, this.defaults.init, init, { active: true }));\n }\n else {\n this.addState(this.defaults.init.from);\n return this.defaults.init;\n }\n },\n\n configureData: function(data) {\n if (typeof data === 'function')\n return data;\n else if (typeof data === 'object')\n return function() { return data; }\n else\n return function() { return {}; }\n },\n\n configureMethods: function(methods) {\n return methods || {};\n },\n\n configurePlugins: function(plugins, builtin) {\n plugins = plugins || [];\n var n, max, plugin;\n for(n = 0, max = plugins.length ; n < max ; n++) {\n plugin = plugins[n];\n if (typeof plugin === 'function')\n plugins[n] = plugin = plugin()\n if (plugin.configure)\n plugin.configure(this);\n }\n return plugins\n },\n\n configureTransitions: function(transitions) {\n var i, n, transition, from, to, wildcard = this.defaults.wildcard;\n for(n = 0 ; n < transitions.length ; n++) {\n transition = transitions[n];\n from = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard]\n to = transition.to || wildcard;\n for(i = 0 ; i < from.length ; i++) {\n this.mapTransition({ name: transition.name, from: from[i], to: to });\n }\n }\n },\n\n transitionFor: function(state, transition) {\n var wildcard = this.defaults.wildcard;\n return this.map[state][transition] ||\n this.map[wildcard][transition];\n },\n\n transitionsFor: function(state) {\n var wildcard = this.defaults.wildcard;\n return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard]));\n },\n\n allStates: function() {\n return this.states;\n },\n\n allTransitions: function() {\n return this.transitions;\n }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = Config;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\nvar mixin = __webpack_require__(0),\n Exception = __webpack_require__(6),\n plugin = __webpack_require__(1),\n UNOBSERVED = [ null, [] ];\n\n//-------------------------------------------------------------------------------------------------\n\nfunction JSM(context, config) {\n this.context = context;\n this.config = config;\n this.state = config.init.from;\n this.observers = [context];\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(JSM.prototype, {\n\n init: function(args) {\n mixin(this.context, this.config.data.apply(this.context, args));\n plugin.hook(this, 'init');\n if (this.config.init.active)\n return this.fire(this.config.init.name, []);\n },\n\n is: function(state) {\n return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state);\n },\n\n isPending: function() {\n return this.pending;\n },\n\n can: function(transition) {\n return !this.isPending() && !!this.seek(transition);\n },\n\n cannot: function(transition) {\n return !this.can(transition);\n },\n\n allStates: function() {\n return this.config.allStates();\n },\n\n allTransitions: function() {\n return this.config.allTransitions();\n },\n\n transitions: function() {\n return this.config.transitionsFor(this.state);\n },\n\n seek: function(transition, args) {\n var wildcard = this.config.defaults.wildcard,\n entry = this.config.transitionFor(this.state, transition),\n to = entry && entry.to;\n if (typeof to === 'function')\n return to.apply(this.context, args);\n else if (to === wildcard)\n return this.state\n else\n return to\n },\n\n fire: function(transition, args) {\n return this.transit(transition, this.state, this.seek(transition, args), args);\n },\n\n transit: function(transition, from, to, args) {\n\n var lifecycle = this.config.lifecycle,\n changed = this.config.options.observeUnchangedState || (from !== to);\n\n if (!to)\n return this.context.onInvalidTransition(transition, from, to);\n\n if (this.isPending())\n return this.context.onPendingTransition(transition, from, to);\n\n this.config.addState(to); // might need to add this state if it's unknown (e.g. conditional transition or goto)\n\n this.beginTransit();\n\n args.unshift({ // this context will be passed to each lifecycle event observer\n transition: transition,\n from: from,\n to: to,\n fsm: this.context\n });\n\n return this.observeEvents([\n this.observersForEvent(lifecycle.onBefore.transition),\n this.observersForEvent(lifecycle.onBefore[transition]),\n changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED,\n changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED,\n this.observersForEvent(lifecycle.on.transition),\n changed ? [ 'doTransit', [ this ] ] : UNOBSERVED,\n changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED,\n changed ? this.observersForEvent(lifecycle.onEnter[to]) : UNOBSERVED,\n changed ? this.observersForEvent(lifecycle.on[to]) : UNOBSERVED,\n this.observersForEvent(lifecycle.onAfter.transition),\n this.observersForEvent(lifecycle.onAfter[transition]),\n this.observersForEvent(lifecycle.on[transition])\n ], args);\n },\n\n beginTransit: function() { this.pending = true; },\n endTransit: function(result) { this.pending = false; return result; },\n failTransit: function(result) { this.pending = false; throw result; },\n doTransit: function(lifecycle) { this.state = lifecycle.to; },\n\n observe: function(args) {\n if (args.length === 2) {\n var observer = {};\n observer[args[0]] = args[1];\n this.observers.push(observer);\n }\n else {\n this.observers.push(args[0]);\n }\n },\n\n observersForEvent: function(event) { // TODO: this could be cached\n var n = 0, max = this.observers.length, observer, result = [];\n for( ; n < max ; n++) {\n observer = this.observers[n];\n if (observer[event])\n result.push(observer);\n }\n return [ event, result, true ]\n },\n\n observeEvents: function(events, args, previousEvent, previousResult) {\n if (events.length === 0) {\n return this.endTransit(previousResult === undefined ? true : previousResult);\n }\n\n var event = events[0][0],\n observers = events[0][1],\n pluggable = events[0][2];\n\n args[0].event = event;\n if (event && pluggable && event !== previousEvent)\n plugin.hook(this, 'lifecycle', args);\n\n if (observers.length === 0) {\n events.shift();\n return this.observeEvents(events, args, event, previousResult);\n }\n else {\n var observer = observers.shift(),\n result = observer[event].apply(observer, args);\n if (result && typeof result.then === 'function') {\n return result.then(this.observeEvents.bind(this, events, args, event))\n .catch(this.failTransit.bind(this))\n }\n else if (result === false) {\n return this.endTransit(false);\n }\n else {\n return this.observeEvents(events, args, event, result);\n }\n }\n },\n\n onInvalidTransition: function(transition, from, to) {\n throw new Exception(\"transition is invalid in current state\", transition, from, to, this.state);\n },\n\n onPendingTransition: function(transition, from, to) {\n throw new Exception(\"transition is invalid while previous transition is still in progress\", transition, from, to, this.state);\n }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = JSM;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-----------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0),\n camelize = __webpack_require__(2),\n plugin = __webpack_require__(1),\n Config = __webpack_require__(3),\n JSM = __webpack_require__(4);\n\n//-----------------------------------------------------------------------------------------------\n\nvar PublicMethods = {\n is: function(state) { return this._fsm.is(state) },\n can: function(transition) { return this._fsm.can(transition) },\n cannot: function(transition) { return this._fsm.cannot(transition) },\n observe: function() { return this._fsm.observe(arguments) },\n transitions: function() { return this._fsm.transitions() },\n allTransitions: function() { return this._fsm.allTransitions() },\n allStates: function() { return this._fsm.allStates() },\n onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to) },\n onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to) },\n}\n\nvar PublicProperties = {\n state: {\n configurable: false,\n enumerable: true,\n get: function() {\n return this._fsm.state;\n },\n set: function(state) {\n throw Error('use transitions to change state')\n }\n }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nfunction StateMachine(options) {\n return apply(this || {}, options);\n}\n\nfunction factory() {\n var cstor, options;\n if (typeof arguments[0] === 'function') {\n cstor = arguments[0];\n options = arguments[1] || {};\n }\n else {\n cstor = function() { this._fsm.apply(this, arguments) };\n options = arguments[0] || {};\n }\n var config = new Config(options, StateMachine);\n build(cstor.prototype, config);\n cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance\n return cstor;\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction apply(instance, options) {\n var config = new Config(options, StateMachine);\n build(instance, config);\n instance._fsm();\n return instance;\n}\n\nfunction build(target, config) {\n if ((typeof target !== 'object') || Array.isArray(target))\n throw Error('StateMachine can only be applied to objects');\n plugin.build(target, config);\n Object.defineProperties(target, PublicProperties);\n mixin(target, PublicMethods);\n mixin(target, config.methods);\n config.allTransitions().forEach(function(transition) {\n target[camelize(transition)] = function() {\n return this._fsm.fire(transition, [].slice.call(arguments))\n }\n });\n target._fsm = function() {\n this._fsm = new JSM(this, config);\n this._fsm.init(arguments);\n }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nStateMachine.version = '3.0.1';\nStateMachine.factory = factory;\nStateMachine.apply = apply;\nStateMachine.defaults = {\n wildcard: '*',\n init: {\n name: 'init',\n from: 'none'\n }\n}\n\n//===============================================================================================\n\nmodule.exports = StateMachine;\n\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(message, transition, from, to, current) {\n this.message = message;\n this.transition = transition;\n this.from = from;\n this.to = to;\n this.current = current;\n}\n\n\n/***/ })\n/******/ ]);\n});\n\n/***/ }),\n/* 304 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst shouldUseStandardGetStats = __webpack_require__(102)(); // getRtcStatsReport is only supported on browsers that support the standard getStats API.\n\n\nmodule.exports = shouldUseStandardGetStats;\n\n/***/ }),\n/* 305 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\n/**\n * promisify - Promisify a node style callback function into a promise\n *\n * Returns a promisifed version of the function, if it's callback is called\n * with a truthy error parameter the promise will be rejected. If the callback\n * is called with one argument, it's value will be resolved. If the callback\n * is called with multiple arguments, the promise will resolve to an array\n * of those arguments.\n *\n * @param {function} fn Function to promisify\n * @returns {function} Promise returning function\n */\nmodule.exports = function promisify(fn) {\n return function promisified() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return new Promise((resolve, reject) => {\n fn(...args, function (err) {\n if (err) {\n reject(err);\n return;\n }\n\n for (var _len2 = arguments.length, callbackArgs = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n callbackArgs[_key2 - 1] = arguments[_key2];\n }\n\n if (callbackArgs.length === 1) {\n resolve(callbackArgs[0]);\n return;\n }\n\n resolve(callbackArgs);\n });\n });\n };\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 306 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-param-reassign, global-require, max-len, prefer-rest-params */\n\n/* eslint-disable no-use-before-define */\nconst isFunction = __webpack_require__(13);\n\nconst isObject = __webpack_require__(8);\n\nconst isNullOrFalse = __webpack_require__(674);\n\nmodule.exports = function initPublisherFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const Errors = deps.Errors || __webpack_require__(6);\n\n const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(9);\n\n const logging = deps.logging || __webpack_require__(0)('publisherInit');\n\n const otError = deps.otError || __webpack_require__(11)();\n\n const OTHelpers = deps.OTHelpers || __webpack_require__(4);\n\n const Publisher = deps.Publisher || __webpack_require__(166)();\n\n const sessionObjects = deps.sessionObjects || __webpack_require__(23);\n /**\n *
\n * Initializes and returns a Publisher object. You can then pass this Publisher\n * object to Session.publish()
to publish a stream to a session.\n *
\n * \n * Note: If you intend to reuse a Publisher object created using\n * OT.initPublisher()
to publish to different sessions sequentially,\n * call either Session.disconnect()
or Session.unpublish()
.\n * Do not call both. Then call the preventDefault()
method of the\n * streamDestroyed
or sessionDisconnected
event object to prevent the\n * Publisher object from being removed from the page.\n *
\n *\n * @param {Object} targetElement (Optional) The DOM element or the id
attribute of the\n * existing DOM element used to determine the location of the Publisher video in the HTML DOM. See\n * the insertMode
property of the properties
parameter. If you do not\n * specify a targetElement
, the application appends a new DOM element to the HTML\n * body
.\n *\n * \n * The application throws an error if an element with an ID set to the\n * targetElement
value does not exist in the HTML DOM.\n *
\n *\n * @param {Object} properties (Optional) This object contains the following properties (each of which\n * are optional):\n * \n * \n * - \n * audioBitrate (Number) The desired bitrate for the published audio,\n * in bits per second. The supported range of values is 6,000 - 510,000. (Invalid values are\n * ignored.) Set this value to enable high-quality audio (or to reduce bandwidth usage with\n * lower-quality audio).\n *
\n * The following are recommended settings:\n *
\n * - 8,000 - 12,000 for narrowband (NB) speech
\n * - 16,000 - 20,000 for wideband (WB) speech
\n * - 28,000 - 40,000 for full-band (FB) speech
\n * - 48,000 - 64,000 for full-band (FB) music
\n *
\n * \n * \n * The default value is 40,000.\n *
\n * \n * Currently, this setting is not supported in streams published in Firefox.\n *
\n * \n * - \n * audioFallbackEnabled (Boolean) Whether the stream will use the\n * audio-fallback feature (
true
) or not (false
). The audio-fallback\n * feature is available in sessions that use the OpenTok Media Router. With the audio-fallback\n * feature enabled (the default), when the server determines that a stream's quality has degraded\n * significantly for a specific subscriber, it disables the video in that subscriber in order to\n * preserve audio quality. For streams that use a camera as a video source, the default setting is\n * true
(the audio-fallback feature is enabled). The default setting is\n * false
(the audio-fallback feature is disabled) for screen-sharing streams, which\n * have the videoSource
property set to \"application\"
,\n * \"screen\"
, or \"window\"
in the OT.initPublisher()
\n * options. For more information, see the Subscriber\n * videoDisabled event and\n * the OpenTok Media\n * Router and media modes.\n * \n * - \n * audioSource (String, MediaStreamTrack, Boolean, or null) \n * The ID of the audio input device (such as a\n * microphone) to be used by the publisher. You can obtain a list of available devices, including\n * audio input devices, by calling the OT.getDevices() method. Each\n * device listed by the method has a unique device ID. If you pass in a device ID that does not\n * match an existing audio input device, the call to
OT.initPublisher()
fails with an\n * error (error code 1500, \"Unable to Publish\") passed to the completion handler function.\n * \n * If you set this property to null
or false
, the browser does not\n * request access to the microphone, and no audio is published.\n *
\n * \n * You can also set this property to an audio\n * \n * MediaStreamTrack object. This lets you use the object as the audio source for\n * the published stream. For example, you can get an array of audio MediaStreamTrack objects\n * from the audioTracks
property of an HTMLMediaElement object. Or you can call\n * the getLocalStreams()
or getRemoteStreams()
method of an\n * RTCPeerConnection object to get an array of MediaStream objects, and then call the\n * getAudioTracks()
method of one of the MediaStream objects to get an audio\n * MediaStreamTrack object.\n *
\n * \n * - \n * autoGainControl (Boolean) Whether to enable automatic gain control\n * for the published audio. You may want to set\n * this to
false
when publishing high-quality audio (by setting the\n * audioBitrate
property of the OT.initPublisher()
options). The\n * default value is true
. This setting is ignored if you set disableAudioProcessing
\n * to false
(which disables echo cancellation, automatic gain control, and noise suppression for\n * the published stream).\n * \n * - \n * disableAudioProcessing (Boolean) Whether to disable echo cancellation,\n * automatic gain control, and noise suppression for the published audio. You may want to set\n * this to
true
when publishing high-quality audio (by setting the\n * audioBitrate
property of the OT.initPublisher()
options). The\n * default value is false
.\n * \n * - \n * echoCancellation (Boolean) Whether to enable echo cancellation\n * for the published audio. You may want to set\n * this to
false
when publishing high-quality audio (by setting the\n * audioBitrate
property of the OT.initPublisher()
options). The\n * default value is true
. This setting is ignored if you set disableAudioProcessing
\n * to false
(which disables echo cancellation, automatic gain control, and noise suppression for\n * the published stream).\n * \n * - \n * enableStereo (Boolean) Whether to publish stereo audio. The default\n * value is
false
.\n * \n * Note: Some browsers (such as Chome 73+) do not support\n * echo cancellation for stereo audio (see this\n * Chrome issue report).\n *
\n * - \n * facingMode (String) The preferred camera position to use for the\n * video source. Generally, this setting only applies to mobile devices. This can be set to\n * one of the following values:\n *
\n *
\n * - \n *
\"user\"
— The front-facing camera.\n * \n * - \n *
\"environment\"
— The back camera.\n * \n * - \n *
\"left\"
— The camera facing the user but to the left\n * (uncommon).\n * \n * - \n *
\"right\"
— The camera facing the user but to the right\n * (uncommon).\n * \n *
\n * \n * \n * - \n * fitMode (String) Determines how the video is displayed if the its\n * dimensions do not match those of the DOM element. You can set this property to one of the\n * following values:\n *
\n *
\n * - \n *
\"cover\"
— The video is cropped if its dimensions do not match those of\n * the DOM element. This is the default setting for videos publishing a camera feed.\n * \n * - \n *
\"contain\"
— The video is letterboxed if its dimensions do not match\n * those of the DOM element. This is the default setting for screen-sharing videos.\n * \n *
\n * \n * - \n * frameRate (Number) The desired frame rate, in frames per second,\n * of the video. Valid values are 30, 15, 7, and 1. The published stream will use the closest\n * value supported on the publishing client. The frame rate can differ slightly from the value\n * you set, depending on the browser of the client. And the video will only use the desired\n * frame rate if the client configuration supports it.\n *
If the publisher specifies a frame rate, the actual frame rate of the video stream\n * is set as the frameRate
property of the Stream object, though the actual frame rate\n * will vary based on changing network and system conditions. If the developer does not specify a\n * frame rate, this property is undefined. Also, if the video source is a video MediaStreamTrack\n * object (see the videoSource
), this value is ignored and the frame rate is\n * defined by the MediaStreamTrack object, which you can get by calling the\n * getConstraints()
method of the MediaStreamTrack object.\n *
\n * \n * For sessions that use the OpenTok Media Router (sessions with\n * the media mode\n * set to routed, lowering the frame rate proportionally reduces the maximum bandwidth the stream\n * uses. However, in sessions with the media mode set to relayed, lowering the frame rate does not\n * reduce the stream's bandwidth.\n *
\n * \n * You can also restrict the frame rate of a Subscriber's video stream. To restrict the frame rate\n * a Subscriber, call the restrictFrameRate()
method of the subscriber, passing in\n * true
.\n * (See Subscriber.restrictFrameRate().)\n *
\n * \n * - \n * height (Number or String) The desired initial height of the displayed Publisher\n * video in the HTML page (default: 198 pixels). You can specify the number of pixels as either\n * a number (such as 300) or a string ending in \"px\" (such as \"300px\"). Or you can specify a\n * percentage of the size of the parent element, with a string ending in \"%\" (such as \"100%\").\n * Note: To resize the publisher video, adjust the CSS of the publisher's DOM element\n * (the
element
property of the Publisher object) or (if the height is specified as\n * a percentage) its parent DOM element (see\n * Resizing\n * or repositioning a video).\n * \n * - \n * insertDefaultUI (Boolean) Whether to use the default OpenTok UI\n * (
true
, the default) or not (false
). The default UI element contains\n * user interface controls, a video loading indicator, and automatic video cropping or\n * letterboxing, in addition to the video. (If you leave insertDefaultUI
set to\n * true
, you can control individual UI settings using the fitMode
,\n * showControls
, and style
options.)\n * \n * If you set this option to false
, OpenTok.js does not insert a default UI element\n * in the HTML DOM, and the element
property of the Publisher object is undefined.\n * Instead, the Publisher object dispatches a\n * videoElementCreated event when\n * the video
element (or in Internet Explorer the object
element\n * containing the video) is created. The element
property of the event object is a\n * reference to the Publisher's video
(or object
) element. Add it to\n * the HTML DOM to display the video.\n *
\n * Set this option to false
if you want to move the Publisher's video
\n * (or object
) element in the HTML DOM.\n *
\n * If you set this to false
, do not set the targetElement
parameter.\n * (This results in an error passed into to the OT.initPublisher()
callback\n * function.) To add the video to the HTML DOM, add an event listener for the\n * videoElementCreated
event, and then add the element
property of\n * the event object into the HTML DOM.\n *
\n * - \n * insertMode (String) Specifies how the Publisher object will be\n * inserted in the HTML DOM. See the
targetElement
parameter. This string can\n * have the following values:\n * \n *
\n * \"replace\"
The Publisher object replaces contents of the\n * targetElement. This is the default. \n * \"after\"
The Publisher object is a new element inserted after\n * the targetElement in the HTML DOM. (Both the Publisher and targetElement have the\n * same parent element.) \n * \"before\"
The Publisher object is a new element inserted before\n * the targetElement in the HTML DOM. (Both the Publisher and targetElement have the same\n * parent element.) \n * \"append\"
The Publisher object is a new element added as a child\n * of the targetElement. If there are other child elements, the Publisher is appended as\n * the last child element of the targetElement. \n *
\n * - \n * maxResolution (Object) Sets the maximum resolution to stream.\n * This setting only applies to when the
videoSource
property is set to\n * \"application\"
, \"screen\"
, or \"window\"
\n * (when the publisher is screen-sharing). The resolution of the\n * stream will match the captured screen region unless the region is greater than the\n * maxResolution
setting. Set this to an object that has two properties:\n * width
and height
(both numbers). The maximum value for each of\n * the width
and height
properties is 1920, and the minimum value\n * is 10.\n * \n * - \n * mirror (Boolean) Whether the publisher's video image\n * is mirrored in the publisher's page. The default value is
true
\n * (the video image is mirrored), except for a screen-sharing video (when the\n * videoSource
property is set to \"application\"
,\n * \"screen\"
, or \"window\"
\n * (in which case the default is false
). This property\n * does not affect the display on subscribers' views of the video.\n * \n * - \n * name (String) The name for this stream. The name appears at\n * the bottom of Subscriber videos. The default value is \"\" (an empty string). Setting\n * this to a string longer than 1000 characters results in an runtime exception.\n *
\n * - \n * noiseSuppression (Boolean) Whether to enable noise suppression\n * for the published audio. You may want to set\n * this to
false
when publishing high-quality audio (by setting the\n * audioBitrate
property of the OT.initPublisher()
options). The\n * default value is true
. This setting is ignored if you set disableAudioProcessing
\n * to false
(which disables echo cancellation, automatic gain control, and noise suppression for\n * the published stream).\n * \n * - \n * publishAudio (Boolean) Whether to initially publish audio\n * for the stream (default:
true
). This setting applies when you pass\n * the Publisher object in a call to the Session.publish()
method.\n * \n * - \n * publishVideo (Boolean) Whether to initially publish video\n * for the stream (default:
true
). This setting applies when you pass\n * the Publisher object in a call to the Session.publish()
method.\n * \n * - \n * resolution (String) The desired resolution of the video. The format\n * of the string is
\"widthxheight\"
, where the width and height are represented in\n * pixels. Valid values are \"1280x720\"
, \"640x480\"
, and\n * \"320x240\"
. The published video will only use the desired resolution if the\n * client configuration supports it. Some browsers and clients do not support each of these\n * resolution settings.\n *
\n * The requested resolution of a video stream is set as the videoDimensions.width
and\n * videoDimensions.height
properties of the Stream object.\n *
\n * \n * The default resolution for a stream (if you do not specify a resolution) is 640x480 pixels.\n * If the client system cannot support the resolution you requested, the stream will use the\n * next largest setting supported.\n *
\n * \n * The actual resolution used by the Publisher is returned by the videoHeight()
and\n * videoWidth()
methods of the Publisher object. The actual resolution of a\n * Subscriber video stream is returned by the videoHeight()
and\n * videoWidth()
properties of the Subscriber object. These may differ from the values\n * of the resolution
property passed in as the properties
property of the\n * OT.initPublisher()
method, if the browser does not support the requested\n * resolution, this value is ignored. Also, if the video source is a video MediaStreamTrack\n * object (see the videoSource
), this value is ignored and the resolution\n * is defined by the MediaStreamTrack object, which you can get by calling the\n * getConstraints()
method of the MediaStreamTrack object.\n *
\n * \n * - \n * showControls (Boolean) Whether to display the built-in user interface\n * controls (default:
true
) for the Publisher. These controls include the name\n * display, the audio level indicator, and the microphone control button. You can turn off all user\n * interface controls by setting this property to false
. You can control the display\n * of individual user interface controls by leaving this property set to true
(the\n * default) and setting individual properties of the style
property.\n * \n * - \n * style (Object) An object containing properties that define the initial\n * appearance of user interface controls of the Publisher. The
style
object includes\n * the following properties:\n * \n * audioLevelDisplayMode
(String) — How to display the audio level\n * indicator. Possible values are: \"auto\"
(the indicator is displayed when the\n * video is disabled), \"off\"
(the indicator is not displayed), and\n * \"on\"
(the indicator is always displayed). \n *\n * archiveStatusDisplayMode
(String) — How to display the archive status\n * indicator. Possible values are: \"auto\"
(the indicator is displayed when the\n * session is being recorded), \"off\"
(the indicator is not displayed). If you\n * disable the archive status display indicator, you can display your own user interface\n * notifications based on the archiveStarted
and archiveStopped
\n * events dispatched by the Session object. \n *\n * backgroundImageURI
(String) — A URI for an image to display as\n * the background image when a video is not displayed. (A video may not be displayed if\n * you call publishVideo(false)
on the Publisher object). You can pass an http\n * or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the\n * data
URI scheme (instead of http or https) and pass in base-64-encrypted\n * PNG data, such as that obtained from the\n * Publisher.getImgData() method. (For example,\n * you could set the property to a value returned by calling getImgData()
on\n * a previous Publisher object.) If the URL or the image data is invalid, the property\n * is ignored (the attempt to set the image fails silently). \n *\n * buttonDisplayMode
(String) — How to display the microphone controls\n * Possible values are: \"auto\"
(controls are displayed when the stream is first\n * displayed and when the user mouses over the display), \"off\"
(controls are not\n * displayed), and \"on\"
(controls are always displayed). \n *\n * nameDisplayMode
(String) Whether to display the stream name.\n * Possible values are: \"auto\"
(the name is displayed when the stream is first\n * displayed and when the user mouses over the display), \"off\"
(the name is not\n * displayed), and \"on\"
(the name is always displayed). \n *
\n * \n * - \n * videoContentHint (String) Sets the content hint of\n * the the video track of the publisher's stream. This allows browsers to use encoding\n * or processing methods more appropriate to the type of content. You can set this to one of\n * the following values:\n *
\n *
\n * - \n *
\"\"
— No hint is provided (the default).\n * \n * - \n *
\"motion\"
— The track should be treated as if it contains video\n * where motion is important.\n * \n * - \n *
\"detail\"
— The track should be treated as if video details\n * are extra important.\n * \n * - \n *
\"text\"
— The track should be treated as if text details are\n * extra important.\n * \n *
\n * \n * \n * You can read more about these options in the\n * W3C Working Draft.\n * You may want to set the video content hint to optimize performance for screen-sharing\n * videos. For example, you can set it to \"text\"
when sharing a screen that contains\n * a lot of text (such as a slideshow).\n *
\n * \n * Chrome 60+, Safari 12.1+, Edge 79+, and Opera 47+ support video content hints.\n *
\n * \n * Also see the Publisher.setVideoContentHint() method.\n *
\n * - \n * videoSource (String, MediaStreamTrack, Boolean, or null) \n * The ID of the video input device (such as a\n * camera) to be used by the publisher. You can obtain a list of available devices, including\n * video input devices, by calling the OT.getDevices() method. Each\n * device listed by the method has a unique device ID. If you pass in a device ID that does not\n * match an existing video input device, the call to
OT.initPublisher()
fails with an\n * error (error code 1500, \"Unable to Publish\") passed to the completion handler function.\n * \n * If you set this property to null
or false
, the browser does not\n * request access to the camera, and no video is published. In a voice-only call, set this\n * property to null
or false
for each Publisher.\n *
\n * \n * To publish a screen-sharing stream, set this property to \"screen\"
.\n * In older browser versions, you could also pass in \"application\"
or\n * \"window\"
to specify a type of screen-sharing source. However, in current\n * browsers that support screen sharing, passing in any of these values results in\n * the same behavior — the browser displays a dialog box in which the end user\n * selects the screen-sharing source (which can be the entire screen or a specific window).\n * In Electron, screen sharing captures the entire screen, without prompting\n * the user for permission. For screen-sharing streams, the following are\n * default values for other properties: audioFallbackEnabled == false
,\n * maxResolution == {width: 1920, height: 1920}
, mirror == false
,\n * scaleMode == \"fit\"
. Also, the default scaleMode
setting for\n * subscribers to the stream is \"fit\"
.\n *
\n * \n * You can also set this property to a video\n * \n * MediaStreamTrack object. This lets you use the MediaStreamTrack object as the\n * video source for the published stream. For example, you can get a CanvasCaptureMediaStream\n * object by calling the captureStream()
method of a HTMLCanvasElement, and then\n * you can call the getVideoTracks()
method of the CanvasCaptureMediaStream object\n * to get a video MediaStreamTrack object. Or you can get an array of video MediaStreamTrack\n * object from the videoTracks
property of an HTMLMediaElement object. Or you can\n * call the getLocalStreams()
or getRemoteStreams()
method of an\n * RTCPeerConnection object to get an array of MediaStream objects, and then call the\n * getVideoTracks()
method of one of the MediaStream objects to get an array\n * of video MediaStreamTrack objects. Note that by default the publisher uses a microphone\n * as the audio source, so be sure to set the audioSource
option if you want\n * to use a MediaStreamTrack as the audio source or if you do not want to publish an audio\n * track. The default mirror
setting for a Publisher with a MediaStreamTrack video\n * source is false
. You can also call\n * OT.getUserMedia() to get a MediaStream object that uses\n * the microphone and camera as the audio and video sources.\n *
\n * \n * - \n * width (Number or String) The desired initial width of the displayed Publisher\n * video in the HTML page (default: 264 pixels). You can specify the number of pixels as either\n * a number (such as 400) or a string ending in \"px\" (such as \"400px\"). Or you can specify a\n * percentage of the size of the parent element, with a string ending in \"%\" (such as \"100%\").\n * Note: To resize the publisher video, adjust the CSS of the publisher's DOM element\n * (the
element
property of the Publisher object) or (if the width is specified as\n * a percentage) its parent DOM element (see\n * Resizing\n * or repositioning a video).\n * \n *
\n * @param {Function} completionHandler (Optional) A function to be called when the method succeeds\n * or fails in initializing a Publisher object. This function takes one parameter —\n * error
. On success, the error
object is set to null
. On\n * failure, the error
object has two properties: code
(an integer) and\n * message
(a string), which identify the cause of the failure. The method succeeds\n * when the user grants access to the camera and microphone. The method fails if the user denies\n * access to the camera and microphone. The completionHandler
function is called\n * before the Publisher dispatches an accessAllowed
(success) event or an\n * accessDenied
(failure) event.\n * \n * The following code adds a completionHandler
when calling the\n * OT.initPublisher()
method:\n *
\n * \n * var publisher = OT.initPublisher('publisher', null, function (error) {\n * if (error) {\n * console.log(error);\n * } else {\n * console.log(\"Publisher initialized.\");\n * }\n * });\n *
\n *\n * @returns {Publisher} The Publisher object.\n * @see Session.publish()\n * @method OT.initPublisher\n * @memberof OT\n */\n\n\n return function initPublisher(targetElement, properties, completionHandler) {\n logging.debug(`OT.initPublisher(${targetElement})`); // To support legacy (apikey, targetElement, properties) users\n // we check to see if targetElement is actually an apikey. Which we ignore.\n\n if (typeof targetElement === 'string' && !document.getElementById(targetElement)) {\n targetElement = properties;\n properties = completionHandler;\n completionHandler = arguments[3];\n }\n\n if (typeof targetElement === 'function') {\n completionHandler = targetElement;\n properties = undefined;\n targetElement = undefined;\n } else if (isObject(targetElement) && !OTHelpers.isElementNode(targetElement)) {\n completionHandler = properties;\n properties = targetElement;\n targetElement = undefined;\n }\n\n if (typeof properties === 'function') {\n completionHandler = properties;\n properties = undefined;\n }\n\n let errMsg;\n\n if (properties) {\n if (!isObject(properties)) {\n errMsg = 'properties argument to Publisher constructor, if provided, should be an object';\n properties = undefined;\n } else if (properties.insertDefaultUI === false && targetElement) {\n errMsg = 'You cannot specify a target element if insertDefaultUI is false';\n } else if (isNullOrFalse(properties.audioSource) && isNullOrFalse(properties.videoSource)) {\n errMsg = 'You cannot specify both audioSource and videoSource as null or false';\n }\n }\n\n const publisher = new Publisher(properties || {});\n sessionObjects.publishers.add(publisher);\n\n const triggerCallback = function triggerCallback() {\n if (completionHandler && isFunction(completionHandler)) {\n completionHandler(...arguments);\n completionHandler = undefined;\n }\n };\n\n if (errMsg !== undefined) {\n logging.error(errMsg);\n triggerCallback(otError(Errors.INVALID_PARAMETER, new Error(errMsg), ExceptionCodes.INVALID_PARAMETER));\n }\n\n const removeInitSuccessAndCallComplete = function removeInitSuccessAndCallComplete(err) {\n publisher.off('publishComplete', removeHandlersAndCallComplete);\n triggerCallback(err);\n };\n\n let removeHandlersAndCallComplete = function removeHandlersAndCallComplete(err) {\n publisher.off('initSuccess', removeInitSuccessAndCallComplete); // We're only handling the error case here as we're just\n // initing the publisher, not actually attempting to publish.\n\n if (err) {\n triggerCallback(err);\n }\n };\n\n publisher.once('initSuccess', removeInitSuccessAndCallComplete);\n publisher.once('publishComplete', removeHandlersAndCallComplete);\n publisher.publish(targetElement);\n return publisher;\n };\n};\n\n/***/ }),\n/* 307 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n/* WEBPACK VAR INJECTION */(function(Promise) {\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _regenerator = _interopRequireDefault(__webpack_require__(18));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));\n\nvar _extends2 = _interopRequireDefault(__webpack_require__(25));\n\n/* eslint-disable no-param-reassign, global-require, no-underscore-dangle, func-names */\n\n/* eslint-disable max-len */\nconst EventEmitter = __webpack_require__(39);\n\nconst findKey = __webpack_require__(675);\n\nconst isObject = __webpack_require__(8);\n\nconst isFunction = __webpack_require__(13);\n\nconst assign = __webpack_require__(7);\n\nconst eventNames = __webpack_require__(24);\n\nconst eventing = __webpack_require__(5);\n\nconst AnalyticsHelperDefault = __webpack_require__(46);\n\nconst StaticConfigDefault = __webpack_require__(42)();\n\nconst IntervalRunner = __webpack_require__(74);\n\nconst promisify = __webpack_require__(305);\n\nconst testRemovingVideoCodecs = __webpack_require__(677);\n\nconst validateIceConfigFactory = __webpack_require__(678);\n\nconst _require = __webpack_require__(138),\n prependProxyToUrlIfNeeded = _require.prependProxyToUrlIfNeeded;\n\nconst _require2 = __webpack_require__(139),\n getProxyUrl = _require2.getProxyUrl;\n\nmodule.exports = function SessionFactory(deps) {\n if (deps === void 0) {\n deps = {};\n }\n\n const adaptIceServers = deps.adaptIceServers || __webpack_require__(100).adaptIceServers;\n /** @type {AnalyticsHelper} */\n\n\n const AnalyticsHelper = deps.AnalyticsHelper || AnalyticsHelperDefault;\n\n const APIKEY = deps.APIKEY || __webpack_require__(71);\n\n const Capabilities = deps.Capabilities || __webpack_require__(167);\n\n const convertAnvilErrorCode = deps.convertAnvilErrorCode || __webpack_require__(679);\n\n const convertRumorError = deps.convertRumorError || __webpack_require__(308);\n\n const errors = deps.Errors || __webpack_require__(6);\n\n const Events = deps.Events || __webpack_require__(21)();\n\n const Stream = deps.Stream || __webpack_require__(168);\n\n const ExceptionCodes = deps.ExceptionCodes || __webpack_require__(9);\n\n const hasIceRestartsCapability = deps.hasIceRestartsCapability || __webpack_require__(309);\n\n const logging = deps.logging || __webpack_require__(0)('Session');\n\n const otError = deps.otError || __webpack_require__(11)();\n\n const OTErrorClass = deps.OTErrorClass || __webpack_require__(20);\n\n const OTHelpers = deps.OTHelpers || __webpack_require__(4);\n /** @type {typeof StaticConfigDefault} */\n\n\n const StaticConfig = deps.StaticConfig || StaticConfigDefault;\n /** @type {StaticConfigDefault} */\n\n const localStaticConfig = deps.staticConfig || StaticConfig.onlyLocal();\n\n const Publisher = deps.Publisher || __webpack_require__(166)();\n\n const RaptorSocket = deps.RaptorSocket || __webpack_require__(680).default();\n\n const SessionDispatcher = deps.SessionDispatcher || __webpack_require__(316);\n\n const sessionObjects = deps.sessionObjects || __webpack_require__(23);\n\n const sessionTag = deps.sessionTag || __webpack_require__(315);\n\n const socketCloseCodes = deps.socketCloseCodes || __webpack_require__(76);\n\n const Subscriber = deps.Subscriber || __webpack_require__(298)();\n\n const systemRequirements = deps.systemRequirements || __webpack_require__(152);\n\n const uuid = deps.uuid || __webpack_require__(17);\n\n const validateIceConfig = validateIceConfigFactory({\n otError\n });\n const windowMock = deps.global || (typeof window !== undefined ? window : global);\n\n const getSessionInfo = deps.getSessionInfo || __webpack_require__(704)();\n\n const SessionInfo = __webpack_require__(141);\n\n const initPublisher = deps.initPublisher || __webpack_require__(306)({\n Publisher\n });\n /**\n * The Session object returned by the OT.initSession()
method provides access to\n * much of the OpenTok functionality.\n *\n * @class Session\n * @augments EventDispatcher\n *\n * @property {Capabilities} capabilities A {@link Capabilities} object that includes information\n * about the capabilities of the client. All properties of the capabilities
object\n * are undefined until you have connected to a session and the completion handler for the\n * Session.connect()
method has been called without error.\n * @property {Connection} connection The {@link Connection} object for this session. The\n * connection property is only available once the completion handler for the\n * Session.connect()
method has been called successfully. See the\n * Session.connect() method and the {@link Connection} class.\n * @property {String} sessionId The session ID for this session. You pass this value into the\n * OT.initSession()
method when you create the Session object. (Note: a Session\n * object is not connected to the OpenTok server until you call the connect() method of the\n * object and its completion handler is called without error. See the\n * OT.initSession() and\n * the Session.connect()\n * methods.) For more information on sessions and session IDs, see\n * Session creation.\n */\n\n\n const Session = function Session(apiKey, sessionId, _temp) {\n var _this = this;\n\n let _ref = _temp === void 0 ? {} : _temp,\n iceConfig = _ref.iceConfig,\n connectionEventsSuppressed = _ref.connectionEventsSuppressed,\n _ref$ipWhitelist = _ref.ipWhitelist,\n ipWhitelist = _ref$ipWhitelist === void 0 ? false : _ref$ipWhitelist;\n\n const proxyUrl = getProxyUrl();\n /** @type AnalyticsHelperDefault */\n\n const analytics = new AnalyticsHelper();\n\n const getStream = stream => typeof stream === 'string' ? this.streams.get(stream) || {\n id: stream\n } : stream;\n\n eventing(this);\n this._tag = sessionTag; // Check that the client meets the minimum requirements, if they don't the upgrade\n // flow will be triggered.\n\n if (!systemRequirements.check()) {\n systemRequirements.upgrade();\n }\n\n if (sessionId == null) {\n sessionId = apiKey;\n apiKey = null;\n }\n\n validateIceConfig(iceConfig);\n this.id = sessionId;\n this.sessionId = sessionId;\n\n let _socket;\n /** @type IntervalRunner | undefined */\n\n\n let _connectivityAttemptPinger;\n\n let _token;\n\n let _p2p;\n\n let _messagingServer;\n\n let _attemptStartTime;\n\n let _configurationAttemptStartTime;\n\n let _iceServerDetails;\n\n let _initialConnection = true;\n let _isSocketReconnecting = false;\n let _apiKey = apiKey;\n\n const _session = this;\n\n let _sessionId = sessionId;\n\n let _widgetId = uuid();\n\n let _connectionId = uuid();\n\n let _logging = logging;\n let _muteOnEntry = false;\n let disconnectComponents;\n let reset;\n let destroyPublishers;\n let destroySubscribers;\n const setState = OTHelpers.statable(this, ['disconnected', 'connecting', 'connected', 'disconnecting'], 'disconnected');\n this.connection = null;\n this.connections = new OTHelpers.Collection();\n this.streams = new OTHelpers.Collection();\n this.archives = new OTHelpers.Collection(); //--------------------------------------\n // MESSAGE HANDLERS\n //--------------------------------------\n\n /*\n * The sessionConnectFailed event handler\n * @param {Error}\n */\n\n const sessionConnectFailed = function sessionConnectFailed(error) {\n setState('disconnected');\n\n if (!error.code) {\n error.code = ExceptionCodes.CONNECT_FAILED;\n }\n\n _logging.error(`${error.name || 'Unknown Error'}: ${error.message}`);\n\n OTErrorClass.handleJsException({\n error,\n target: this,\n analytics\n });\n this.trigger('sessionConnectFailed', error);\n };\n\n const sessionDisconnectedHandler = function sessionDisconnectedHandler(event) {\n _isSocketReconnecting = false;\n const reason = event.reason;\n this.logEvent('Connect', 'Disconnected', {\n reason: event.reason\n });\n const publicEvent = new Events.SessionDisconnectEvent('sessionDisconnected', reason.replace('networkTimedout', 'networkDisconnected'));\n\n if (this.isConnected()) {\n this.disconnect();\n }\n\n reset();\n disconnectComponents.call(this, reason);\n setTimeout(() => {\n this.dispatchEvent(publicEvent); // Although part of the defaultAction for sessionDisconnected we have\n // chosen to still destroy Publishers within the session as there is\n // another mechanism to stop a Publisher from being destroyed.\n // Publishers use preventDefault on the Publisher streamDestroyed event\n\n destroyPublishers.call(this, publicEvent.reason);\n\n if (!publicEvent.isDefaultPrevented()) {\n destroySubscribers.call(this, publicEvent.reason);\n }\n });\n };\n\n const connectionCreatedHandler = function connectionCreatedHandler(connection) {\n // With connectionEventsSuppressed, connections objects are still added internally, but they\n // don't come from rumor messages, and can't have corresponding delete events, so we don't\n // emit created events.\n if (connectionEventsSuppressed) {\n return;\n } // We don't broadcast events for the symphony connection\n\n\n if (connection.id.match(/^symphony\\./)) {\n return;\n }\n\n this.dispatchEvent(new Events.ConnectionEvent(eventNames.CONNECTION_CREATED, connection));\n };\n\n const connectionDestroyedHandler = function connectionDestroyedHandler(connection, reason) {\n // We don't broadcast events for the symphony connection\n if (connection.id.match(/^symphony\\./)) {\n return;\n } // Don't delete the connection if it's ours. This only happens when\n // we're about to receive a session disconnected and session disconnected\n // will also clean up our connection.\n\n\n if (_socket && connection.id === _socket.id()) {\n return;\n }\n\n this.dispatchEvent(new Events.ConnectionEvent(eventNames.CONNECTION_DESTROYED, connection, reason));\n };\n\n const streamCreatedHandler = function streamCreatedHandler(stream) {\n if (stream && stream.connection && (!this.connection || stream.connection.id !== this.connection.id)) {\n this.dispatchEvent(new Events.StreamEvent(eventNames.STREAM_CREATED, stream, null, false));\n }\n };\n\n const streamPropertyModifiedHandler = function streamPropertyModifiedHandler(event) {\n const stream = event.target;\n const propertyName = event.changedProperty;\n let newValue = event.newValue;\n\n if (propertyName === 'videoDisableWarning' || propertyName === 'audioDisableWarning') {\n return; // These are not public properties, skip top level event for them.\n }\n\n if (propertyName === 'videoDimensions') {\n newValue = {\n width: newValue.width,\n height: newValue.height\n };\n }\n\n this.dispatchEvent(new Events.StreamPropertyChangedEvent(eventNames.STREAM_PROPERTY_CHANGED, stream, propertyName, event.oldValue, newValue));\n };\n\n const streamDestroyedHandler = function streamDestroyedHandler(stream, reason) {\n if (reason === void 0) {\n reason = 'clientDisconnected';\n }\n\n const event = new Events.StreamEvent('streamDestroyed', stream, reason, true);\n\n const disconnectAndDestroySubscribers = () => {\n // If we are subscribed to any of the streams we should unsubscribe\n sessionObjects.subscribers.where({\n streamId: stream.id\n }).filter(subscriber => subscriber.session.id === this.id && subscriber.stream).forEach(subscriber => {\n subscriber._disconnect({\n reason\n });\n\n if (!event.isDefaultPrevented()) {\n subscriber._destroy({\n reason,\n noStateTransition: true\n });\n }\n });\n }; // if the stream is one of ours we delegate handling to the publisher itself.\n\n\n if (stream.connection.id === this.connection.id) {\n sessionObjects.publishers.where({\n streamId: stream.id\n }).forEach(function (publisher) {\n publisher._.unpublishStreamFromSession(stream, this, reason);\n }, this);\n } else {\n // The streamDestroyed event is only dispatched by the session when the stream is not one of ours.\n // In the case the stream is ours, the streamDestroyed is dispatched by the publisher.\n this.dispatchEvent(event);\n }\n\n disconnectAndDestroySubscribers(); // @TODO Add a else with a one time warning that this no longer cleans up the publisher\n };\n\n const archiveCreatedHandler = function archiveCreatedHandler(archive) {\n this.dispatchEvent(new Events.ArchiveEvent('archiveStarted', archive));\n };\n\n const archiveDestroyedHandler = function archiveDestroyedHandler(archive) {\n this.dispatchEvent(new Events.ArchiveEvent('archiveDestroyed', archive));\n };\n\n const archiveUpdatedHandler = function archiveUpdatedHandler(event) {\n const archive = event.target;\n const propertyName = event.changedProperty;\n const newValue = event.newValue;\n\n if (propertyName === 'status' && newValue === 'stopped') {\n this.dispatchEvent(new Events.ArchiveEvent('archiveStopped', archive));\n } else {\n this.dispatchEvent(new Events.ArchiveEvent('archiveUpdated', archive));\n }\n };\n\n const init = function init() {\n _session.token = null;\n _token = null;\n setState('disconnected');\n _socket = null;\n\n if (_connectivityAttemptPinger) {\n _connectivityAttemptPinger.stop();\n\n _connectivityAttemptPinger = null;\n }\n\n _session.connection = null;\n _session.capabilities = new Capabilities([]);\n\n _session.connections.destroy();\n\n _session.streams.destroy();\n\n _session.archives.destroy();\n }; // Put ourselves into a pristine state\n\n\n reset = function reset() {\n // reset connection id now so that following calls to testNetwork and connect will share\n // the same new session id. We need to reset here because testNetwork might be call after\n // and it is always called before the session is connected\n // on initial connection we don't reset\n _connectionId = uuid();\n init();\n };\n\n disconnectComponents = function disconnectComponents(reason) {\n sessionObjects.publishers.where({\n session: this\n }).forEach(publisher => {\n publisher.disconnect(reason);\n });\n sessionObjects.subscribers.where({\n session: this\n }).forEach(subscriber => {\n subscriber._disconnect();\n });\n };\n\n destroyPublishers = function destroyPublishers(reason) {\n sessionObjects.publishers.where({\n session: this\n }).forEach(publisher => {\n publisher._.streamDestroyed(reason);\n });\n };\n\n destroySubscribers = function destroySubscribers(reason) {\n sessionObjects.subscribers.where({\n session: this\n }).forEach(subscriber => {\n subscriber._destroy({\n reason\n });\n });\n };\n\n const connectMessenger = function connectMessenger() {\n _logging.debug('OT.Session: connecting to Raptor');\n\n const messagingUrl = prependProxyToUrlIfNeeded(this.sessionInfo.messagingURL, proxyUrl); // capabilities supported by default.\n\n const capabilities = ['forceMute', 'adaptive'];\n _socket = new RaptorSocket(_connectionId, _widgetId, messagingUrl, this.sessionInfo.symphonyAddress, SessionDispatcher(this, {\n connectionEventsSuppressed\n }), analytics, capabilities);\n /**\n * Maps an error from RaptorSocket.connect to its corresponding name\n * @param {string} reason - Failure reason\n * @param {number} code - Error code\n * @return {string|undefined} Error name\n */\n\n function getErrorNameFromCode(reason, code) {\n let name;\n\n switch (reason) {\n case 'WebSocketConnection':\n name = findKey(socketCloseCodes.codes, x => x === code);\n\n if (name) {\n return errors[`SOCKET_${name}`];\n }\n\n break;\n\n case 'ConnectToSession':\n case 'GetSessionState':\n switch (code) {\n case ExceptionCodes.CONNECT_FAILED:\n return errors.CONNECT_FAILED;\n\n case ExceptionCodes.UNEXPECTED_SERVER_RESPONSE:\n return errors.UNEXPECTED_SERVER_RESPONSE;\n\n case ExceptionCodes.CONNECTION_LIMIT_EXCEEDED:\n return errors.CONNECTION_LIMIT_EXCEEDED;\n\n default:\n break;\n }\n\n break;\n\n default:\n break;\n }\n\n return undefined;\n }\n\n _socket.connect(_token, this.sessionInfo, {\n connectionEventsSuppressed\n }, (error, sessionState) => {\n if (error) {\n const payload = {};\n let options;\n\n if (error.reason === 'ConnectToSession' || error.reason === 'GetSessionState') {\n const converted = convertRumorError(error);\n assign(payload, {\n originalMessage: error.message,\n originalCode: error.code\n });\n error.code = converted.code;\n error.message = converted.message;\n }\n\n if (error.code || error.message || error.reason) {\n options = {\n failureCode: error.code,\n failureMessage: error.message,\n failureReason: error.reason,\n socketId: _socket.socketId\n };\n }\n\n _socket = null;\n this.logConnectivityEvent('Failure', payload, options);\n const errorName = getErrorNameFromCode(error.reason, error.code);\n\n if (errorName) {\n error = otError(errorName, new Error(error.message), error.code);\n }\n\n sessionConnectFailed.call(this, error);\n return;\n }\n\n _logging.debug('OT.Session: Received session state from Raptor', sessionState);\n\n this.connection = this.connections.get(_socket.id());\n\n if (this.connection) {\n this.capabilities = this.connection.permissions;\n }\n\n setState('connected');\n this.logConnectivityEvent('Success', {\n connectionId: this.connection.id\n }); // Listen for our own connection's destroyed event so we know when we've been disconnected.\n\n this.connection.on('destroyed', sessionDisconnectedHandler, this);\n this.dispatchEvent(new Events.SessionConnectEvent(eventNames.SESSION_CONNECTED)); // Listen for connection updates\n\n this.connections.on({\n add: connectionCreatedHandler,\n remove: connectionDestroyedHandler\n }, this); // Listen for stream updates\n\n this.streams.on({\n add: streamCreatedHandler,\n remove: streamDestroyedHandler,\n update: streamPropertyModifiedHandler\n }, this);\n this.archives.on({\n add: archiveCreatedHandler,\n remove: archiveDestroyedHandler,\n update: archiveUpdatedHandler\n }, this);\n\n this.connections._triggerAddEvents(); // { id: this.connection.id }\n\n\n this.streams._triggerAddEvents(); // { id: this.stream.id }\n\n\n this.archives._triggerAddEvents();\n });\n }; // Check whether we have permissions to perform the action.\n\n\n const permittedTo = action => this.capabilities.permittedTo(action);\n\n const dispatchOTError = (error, completionHandler) => {\n logging.error(`${error.name}: ${error.message}`);\n\n if (typeof completionHandler === 'function') {\n completionHandler(error);\n }\n\n OTErrorClass.handleJsException({\n error,\n target: this,\n analytics\n });\n };\n\n this.reportIssue = (_ref2) => {\n let id = _ref2.id;\n return promisify(analytics.logEvent.bind(analytics))({\n action: 'ReportIssue',\n variation: 'Event',\n connectionId: _connectionId,\n payload: {\n reportIssueId: id\n }\n }, null);\n };\n\n this.logEvent = function (action, variation, payload, options) {\n let event = {\n action,\n variation,\n payload,\n sessionId: _sessionId,\n messagingServer: _messagingServer,\n p2p: _p2p,\n partnerId: _apiKey,\n connectionId: _connectionId\n };\n\n if (options) {\n event = assign(options, event);\n }\n\n analytics.logEvent(event);\n };\n\n this.logConfigurationFileEvent = function (variation, payload, options) {\n if (payload === void 0) {\n payload = null;\n }\n\n if (options === void 0) {\n options = {};\n }\n\n if (variation === 'Attempt') {\n _configurationAttemptStartTime = new Date().getTime();\n } else if (variation === 'Failure' || variation === 'Success') {\n const attemptDuration = new Date().getTime() - _configurationAttemptStartTime;\n\n assign(options, {\n attemptDuration\n });\n }\n\n if (proxyUrl) {\n options.proxyUrl = proxyUrl;\n }\n\n this.logEvent('ConfigurationFile', variation, payload, (0, _extends2.default)({}, options));\n };\n\n this.logConnectivityEvent = function (variation, payload, options) {\n if (payload === void 0) {\n payload = null;\n }\n\n if (options === void 0) {\n options = {};\n }\n\n if (proxyUrl) {\n options.proxyUrl = proxyUrl;\n }\n\n if (variation === 'Attempt') {\n if (_connectivityAttemptPinger) {\n _connectivityAttemptPinger.stop();\n\n logging.error('_connectivityAttemptPinger should have been cleaned up');\n }\n\n _attemptStartTime = new Date().getTime();\n _connectivityAttemptPinger = new IntervalRunner(() => {\n this.logEvent('Connect', 'Attempting', payload, (0, _extends2.default)({}, options));\n }, 1 / 5, 6);\n\n _connectivityAttemptPinger.start();\n }\n\n if (variation === 'Failure' || variation === 'Success' || variation === 'Cancel') {\n const logConnect = _iceConfig => {\n if (_connectivityAttemptPinger) {\n _connectivityAttemptPinger.stop();\n\n _connectivityAttemptPinger = undefined;\n }\n\n this.logEvent('Connect', variation, payload, (0, _extends2.default)({}, options, {\n attemptDuration: new Date().getTime() - _attemptStartTime,\n iceConfig: _iceConfig,\n ipWhitelist\n }));\n };\n\n if (variation === 'Success') {\n this._.getIceConfig().then(config => {\n const _iceConfig = {\n includeServers: iceConfig && iceConfig.includeServers || 'all',\n transportPolicy: config.transportPolicy\n };\n _iceConfig.servers = config.servers ? config.servers.map(server => ({\n url: server.urls\n })) : [];\n logConnect(_iceConfig);\n });\n } else {\n logConnect();\n }\n } else {\n this.logEvent('Connect', variation, payload, options);\n }\n };\n /**\n * Connects to an OpenTok session.\n * \n * Upon a successful connection, the completion handler (the second parameter of the method) is\n * invoked without an error object passed in. (If there is an error connecting, the completion\n * handler is invoked with an error object.) Make sure that you have successfully connected to the\n * session before calling other methods of the Session object.\n *
\n * \n * The Session object dispatches a connectionCreated
event when any client\n * (including your own) connects to the session.\n *
\n *\n * \n * Example\n *
\n * \n * The following code initializes a session and sets up an event listener for when the session\n * connects:\n *
\n * \n * var apiKey = \"\"; // Replace with your API key. See https://tokbox.com/account\n * var sessionID = \"\"; // Replace with your own session ID.\n * // See https://tokbox.com/developer/guides/create-session/.\n * var token = \"\"; // Replace with a generated token.\n * // See https://tokbox.com/developer/guides/create-token/.\n *\n * var session = OT.initSession(apiKey, sessionID);\n * session.connect(token, function(error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * // You have connected to the session. You could publish a stream now.\n * }\n * });\n *
\n * \n *\n *
\n * Events dispatched:\n *
\n *\n * \n * exception
(ExceptionEvent) Dispatched\n * by the OT class locally in the event of an error.\n *
\n * \n * connectionCreated
(ConnectionEvent) \n * Dispatched by the Session object on all clients connected to the session.\n *
\n * \n * sessionConnected
(SessionConnectEvent)\n * Dispatched locally by the Session object when the connection is established. However,\n * you can pass a completion handler function in as the second parameter of the\n * connect()
and use this function instead of a listener for the\n * sessionConnected
event.\n *
\n *\n * @param {String} token The session token. You generate a session token using our\n * server-side libraries or at your\n * TokBox account page. For more information, see\n * Connection token creation.\n *\n * @param {Function} completionHandler (Optional) A function to be called when the call to the\n * connect()
method succeeds or fails. This function takes one parameter —\n * error
(see the Error object).\n * On success, the completionHandler
function is not passed any\n * arguments. On error, the function is passed an error
object parameter\n * (see the Error object). The\n * error
object has two properties: code
(an integer) and\n * message
(a string), which identify the cause of the failure. The following\n * code adds a completionHandler
when calling the connect()
method:\n * \n * session.connect(token, function (error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * console.log(\"Connected to session.\");\n * }\n * });\n *
\n * \n * Note that upon connecting to the session, the Session object dispatches a\n * sessionConnected
event in addition to calling the completionHandler
.\n * The SessionConnectEvent object, which defines the sessionConnected
event,\n * includes connections
and streams
properties, which\n * list the connections and streams in the session when you connect.\n *
\n *\n * @see SessionConnectEvent\n * @method #connect\n * @memberOf Session\n */\n\n\n this.connect = function () {\n let token;\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n if (args.length > 1 && (typeof args[0] === 'string' || typeof args[0] === 'number') && typeof args[1] === 'string') {\n if (apiKey == null) {\n _apiKey = args[0].toString();\n }\n\n token = args[1];\n } else {\n token = args[0];\n } // The completion handler is always the last argument.\n\n\n const completionHandler = args[args.length - 1];\n\n if (_this.is('connecting', 'connected')) {\n _logging.warn(`OT.Session: Cannot connect, the session is already ${_this.state}`);\n\n return _this;\n } // On disconnects, GSI fields may be unintentionally cached and logged so we instantiate a new instance.\n\n\n analytics.sessionInfo = new SessionInfo();\n init();\n setState('connecting');\n const currentConnectionId = _connectionId;\n\n function checkInterrupted() {\n const interrupted = _connectionId !== currentConnectionId;\n\n if (interrupted) {\n logging.debug('Connection was interrupted');\n }\n\n return interrupted;\n }\n\n _this.token = !isFunction(token) && token;\n _token = !isFunction(token) && token; // Get a new widget ID when reconnecting.\n\n if (_initialConnection) {\n _initialConnection = false;\n } else {\n _widgetId = uuid();\n }\n\n if (completionHandler && isFunction(completionHandler)) {\n let cleanup;\n\n const onCompleteSuccess = function onCompleteSuccess() {\n cleanup();\n\n for (var _len2 = arguments.length, cbArgs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n cbArgs[_key2] = arguments[_key2];\n }\n\n completionHandler(undefined, ...cbArgs);\n };\n\n const onCompleteFailure = function onCompleteFailure() {\n cleanup();\n completionHandler(...arguments);\n };\n\n cleanup = () => {\n _this.off('sessionConnected', onCompleteSuccess);\n\n _this.off('sessionConnectFailed', onCompleteFailure);\n };\n\n _this.once('sessionConnected', onCompleteSuccess);\n\n _this.once('sessionConnectFailed', onCompleteFailure);\n }\n\n if (_apiKey == null || isFunction(_apiKey)) {\n setTimeout(sessionConnectFailed.bind(_this, otError(errors.AUTHENTICATION_ERROR, new Error('API Key is undefined. You must pass an API Key to initSession.'), ExceptionCodes.AUTHENTICATION_ERROR)));\n return _this;\n }\n\n if (!_sessionId || isObject(_sessionId) || Array.isArray(_sessionId)) {\n let errorMsg;\n\n if (!_sessionId) {\n errorMsg = 'SessionID is undefined. You must pass a sessionID to initSession.';\n } else {\n errorMsg = 'SessionID is not a string. You must use string as the session ID passed into ' + 'OT.initSession().';\n _sessionId = _sessionId.toString();\n }\n\n setTimeout(sessionConnectFailed.bind(_this, otError(errors.INVALID_SESSION_ID, new Error(errorMsg), ExceptionCodes.INVALID_SESSION_ID)));\n\n _this.logConnectivityEvent('Attempt');\n\n _this.logConnectivityEvent('Failure', null, {\n failureReason: 'ConnectToSession',\n failureCode: ExceptionCodes.INVALID_SESSION_ID,\n failureMessage: errorMsg\n });\n\n return _this;\n }\n\n _this.apiKey = _apiKey.toString();\n _apiKey = _apiKey.toString(); // TODO: Ugly hack, make sure APIKEY is set\n\n if (APIKEY.value.length === 0) {\n APIKEY.value = _apiKey;\n }\n\n const hasStaticConfigUrl = Boolean(StaticConfig.onlyLocal().configUrl);\n const useIpWhitelistConfigUrl = ipWhitelist === true && Boolean(StaticConfig.onlyLocal().ipWhitelistConfigUrl);\n\n if (!hasStaticConfigUrl && !useIpWhitelistConfigUrl) {\n _this.logConfigurationFileEvent('Event', {\n message: 'No configUrl, using local config only'\n });\n } else {\n _this.logConfigurationFileEvent('Attempt');\n }\n\n const futureStaticConfig = hasStaticConfigUrl || useIpWhitelistConfigUrl ? StaticConfig.get({\n partnerId: _apiKey,\n token,\n useIpWhitelistConfigUrl,\n proxyUrl\n }) : Promise.resolve(StaticConfig.onlyLocal());\n futureStaticConfig.then(staticConfig => {\n if (hasStaticConfigUrl || useIpWhitelistConfigUrl) {\n _this.logConfigurationFileEvent('Success');\n }\n\n return staticConfig;\n }, err => {\n if (hasStaticConfigUrl || useIpWhitelistConfigUrl) {\n _this.logConfigurationFileEvent('Failure', {\n failureMessage: err.message,\n failureStack: err.stack\n });\n }\n\n return StaticConfig.onlyLocal();\n }).then(staticConfig => {\n _this.staticConfig = staticConfig;\n analytics.staticConfig = staticConfig;\n\n if (staticConfig.apiEnabled === false) {\n throw otError(errors.API_KEY_DISABLED, new Error('The API KEY has been disabled. Access to the service is currently being ' + 'restricted. Please contact support.'));\n }\n\n if (checkInterrupted()) {\n return undefined;\n }\n\n _this.logConnectivityEvent('Attempt');\n\n if (proxyUrl) {\n _this.logEvent('SessionInfo', 'Attempt', null, {\n proxyUrl\n });\n } else {\n _this.logEvent('SessionInfo', 'Attempt');\n }\n\n const onSessionInfoError = error => {\n // @todo I think we should move convertAnvilErrorCode to after we log the Failure. It's\n // a lossy process, and the more information we have in our logged failure, the better\n // we can understand the failures.\n error.code = convertAnvilErrorCode(error.code);\n\n _this.logConnectivityEvent('Failure', null, {\n failureReason: 'GetSessionInfo',\n failureCode: error.code || 'No code',\n failureMessage: error.message,\n failureName: error.name\n });\n\n if (error.name) {\n error = otError(error.name, new Error(`${error.message}${error.code ? ` (${error.code})` : ''}`), error.code);\n }\n\n sessionConnectFailed.call(_this, error);\n };\n\n const onSessionInfoSuccess = sessionInfo => {\n if (checkInterrupted()) {\n return;\n }\n\n if (sessionInfo.partnerId && sessionInfo.partnerId !== _apiKey) {\n // The apiKey does not match, this is an error\n const reason = 'Authentication Error: The API key does not match the token or session.';\n onSessionInfoError(otError(errors.AUTHENTICATION_ERROR, new Error(reason), ExceptionCodes.AUTHENTICATION_ERROR));\n return;\n }\n\n analytics.sessionInfo = sessionInfo;\n const sessionInfoSuccessLogPayload = {\n features: {\n reconnection: sessionInfo.reconnection,\n renegotiation: hasIceRestartsCapability() && sessionInfo.renegotiation,\n simulcast: sessionInfo.simulcast === undefined ? false : sessionInfo.simulcast && OTHelpers.env.name === 'Chrome'\n }\n };\n\n if (proxyUrl) {\n sessionInfoSuccessLogPayload.proxyUrl = proxyUrl;\n }\n\n if (_this.is('connecting')) {\n _this.sessionInfo = sessionInfo;\n\n _this._.setIceServers(_this.sessionInfo.iceServers);\n\n _p2p = sessionInfo.p2pEnabled;\n _messagingServer = sessionInfo.messagingServer;\n\n _this.logEvent('SessionInfo', 'Success', null, sessionInfoSuccessLogPayload, {\n messagingServer: sessionInfo.messagingServer\n });\n /**\n * The only sessionInfo overrides that was being used is\n */\n\n\n const overrides = staticConfig.sessionInfoOverrides;\n\n if (overrides != null && typeof overrides === 'object') {\n Object.keys(overrides).forEach(key => {\n Object.defineProperty(_this.sessionInfo, key, {\n value: overrides[key]\n });\n });\n }\n\n connectMessenger.call(_this);\n }\n };\n\n const targetUrl = prependProxyToUrlIfNeeded(staticConfig.apiUrl, proxyUrl);\n return getSessionInfo({\n anvilUrl: targetUrl,\n sessionId,\n token: _token,\n connectionId: _connectionId,\n clientVersion: staticConfig.clientVersion\n }).then(onSessionInfoSuccess, onSessionInfoError);\n }).catch(err => {\n sessionConnectFailed.call(_this, err);\n });\n return _this;\n };\n /**\n * Disconnects from the OpenTok session.\n *\n * \n * Calling the disconnect()
method ends your connection with the session. In the\n * course of terminating your connection, it also ceases publishing any stream(s) you were\n * publishing.\n *
\n * \n * Session objects on remote clients dispatch streamDestroyed
events for any\n * stream you were publishing. The Session object dispatches a sessionDisconnected
\n * event locally. The Session objects on remote clients dispatch connectionDestroyed
\n * events, letting other connections know you have left the session. The\n * {@link SessionDisconnectEvent} and {@link StreamEvent} objects that define the\n * sessionDisconnect
and connectionDestroyed
events each have a\n * reason
property. The reason
property lets the developer determine\n * whether the connection is being terminated voluntarily and whether any streams are being\n * destroyed as a byproduct of the underlying connection's voluntary destruction.\n *
\n * \n * If the session is not currently connected, calling this method causes a warning to be logged.\n * See OT.setLogLevel().\n *
\n *\n * \n * Note: If you intend to reuse a Publisher object created using\n * OT.initPublisher()
to publish to different sessions sequentially, call either\n * Session.disconnect()
or Session.unpublish()
. Do not call both.\n * Then call the preventDefault()
method of the Publisher's streamDestroyed
\n * event object to prevent the Publisher object from being removed from the page. Be sure to\n * call preventDefault()
only if the connection.connectionId
property\n * of the Stream object in the event matches the connection.connectionId
property of\n * your Session object (to ensure that you are preventing the default behavior for your published\n * streams, not for other streams that you subscribe to).\n *
\n *\n * \n * Events dispatched:\n *
\n * \n * sessionDisconnected
\n * (SessionDisconnectEvent)\n * Dispatched locally when the connection is disconnected.\n *
\n * \n * connectionDestroyed
(ConnectionEvent) \n * Dispatched on other clients, along with the streamDestroyed
event (as warranted).\n *
\n *\n * \n * streamDestroyed
(StreamEvent) \n * Dispatched on other clients if streams are lost as a result of the session disconnecting.\n *
\n *\n * @method #disconnect\n * @memberOf Session\n */\n\n\n this.disconnect = function () {\n if (_socket && _socket.isNot('disconnected')) {\n if (_socket.isNot('disconnecting')) {\n if (!_socket.isNot('connecting')) {\n this.logConnectivityEvent('Cancel');\n }\n\n setState('disconnecting');\n\n _socket.disconnect();\n }\n } else {\n reset();\n }\n };\n\n this.destroy = function () {\n this.streams.destroy();\n this.connections.destroy();\n this.archives.destroy();\n this.disconnect();\n };\n /**\n * The publish()
method starts publishing an audio-video stream to the session.\n * The audio-video stream is captured from a local microphone and webcam. Upon successful\n * publishing, the Session objects on all connected clients dispatch the\n * streamCreated
event.\n * \n *\n * \n * You pass a Publisher object as the one parameter of the method. You can initialize a\n * Publisher object by calling the OT.initPublisher()\n * method. Before calling Session.publish()
.\n *
\n *\n * This method takes an alternate form: publish(targetElement: String | HTMLElement,\n * properties: Object, completionHandler: Function): Publisher
In this form, you do\n not pass a Publisher object into the function. Instead, you pass in a targetElement
\n (the target HTML element or the ID of the target HTML element for the Publisher), an optional\n properties
object that defines options for the Publisher (see\n OT.initPublisher()), and an optional completion handler function.\n * The method returns a new Publisher object, which starts sending an audio-video stream to the\n * session. The remainder of this documentation describes the form that takes a single Publisher\n * object as a parameter.\n *\n *
\n * A local display of the published stream is created on the web page by replacing\n * the specified element in the DOM with a streaming video display. The video stream\n * is automatically mirrored horizontally so that users see themselves and movement\n * in their stream in a natural way. If the width and height of the display do not match\n * the 4:3 aspect ratio of the video signal, the video stream is cropped to fit the\n * display.\n *
\n *\n * \n * If calling this method creates a new Publisher object and the OpenTok library does not\n * have access to the camera or microphone, the web page alerts the user to grant access\n * to the camera and microphone.\n *
\n *\n * \n * The OT object dispatches an exception
event if the user's role does not\n * include permissions required to publish. For example, if the user's role is set to subscriber,\n * then they cannot publish. You define a user's role when you create the user token\n * (see Token creation overview).\n * You pass the token string as a parameter of the connect()
method of the Session\n * object. See ExceptionEvent and\n * OT.on().\n *
\n * \n * The application throws an error if the session is not connected.\n *
\n *\n * Events dispatched:
\n * \n * exception
(ExceptionEvent) Dispatched\n * by the OT object. This can occur when user's role does not allow publishing (the\n * code
property of event object is set to 1500); it can also occur if the\n * connection fails to connect (the code
property of event object is set to 1013).\n * WebRTC is a peer-to-peer protocol, and it is possible that connections will fail to connect.\n * The most common cause for failure is a firewall that the protocol cannot traverse.\n *
\n * \n * streamCreated
(StreamEvent) \n * The stream has been published. The Session object dispatches this on all clients\n * subscribed to the stream, as well as on the publisher's client.\n *
\n *\n * Example
\n *\n * \n * The following example publishes a video once the session connects:\n *
\n * \n * var apiKey = \"\"; // Replace with your API key. See https://tokbox.com/account\n * var sessionId = \"\"; // Replace with your own session ID.\n * // https://tokbox.com/developer/guides/create-session/.\n * var token = \"\"; // Replace with a generated token that has been assigned the publish role.\n * // See https://tokbox.com/developer/guides/create-token/.\n * var session = OT.initSession(apiKey, sessionID);\n * session.connect(token, function(error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * var publisherOptions = {width: 400, height:300, name:\"Bob's stream\"};\n * // This assumes that there is a DOM element with the ID 'publisher':\n * publisher = OT.initPublisher('publisher', publisherOptions);\n * session.publish(publisher);\n * }\n * });\n *
\n *\n * @param {Publisher} publisher A Publisher object, which you initialize by calling the\n * OT.initPublisher() method.\n *\n * @param {Function} completionHandler (Optional) A function to be called when the call to the\n * publish()
method succeeds or fails. This function takes one parameter —\n * error
. On success, the completionHandler
function is not passed any\n * arguments. On error, the function is passed an error
object parameter\n * (see the Error object). The\n * error
object has two properties: code
(an integer) and\n * message
(a string), which identify the cause of the failure. Calling\n * publish()
fails if the role assigned to your token is not \"publisher\" or\n * \"moderator\"; in this case the error.name
property is set to\n * \"OT_PERMISSION_DENIED\"
. Calling publish()
also fails if the\n * client fails to connect; in this case the error.name
property is set to\n * \"OT_NOT_CONNECTED\"
. The following code adds a completion handler when\n * calling the publish()
method:\n * \n * session.publish(publisher, null, function (error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * console.log(\"Publishing a stream.\");\n * }\n * });\n *
\n *\n * @returns The Publisher object for this stream.\n *\n * @method #publish\n * @memberOf Session\n */\n\n\n this.publish = function (publisher, properties, completionHandler) {\n if (typeof publisher === 'function') {\n completionHandler = publisher;\n publisher = undefined;\n }\n\n if (typeof properties === 'function') {\n completionHandler = properties;\n properties = undefined;\n }\n\n completionHandler = completionHandler || function () {};\n\n if (this.isNot('connected')) {\n analytics.logError(1010, 'OT.exception', 'We need to be connected before you can publish', null, {\n action: 'Publish',\n variation: 'Failure',\n failureReason: 'unconnected',\n failureCode: ExceptionCodes.NOT_CONNECTED,\n failureMessage: 'We need to be connected before you can publish',\n sessionId: _sessionId,\n streamId: publisher && publisher.stream ? publisher.stream.id : null,\n p2p: this.sessionInfo ? this.sessionInfo.p2pEnabled : undefined,\n messagingServer: this.sessionInfo ? this.sessionInfo.messagingServer : null,\n partnerId: _apiKey\n });\n dispatchOTError(otError(errors.NOT_CONNECTED, new Error('We need to be connected before you can publish'), ExceptionCodes.NOT_CONNECTED), completionHandler);\n return null;\n }\n\n if (!permittedTo('publish')) {\n const errorMessage = 'This token does not allow publishing. The role must be at least ' + '`publisher` to enable this functionality';\n const options = {\n failureReason: 'Permission',\n failureCode: ExceptionCodes.UNABLE_TO_PUBLISH,\n failureMessage: errorMessage\n };\n this.logEvent('Publish', 'Failure', null, options);\n dispatchOTError(otError(errors.PERMISSION_DENIED, new Error(errorMessage), ExceptionCodes.UNABLE_TO_PUBLISH), completionHandler);\n return null;\n } // If the user has passed in an ID of a element then we create a new publisher.\n\n\n if (!publisher || typeof publisher === 'string' || OTHelpers.isElementNode(publisher)) {\n // Initiate a new Publisher with the new session credentials\n publisher = initPublisher(publisher, properties);\n } else if (publisher instanceof Publisher) {\n // If the publisher already has a session attached to it we can\n if ('session' in publisher && publisher.session && 'sessionId' in publisher.session) {\n // send a warning message that we can't publish again.\n if (publisher.session.sessionId === this.sessionId) {\n _logging.warn(`Cannot publish ${publisher.guid()} again to ${this.sessionId}. Please call session.unpublish(publisher) first.`);\n } else {\n _logging.warn(`Cannot publish ${publisher.guid()} publisher already attached to ${publisher.session.sessionId}. Please call session.unpublish(publisher) first.`);\n }\n }\n } else {\n 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);\n return undefined;\n }\n\n if (_muteOnEntry) {\n publisher._.forceMuteAudio();\n } // Add publisher reference to the session\n\n\n publisher._.publishToSession(this, analytics).catch(err => {\n err.message = `Session.publish :: ${err.message}`;\n\n _logging.error(err.code, err.message);\n\n throw err;\n }).then(() => publisher).then(function () {\n for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {\n args[_key3] = arguments[_key3];\n }\n\n return completionHandler(null, ...args);\n }, err => completionHandler(err)); // return the embed publisher\n\n\n return publisher;\n };\n /**\n * Ceases publishing the specified publisher's audio-video stream\n * to the session. By default, the local representation of the audio-video stream is\n * removed from the web page. Upon successful termination, the Session object on every\n * connected web page dispatches\n * a streamDestroyed
event.\n * \n *\n * \n * To prevent the Publisher from being removed from the DOM, add an event listener for the\n * streamDestroyed
event dispatched by the Publisher object and call the\n * preventDefault()
method of the event object.\n *
\n *\n * \n * Note: If you intend to reuse a Publisher object created using\n * OT.initPublisher()
to publish to different sessions sequentially, call\n * either Session.disconnect()
or Session.unpublish()
. Do not call\n * both. Then call the preventDefault()
method of the streamDestroyed
\n * or sessionDisconnected
event object to prevent the Publisher object from being\n * removed from the page. Be sure to call preventDefault()
only if the\n * connection.connectionId
property of the Stream object in the event matches the\n * connection.connectionId
property of your Session object (to ensure that you are\n * preventing the default behavior for your published streams, not for other streams that you\n * subscribe to).\n *
\n *\n * Events dispatched:
\n *\n * \n * streamDestroyed
(StreamEvent) \n * The stream associated with the Publisher has been destroyed. Dispatched on by the\n * Publisher on on the Publisher's browser. Dispatched by the Session object on\n * all other connections subscribing to the publisher's stream.\n *
\n *\n * Example
\n *\n * The following example publishes a stream to a session and adds a Disconnect link to the\n * web page. Clicking this link causes the stream to stop being published.\n *\n * \n * <script>\n * var apiKey = \"\"; // Replace with your API key. See https://tokbox.com/account\n * var sessionID = \"\"; // Replace with your own session ID.\n * // See https://tokbox.com/developer/guides/create-session/.\n * var token = \"\"; // Replace with a generated token.\n * // See https://tokbox.com/developer/guides/create-token/.\n * var publisher;\n * var session = OT.initSession(apiKey, sessionID);\n * session.connect(token, function(error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * // This assumes that there is a DOM element with the ID 'publisher':\n * publisher = OT.initPublisher('publisher');\n * session.publish(publisher);\n * }\n * });\n *\n * function unpublish() {\n * session.unpublish(publisher);\n * }\n * </script>\n *\n * <body>\n *\n * <div id=\"publisherContainer/>\n * <br/>\n *\n * <a href=\"javascript:unpublish()\">Stop Publishing</a>\n *\n * </body>\n *\n *
\n *\n * @see publish()\n *\n * @see streamDestroyed event\n *\n * @param {Publisher} publisher The Publisher object to stop streaming.\n *\n * @method #unpublish\n * @memberOf Session\n */\n\n\n this.unpublish = function (publisher) {\n if (!publisher) {\n _logging.error('OT.Session.unpublish: publisher parameter missing.');\n\n return;\n } // Unpublish the localMedia publisher\n\n\n publisher._.unpublishFromSession(this, 'unpublished');\n };\n /**\n * Subscribes to a stream that is available to the session. You can get an array of\n * available streams from the streams
property of the sessionConnected
\n * and streamCreated
events (see\n * SessionConnectEvent and\n * StreamEvent).\n * \n * \n * The subscribed stream is displayed on the local web page by replacing the specified element\n * in the DOM with a streaming video display. If the width and height of the display do not\n * match the 4:3 aspect ratio of the video signal, the video stream is cropped to fit\n * the display. If the stream lacks a video component, a blank screen with an audio indicator\n * is displayed in place of the video stream.\n *
\n *\n * \n * The application throws an error if the session is not connected or if the\n * targetElement
does not exist in the HTML DOM.\n *
\n *\n * Example
\n *\n * The following code subscribes to other clients' streams:\n *\n * \n * var apiKey = \"\"; // Replace with your API key. See https://tokbox.com/account\n * var sessionID = \"\"; // Replace with your own session ID.\n * // See https://tokbox.com/developer/guides/create-session/.\n * var token = \"\"; // Replace with a generated token.\n * // See https://tokbox.com/developer/guides/create-token/.\n *\n * var session = OT.initSession(apiKey, sessionID);\n * session.on(\"streamCreated\", function(event) {\n * subscriber = session.subscribe(event.stream, targetElement);\n * });\n * session.connect(token);\n *
\n *\n * @param {Stream} stream The Stream object representing the stream to which we are trying to\n * subscribe.\n *\n * @param {Object} targetElement (Optional) The DOM element or the id
attribute of\n * the existing DOM element used to determine the location of the Subscriber video in the HTML\n * DOM. See the insertMode
property of the properties
parameter. If\n * you do not specify a targetElement
, the application appends a new DOM element\n * to the HTML body
.\n *\n * @param {Object} properties This is an object that contains the following properties:\n * \n * audioVolume
(Number) The desired audio volume, between 0 and\n * 100, when the Subscriber is first opened (default: 50). After you subscribe to the\n * stream, you can adjust the volume by calling the\n * setAudioVolume()
method of the\n * Subscriber object. This volume setting affects local playback only; it does not affect\n * the stream's volume on other clients. \n *\n * - \n *
fitMode
(String) Determines how the video is displayed if the its\n * dimensions do not match those of the DOM element. You can set this property to one of\n * the following values:\n * \n *
\n * - \n *
\"cover\"
— The video is cropped if its dimensions do not match\n * those of the DOM element. This is the default setting for videos that have a\n * camera as the source (for Stream objects with the videoType
property\n * set to \"camera\"
).\n * \n * - \n *
\"contain\"
— The video is letterboxed if its dimensions do not\n * match those of the DOM element. This is the default setting for screen-sharing\n * videos (for Stream objects with the videoType
property set to\n * \"screen\"
).\n * \n *
\n * \n *\n * - \n *
height
(Number or String) The desired initial height of the displayed\n * video in the HTML page (default: 198 pixels). You can specify the number of pixels as\n * either a number (such as 300) or a string ending in \"px\" (such as \"300px\"). Or you can\n * specify a percentage of the size of the parent element, with a string ending in \"%\"\n * (such as \"100%\"). Note: To resize the video, adjust the CSS of the subscriber's\n * DOM element (the element
property of the Subscriber object) or (if the\n * height is specified as a percentage) its parent DOM element (see\n * \n * Resizing or repositioning a video).\n * \n * - \n * insertDefaultUI (Boolean) Whether to use the default OpenTok UI\n * (
true
, the default) or not (false
). The default UI element\n * contains user interface controls, a video loading indicator, and automatic video cropping\n * or letterboxing, in addition to the video. (If you leave insertDefaultUI
\n * set to true
, you can control individual UI settings using the\n * fitMode
, showControls
, and style
options.)\n * \n * If you set this option to false
, OpenTok.js does not insert a default UI\n * element in the HTML DOM, and the element
property of the Subscriber object is\n * undefined. The Subscriber object dispatches a\n * videoElementCreated event when\n * the video
element (or in Internet Explorer the object
element\n * containing the video) is created. The element
property of the event object\n * is a reference to the Subscriber's video
(or object
) element.\n * Add it to the HTML DOM to display the video.\n *
\n * Set this option to false
if you want to move the Publisher's\n * video
element (or its object
element in Internet Explorer) in\n * the HTML DOM.\n *
\n * If you set this to false
, do not set the targetElement
\n * parameter. (This results in an error passed into to the OT.initPublisher()
\n * callback function.) To add the video to the HTML DOM, add an event listener for the\n * videoElementCreated
event, and then add the element
property of\n * the event object into the HTML DOM.\n *
\n * - \n *
insertMode
(String) Specifies how the Subscriber object will\n * be inserted in the HTML DOM. See the targetElement
parameter. This\n * string can have the following values:\n * \n *
\n * \"replace\"
The Subscriber object replaces contents of the\n * targetElement. This is the default. \n * \"after\"
The Subscriber object is a new element inserted\n * after the targetElement in the HTML DOM. (Both the Subscriber and targetElement\n * have the same parent element.) \n * \"before\"
The Subscriber object is a new element inserted\n * before the targetElement in the HTML DOM. (Both the Subscriber and targetElement\n * have the same parent element.) \n * \"append\"
The Subscriber object is a new element added as a\n * child of the targetElement. If there are other child elements, the Subscriber is\n * appended as the last child element of the targetElement. \n *
\n * - \n *
preferredFrameRate
(Number) The preferred frame rate of the subscriber's\n * video. Lowering the preferred frame rate lowers video quality on the subscribing client,\n * but it also reduces network and CPU usage. You may want to use a lower frame rate for\n * subscribers to a stream that is less important than other streams.\n * \n * This property only applies when subscribing to a stream that uses the\n * \n * scalable video feature. Scalable video is available:\n *
\n * - \n * Only in sessions that use the OpenTok Media Router (sessions with the\n * media\n * mode set to routed).\n *
\n * - \n * Only for streams published by clients that support scalable video:\n * clients that use the OpenTok iOS SDK (on certain devices), the OpenTok\n * Android SDK (on certain devices), or OpenTok.js in Chrome and Safari.\n *
\n *
\n * \n * In streams that do not use scalable video, setting this property has no effect.\n *
\n * Note: The frame rate for scalable video streams automatically adjusts for each\n * subscriber, based on network conditions and CPU usage, even if you do not call this method.\n * Call this method if you want to set a maximum frame rate for this subscriber.\n *
\n *
\n * Not every frame rate is available to a subscriber. When you set the preferred frame rate for\n * the subscriber, OpenTok.js picks the best frame rate available that matches your setting.\n * The frame rates available are based on the value of the Subscriber object's\n * stream.frameRate
property, which represents the maximum value available for the\n * stream. The actual frame rates available depend, dynamically, on network and CPU resources\n * available to the publisher and subscriber.\n *
\n * You can dynamically change the preferred frame rate used by calling the\n * setPreferredFrameRate()
method of the Subscriber object.\n *
\n * - \n *
preferredResolution
(Object) The preferred resolution of the subscriber's\n * video. Set this to an object with two properties: width
and height
\n * (both numbers), such as {width: 320, height: 240}
. Lowering the preferred video\n * resolution lowers video quality on the subscribing client, but it also reduces network and CPU\n * usage. You may want to use a lower resolution based on the dimensions of subscriber's video on\n * the web page. You may want to use a resolution for subscribers to a stream that is less\n * important (and smaller) than other streams.\n * \n * This property only applies when subscribing to a stream that uses the\n * \n * scalable video feature. Scalable video is available:\n *
\n * - \n * Only in sessions that use the OpenTok Media Router (sessions with the\n * media\n * mode set to routed).\n *
\n * - \n * Only for streams published by clients that support scalable video:\n * clients that use the OpenTok iOS SDK (on certain devices), the OpenTok\n * Android SDK (on certain devices), or OpenTok.js in Chrome and Safari.\n *
\n *
\n * \n * In streams that do not use scalable video, setting this property has no effect.\n *
\n * Not every resolution is available to a subscriber. When you set the preferred resolution,\n * OpenTok.js and the video encoder pick the best resolution available that matches your\n * setting. The resolutions available depend on the resolution of the published stream.\n * The Subscriber object's stream.resolution
property represents the highest\n * resolution available for the stream. Each of the resolutions available for a stream will use\n * the same aspect ratio. The actual resolutions available depend, dynamically, on network\n * and CPU resources available to the publisher and subscriber.\n *
\n * You can dynamically change the preferred video resolution used by calling the\n * setPreferredResolution()
method of the Subscriber object.\n *
\n * - \n *
showControls
(Boolean) Whether to display the built-in user interface\n * controls for the Subscriber (default: true
). These controls include the name\n * display, the audio level indicator, the speaker control button, the video disabled indicator,\n * and the video disabled warning icon. You can turn off all user interface controls by setting\n * this property to false
. You can control the display of individual user interface\n * controls by leaving this property set to true
(the default) and setting\n * individual properties of the style
property.\n * \n * - \n *
style
(Object) An object containing properties that define the initial\n * appearance of user interface controls of the Subscriber. The style
object\n * includes the following properties:\n * \n * audioBlockedDisplayMode
(String) — Whether to display\n * the default audio blocked icon in Subscribers (in browsers where audio\n * autoplay is blocked). Possible values are: \"auto\"
(the default,\n * icon is displayed when the audio is disabled) and \"off\"
(the icon\n * is not displayed). Set this to \"off\"
if you want to display\n * your own UI element showing that the audio is blocked. In response to an\n * HTML element dispatching a click
event, you can call the\n * OT.unblockAudio() method to start audio\n * playback in this and all other blocked subscribers. \n *\n * audioLevelDisplayMode
(String) — How to display the audio level\n * indicator. Possible values are: \"auto\"
(the indicator is displayed when the\n * video is disabled), \"off\"
(the indicator is not displayed), and\n * \"on\"
(the indicator is always displayed). \n *\n * backgroundImageURI
(String) — A URI for an image to display as\n * the background image when a video is not displayed. (A video may not be displayed if\n * you call subscribeToVideo(false)
on the Subscriber object). You can pass an\n * http or https URI to a PNG, JPEG, or non-animated GIF file location. You can also use the\n * data
URI scheme (instead of http or https) and pass in base-64-encrypted\n * PNG data, such as that obtained from the\n * Subscriber.getImgData() method. (For example,\n * you could set the property to a value returned by calling getImgData()
on\n * a previous Subscriber object.) If the URL or the image data is invalid, the\n * property is ignored (the attempt to set the image fails silently). \n *\n * buttonDisplayMode
(String) — How to display the speaker controls\n * Possible values are: \"auto\"
(controls are displayed when the stream is first\n * displayed and when the user mouses over the display), \"off\"
(controls are not\n * displayed), and \"on\"
(controls are always displayed). \n *\n * nameDisplayMode
(String) Whether to display the stream name.\n * Possible values are: \"auto\"
(the name is displayed when the stream is first\n * displayed and when the user mouses over the display), \"off\"
(the name is not\n * displayed), and \"on\"
(the name is always displayed). \n *\n * videoDisabledDisplayMode
(String) Whether to display the video\n * disabled indicator and video disabled warning icons for a Subscriber. These icons\n * indicate that the video has been disabled (or is in risk of being disabled for\n * the warning icon) due to poor stream quality. This style only applies to the Subscriber\n * object. Possible values are: \"auto\"
(the icons are automatically when the\n * displayed video is disabled or in risk of being disabled due to poor stream quality),\n * \"off\"
(do not display the icons), and \"on\"
(display the\n * icons). The default setting is \"auto\"
\n *
\n * \n *\n * subscribeToAudio
(Boolean) Whether to initially subscribe to audio\n * (if available) for the stream (default: true
). \n *\n * subscribeToVideo
(Boolean) Whether to initially subscribe to video\n * (if available) for the stream (default: true
). \n *\n * testNetwork
(Boolean) Whether, when subscribing to a stream\n * published by the local client, you want to have the stream come from the OpenTok Media\n * Router (true
) or if you want the DOM to simply to display the local camera's\n * video (false
). Set this to true
when you want to use the\n * Subscriber.getStats() method to check statistics\n * for a stream you publish. This setting only applies to streams published by the local\n * client in a session that uses the OpenTok Media Router (sessions with the\n * media mode\n * set to routed), not in sessions with the media mode set to relayed. The default value is\n * false
. \n *\n * - \n *
width
(Number or String) The desired initial width of the displayed\n * video in the HTML page (default: 264 pixels). You can specify the number of pixels as\n * either a number (such as 400) or a string ending in \"px\" (such as \"400px\"). Or you can\n * specify a percentage of the size of the parent element, with a string ending in \"%\"\n * (such as \"100%\"). Note: To resize the video, adjust the CSS of the subscriber's\n * DOM element (the element
property of the Subscriber object) or (if the\n * width is specified as a percentage) its parent DOM element (see\n * \n * Resizing or repositioning a video).\n * \n *\n *
\n *\n * @param {Function} completionHandler (Optional) A function to be called when the call to the\n * subscribe()
method succeeds or fails. This function takes one parameter —\n * error
. On success, the completionHandler
function is not passed any\n * arguments. On error, the function is passed an error
object, defined by the\n * Error class, has two properties: code
(an integer) and\n * message
(a string), which identify the cause of the failure. The following\n * code adds a completionHandler
when calling the subscribe()
method:\n * \n * session.subscribe(stream, \"subscriber\", null, function (error) {\n * if (error) {\n * console.log(error.message);\n * } else {\n * console.log(\"Subscribed to stream: \" + stream.id);\n * }\n * });\n *
\n *\n * @signature subscribe(stream, targetElement, properties, completionHandler)\n * @returns {Subscriber} The Subscriber object for this stream. Stream control functions\n * are exposed through the Subscriber object.\n * @method #subscribe\n * @memberOf Session\n */\n\n\n this.subscribe = function (stream, targetElement, properties, completionHandler) {\n if (typeof targetElement === 'function') {\n completionHandler = targetElement;\n targetElement = undefined;\n properties = undefined;\n }\n\n if (typeof properties === 'function') {\n completionHandler = properties;\n properties = undefined;\n }\n\n completionHandler = completionHandler || function () {};\n\n if (!this.connection || !this.connection.connectionId) {\n dispatchOTError(otError(errors.NOT_CONNECTED, new Error('Session.subscribe :: Connection required to subscribe'), ExceptionCodes.UNABLE_TO_SUBSCRIBE), completionHandler);\n return undefined;\n }\n\n if (!stream) {\n dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('Session.subscribe :: stream cannot be null'), ExceptionCodes.UNABLE_TO_SUBSCRIBE), completionHandler);\n return undefined;\n }\n\n if (!Object.prototype.hasOwnProperty.call(stream, 'streamId')) {\n dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('Session.subscribe :: invalid stream object'), ExceptionCodes.UNABLE_TO_SUBSCRIBE), completionHandler);\n return undefined;\n }\n\n if (properties && properties.insertDefaultUI === false && targetElement) {\n dispatchOTError(otError(errors.INVALID_PARAMETER, new Error('You cannot specify a target element if insertDefaultUI is false'), ExceptionCodes.INVALID_PARAMETER), completionHandler);\n return undefined;\n }\n\n if (targetElement && targetElement.insertDefaultUI === false) {\n // You can omit the targetElement property if you set insertDefaultUI to false\n properties = targetElement;\n targetElement = undefined;\n }\n\n const subscriber = new Subscriber(targetElement, assign(properties || {}, {\n stream,\n session: this,\n // @todo this needs to go.\n analytics\n }), err => {\n if (err) {\n dispatchOTError(err, completionHandler);\n return;\n }\n\n completionHandler(null, subscriber);\n });\n sessionObjects.subscribers.add(subscriber);\n return subscriber;\n };\n /**\n * Stops subscribing to a stream in the session. the display of the audio-video stream is\n * removed from the local web page.\n *\n * Example
\n * \n * The following code subscribes to other clients' streams. For each stream, the code also\n * adds an Unsubscribe link.\n *
\n * \n * var apiKey = \"\"; // Replace with your API key. See See https://tokbox.com/account\n * var sessionID = \"\"; // Replace with your own session ID.\n * // See https://tokbox.com/developer/guides/create-session/.\n * var token = \"\"; // Replace with a generated token.\n * // See https://tokbox.com/developer/guides/create-token/.\n * var streams = [];\n *\n * var session = OT.initSession(apiKey, sessionID);\n * session.on(\"streamCreated\", function(event) {\n * var stream = event.stream;\n * displayStream(stream);\n * });\n * session.connect(token);\n *\n * function displayStream(stream) {\n * var div = document.createElement('div');\n * div.setAttribute('id', 'stream' + stream.streamId);\n *\n * var subscriber = session.subscribe(stream, div);\n * subscribers.push(subscriber);\n *\n * var aLink = document.createElement('a');\n * aLink.setAttribute('href', 'javascript: unsubscribe(\"' + subscriber.id + '\")');\n * aLink.innerHTML = \"Unsubscribe\";\n *\n * var streamsContainer = document.getElementById('streamsContainer');\n * streamsContainer.appendChild(div);\n * streamsContainer.appendChild(aLink);\n *\n * streams = event.streams;\n * }\n *\n * function unsubscribe(subscriberId) {\n * console.log(\"unsubscribe called\");\n * for (var i = 0; i < subscribers.length; i++) {\n * var subscriber = subscribers[i];\n * if (subscriber.id == subscriberId) {\n * session.unsubscribe(subscriber);\n * }\n * }\n * }\n *
\n *\n * @param {Subscriber} subscriber The Subscriber object to unsubcribe.\n *\n * @see subscribe()\n *\n * @method #unsubscribe\n * @memberOf Session\n */\n\n\n this.unsubscribe = function (subscriber) {\n if (!subscriber) {\n const errorMsg = 'OT.Session.unsubscribe: subscriber cannot be null';\n\n _logging.error(errorMsg);\n\n throw new Error(errorMsg);\n }\n\n if (!subscriber.stream) {\n _logging.warn('OT.Session.unsubscribe:: tried to unsubscribe a subscriber that had no stream');\n\n return false;\n }\n\n _logging.debug(`OT.Session.unsubscribe: subscriber ${subscriber.id}`);\n\n subscriber._destroy({\n reason: 'Unsubscribe'\n });\n\n return true;\n };\n /**\n * Returns an array of local Subscriber objects for a given stream.\n *\n * @param {Stream} stream The stream for which you want to find subscribers.\n *\n * @returns {Array} An array of {@link Subscriber} objects for the specified stream.\n *\n * @see unsubscribe()\n * @see Subscriber\n * @see StreamEvent\n * @method #getSubscribersForStream\n * @memberOf Session\n */\n\n\n this.getSubscribersForStream = function (stream) {\n return sessionObjects.subscribers.where({\n streamId: stream.id\n });\n };\n /**\n * Returns the local Publisher object for a given stream.\n *\n * @param { Stream } stream The stream for which you want to find the Publisher.\n *\n * @returns { Publisher } A Publisher object for the specified stream. Returns\n * null
if there is no local Publisher object\n * for the specified stream.\n *\n * @see forceUnpublish()\n * @see Subscriber\n * @see StreamEvent\n *\n * @method #getPublisherForStream\n * @memberOf Session\n */\n\n\n this.getPublisherForStream = function (stream) {\n let streamId;\n let errorMsg;\n\n if (typeof stream === 'string') {\n streamId = stream;\n } else if (typeof stream === 'object' && stream && Object.hasOwnProperty.call(stream, 'id')) {\n streamId = stream.id;\n } else {\n errorMsg = 'Session.getPublisherForStream :: Invalid stream type';\n\n _logging.error(errorMsg);\n\n throw new Error(errorMsg);\n }\n\n return sessionObjects.publishers.where({\n streamId\n })[0];\n }; // Private Session API: for internal OT use only\n\n\n this._ = {\n getProxyUrl() {\n return proxyUrl;\n },\n\n isSocketReconnecting() {\n return _isSocketReconnecting;\n },\n\n getSocket() {\n return _socket;\n },\n\n reconnecting: function () {\n _isSocketReconnecting = true;\n this.dispatchEvent(new Events.SessionReconnectingEvent());\n }.bind(this),\n reconnected: function () {\n _isSocketReconnecting = false;\n this.dispatchEvent(new Events.SessionReconnectedEvent());\n\n if (this.sessionInfo.reconnection) {\n sessionObjects.publishers.where({\n session: this\n }).forEach(publisher => {\n publisher._.iceRestart();\n });\n\n if (!this.sessionInfo.p2pEnabled) {\n sessionObjects.subscribers.where({\n session: this\n }).forEach(subscriber => {\n subscriber._.iceRestart('socket reconnected');\n });\n }\n }\n }.bind(this),\n // session.on(\"signal\", function(SignalEvent))\n // session.on(\"signal:{type}\", function(SignalEvent))\n dispatchSignal: function (fromConnection, type, data) {\n const event = new Events.SignalEvent(type, data, fromConnection);\n event.target = this; // signal a \"signal\" event\n // NOTE: trigger doesn't support defaultAction, and therefore preventDefault.\n\n this.trigger(eventNames.SIGNAL, event); // signal an \"signal:{type}\" event\" if there was a custom type\n\n if (type) {\n this.dispatchEvent(event);\n }\n }.bind(this),\n\n subscriberChannelUpdate(stream, subscriber, channel, attributes) {\n if (!_socket) {\n _logging.warn('You are disconnected, cannot update subscriber properties ', attributes);\n\n return null;\n }\n\n return _socket.subscriberChannelUpdate(stream.id, subscriber.widgetId, channel.id, attributes);\n },\n\n streamCreate(name, streamId, audioFallbackEnabled, channels, minBitrate, sourceStreamId, completion) {\n if (!_socket) {\n _logging.warn('You are disconnected, cannot create stream ', streamId);\n\n return;\n }\n\n _socket.streamCreate(name, streamId, audioFallbackEnabled, channels, minBitrate, undefined, // Do not expose maxBitrate to the end user,\n sourceStreamId, completion);\n },\n\n streamDestroy(streamId, sourceStreamId) {\n if (!_socket) {\n _logging.warn('You are disconnected, cannot destroy stream ', streamId);\n\n return;\n }\n\n _socket.streamDestroy(streamId, sourceStreamId);\n },\n\n streamChannelUpdate(stream, channel, attributes) {\n if (!_socket) {\n _logging.warn('You are disconnected, cannot update stream properties ', attributes);\n\n return;\n }\n\n _socket.streamChannelUpdate(stream.id, channel.id, attributes);\n },\n\n // allow these variables to be overridden in unit tests\n // it's dirty, but I figure it can be cleaned up when we implement proper DI for our unit tests\n setSocket(newSocket) {\n _socket = newSocket;\n },\n\n setLogging(newLogging) {\n _logging = newLogging;\n },\n\n setState,\n\n setIceServers(iceServers) {\n if (!iceServers) {\n return;\n }\n\n _iceServerDetails = {\n iceServers: adaptIceServers(iceServers),\n timestamp: Date.now()\n };\n },\n\n getOtIceServerInfo() {\n const timeElapsed = !_iceServerDetails ? Infinity : Date.now() - _iceServerDetails.timestamp;\n const validDuration = 24 * 60 * 60 * 1000; // 24 hours\n\n const validTimeRemaining = validDuration - timeElapsed;\n const fiveMinutes = 5 * 60 * 1000;\n\n if (validTimeRemaining > fiveMinutes) {\n return Promise.resolve({\n transportPolicy: _session && _session.sessionInfo && _session.sessionInfo.clientCandidates,\n servers: _iceServerDetails && _iceServerDetails.iceServers\n });\n }\n\n if (!_token) {\n // @todo why would this happen before connect() where a token is set?\n // Need a token for getting ice servers from gsi\n return Promise.resolve({\n transportPolicy: _session && _session.sessionInfo && _session.sessionInfo.clientCandidates,\n servers: [],\n needRumorIceServersFallback: true\n });\n }\n\n const clientVersion = localStaticConfig.clientVersion;\n return getSessionInfo({\n anvilUrl: (this.staticConfig || localStaticConfig).apiUrl,\n sessionId,\n token: _token,\n connectionId: _connectionId,\n clientVersion\n }).then(sessionInfo => {\n _session._.setIceServers(sessionInfo.iceServers);\n\n if (!_iceServerDetails) {\n // No ice servers provided by gsi\n return {\n transportPolicy: _session.sessionInfo.clientCandidates,\n servers: [],\n needRumorIceServersFallback: true\n };\n }\n\n return {\n transportPolicy: _session && _session.sessionInfo && _session.sessionInfo.clientCandidates,\n servers: _iceServerDetails && _iceServerDetails.iceServers\n };\n });\n },\n\n getCodecFlags: () => ({\n h264: _session.sessionInfo.h264,\n vp9: _session.sessionInfo.vp9,\n vp8: _session.sessionInfo.vp8\n }),\n getVideoCodecsCompatible: webRTCStream => testRemovingVideoCodecs({\n RTCPeerConnection: windowMock.RTCPeerConnection,\n env: OTHelpers.env,\n stream: webRTCStream,\n codecFlags: _session._.getCodecFlags()\n }),\n getIceConfig: () => {\n if (!iceConfig) {\n return _session._.getOtIceServerInfo();\n }\n\n const transportPolicy = (() => {\n if (iceConfig && iceConfig.transportPolicy === 'relay') {\n return 'relay';\n }\n\n return _session.sessionInfo.clientCandidates;\n })();\n\n const otIceServersInfoPromise = iceConfig.includeServers === 'custom' ? Promise.resolve({\n servers: []\n }) : _session._.getOtIceServerInfo();\n return otIceServersInfoPromise.then(otIceServerInfo => assign(otIceServerInfo, {\n transportPolicy,\n servers: [...otIceServerInfo.servers, ...iceConfig.customServers]\n }));\n },\n forceMute: function () {\n this.dispatchEvent(new Events.MuteForcedEvent());\n }.bind(this),\n enableMuteOnEntry: () => {\n _muteOnEntry = true;\n },\n disableMuteOnEntry: () => {\n _muteOnEntry = false;\n },\n privateEvents: new EventEmitter()\n };\n /**\n * Sends a signal to each client or a specified client in the session. Specify a\n * to
property of the signal
parameter to limit the signal to\n * be sent to a specific client; otherwise the signal is sent to each client connected to\n * the session.\n * \n * The following example sends a signal of type \"foo\" with a specified data payload (\"hello\")\n * to all clients connected to the session:\n *
\n * session.signal({\n * type: \"foo\",\n * data: \"hello\"\n * },\n * function(error) {\n * if (error) {\n * console.log(\"signal error: \" + error.message);\n * } else {\n * console.log(\"signal sent\");\n * }\n * }\n * );\n *
\n * \n * Calling this method without specifying a recipient client (by setting the to
\n * property of the signal
parameter) results in multiple signals sent (one to each\n * client in the session). For information on charges for signaling, see the\n * OpenTok pricing page.\n *
\n * The following example sends a signal of type \"foo\" with a data payload (\"hello\") to a\n * specific client connected to the session:\n *
\n * session.signal({\n * type: \"foo\",\n * to: recipientConnection; // a Connection object\n * data: \"hello\"\n * },\n * function(error) {\n * if (error) {\n * console.log(\"signal error: \" + error.message);\n * } else {\n * console.log(\"signal sent\");\n * }\n * }\n * );\n *
\n * \n * Add an event handler for the signal
event to listen for all signals sent in\n * the session. Add an event handler for the signal:type
event to listen for\n * signals of a specified type only (replace type
, in signal:type
,\n * with the type of signal to listen for). The Session object dispatches these events. (See\n * events.)\n *\n * @param {Object} signal An object that contains the following properties defining the signal:\n *
\n * data
— (String) The data to send. The limit to the length of data\n * string is 8kB. Do not set the data string to null
or\n * undefined
. \n * retryAfterReconnect
— (Boolean) Upon reconnecting to the session,\n * whether to send any signals that were initiated while disconnected. If your client loses its\n * connection to the OpenTok session, due to a drop in network connectivity, the client\n * attempts to reconnect to the session, and the Session object dispatches a\n * reconnecting
event. By default, signals initiated while disconnected are\n * sent when (and if) the client reconnects to the OpenTok session. You can prevent this by\n * setting the retryAfterReconnect
property to false
. (The default\n * value is true
.)\n * to
— (Connection) A Connection\n * object corresponding to the client that the message is to be sent to. If you do not\n * specify this property, the signal is sent to all clients connected to the session. \n * type
— (String) The type of the signal. You can use the type to\n * filter signals when setting an event handler for the signal:type
event\n * (where you replace type
with the type string). The maximum length of the\n * type
string is 128 characters, and it must contain only letters (A-Z and a-z),\n * numbers (0-9), '-', '_', and '~'. \n * \n *
\n *\n * Each property is optional. If you set none of the properties, you will send a signal\n * with no data or type to each client connected to the session.
\n *\n * @param {Function} completionHandler (Optional) A function that is called when sending the signal\n * succeeds or fails. This function takes one parameter — error
.\n * On success, the completionHandler
function is not passed any\n * arguments. On error, the function is passed an error
object, defined by the\n * Error class. The error
object has the following\n * properties:\n *\n * \n * code
— (Number) An error code, which can be one of the following:\n * \n * \n * 400 | One of the signal properties is invalid. | \n *
\n * \n * 404 | The client specified by the to property is not connected\n * to the session. | \n *
\n * \n * 413 | The type string exceeds the maximum length (128 bytes),\n * or the data string exceeds the maximum size (8 kB). | \n *
\n * \n * 500 | You are not connected to the OpenTok session. | \n *
\n *
\n * \n * message
— (String) A description of the error. \n *
\n *\n * Note that the completionHandler
success result (error == null
)\n * indicates that the options passed into the Session.signal()
method are valid\n * and the signal was sent. It does not indicate that the signal was successfully\n * received by any of the intended recipients.\n *\n * @method #signal\n * @memberOf Session\n * @see signal and signal:type events\n */\n\n this.signal = function (options, completion) {\n let _options = options;\n\n let _completion = completion || function () {};\n\n if (isFunction(_options)) {\n _completion = _options;\n _options = null;\n }\n\n if (this.isNot('connected')) {\n const notConnectedErrorMsg = 'Unable to send signal - you are not connected to the session.';\n dispatchOTError(otError(errors.NOT_CONNECTED, new Error(notConnectedErrorMsg), 500), _completion);\n return;\n }\n\n function getErrorNameFromCode(code) {\n switch (code) {\n case 400:\n case 413:\n return errors.INVALID_PARAMETER;\n\n case 429:\n return errors.RATE_LIMIT_EXCEEDED;\n\n case 404:\n return errors.NOT_FOUND;\n\n case 500:\n return errors.NOT_CONNECTED;\n\n case 403:\n return errors.PERMISSION_DENIED;\n\n case 2001:\n return errors.UNEXPECTED_SERVER_RESPONSE;\n\n default:\n return undefined;\n }\n }\n\n _socket.signal(_options, function (error) {\n if (error) {\n const errorName = getErrorNameFromCode(error.code);\n\n if (errorName) {\n error = otError(errorName, new Error(error.message), error.code);\n }\n\n _completion(error);\n\n return;\n }\n\n for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {\n args[_key4 - 1] = arguments[_key4];\n }\n\n _completion(error, ...args);\n }, this.logEvent);\n\n if (options && options.data && typeof options.data !== 'string') {\n _logging.warn('Signaling of anything other than Strings is deprecated. ' + 'Please update the data property to be a string.');\n }\n };\n /**\n * Forces a remote connection to leave the session.\n *\n *
\n * The forceDisconnect()
method is normally used as a moderation tool\n * to remove users from an ongoing session.\n *
\n * \n * When a connection is terminated using the forceDisconnect()
,\n * sessionDisconnected
, connectionDestroyed
and\n * streamDestroyed
events are dispatched in the same way as they\n * would be if the connection had terminated itself using the disconnect()
\n * method. However, the reason
property of a {@link ConnectionEvent} or\n * {@link StreamEvent} object specifies \"forceDisconnected\"
as the reason\n * for the destruction of the connection and stream(s).\n *
\n * \n * While you can use the forceDisconnect()
method to terminate your own connection,\n * calling the disconnect()
method is simpler.\n *
\n * \n * The OT object dispatches an exception
event if the user's role\n * does not include permissions required to force other users to disconnect.\n * You define a user's role when you create the user token (see the\n * Token creation overview).\n * See ExceptionEvent and OT.on().\n *
\n * \n * The application throws an error if the session is not connected.\n *
\n *\n * Events dispatched:
\n *\n * \n * connectionDestroyed
(ConnectionEvent) \n * On clients other than which had the connection terminated.\n *
\n * \n * exception
(ExceptionEvent) \n * The user's role does not allow forcing other user's to disconnect (event.code =\n * 1530
),\n * or the specified stream is not publishing to the session (event.code = 1535
).\n *
\n * \n * sessionDisconnected
\n * (SessionDisconnectEvent) \n * On the client which has the connection terminated.\n *
\n * \n * streamDestroyed
(StreamEvent) \n * If streams are stopped as a result of the connection ending.\n *
\n *\n * @param {Connection} connection The connection to be disconnected from the session.\n * This value can either be a Connection object or a connection\n * ID (which can be obtained from the connectionId
property of the Connection object).\n *\n * @param {Function} completionHandler (Optional) A function to be called when the call to the\n * forceDiscononnect()
method succeeds or fails. This function takes one parameter\n * — error
. On success, the completionHandler
function is\n * not passed any arguments. On error, the function is passed an error
object\n * parameter. The error
object, defined by the Error\n * class, has two properties: code
(an integer)\n * and message
(a string), which identify the cause of the failure.\n * Calling forceDisconnect()
fails if the role assigned to your\n * token is not \"moderator\"; in this case the error.name
property is set to\n * \"OT_PERMISSION_DENIED\"
. The following code adds a completionHandler
\n * when calling the forceDisconnect()
method:\n * \n * session.forceDisconnect(connection, function (error) {\n * if (error) {\n * console.log(error);\n * } else {\n * console.log(\"Connection forced to disconnect: \" + connection.id);\n * }\n * });\n *
\n *\n * @method #forceDisconnect\n * @memberOf Session\n */\n\n\n this.forceDisconnect = function (connectionOrConnectionId, completionHandler) {\n if (this.isNot('connected')) {\n const notConnectedErrorMsg = 'Cannot call forceDisconnect(). You are not ' + 'connected to the session.';\n dispatchOTError(otError(errors.NOT_CONNECTED, new Error(notConnectedErrorMsg), ExceptionCodes.NOT_CONNECTED), completionHandler);\n return;\n }\n\n const connectionId = typeof connectionOrConnectionId === 'string' ? connectionOrConnectionId : connectionOrConnectionId.id;\n const invalidParameterErrorMsg = 'Invalid Parameter. Check that you have passed valid parameter values into the method call.';\n\n if (!connectionId) {\n dispatchOTError(otError(errors.INVALID_PARAMETER, new Error(invalidParameterErrorMsg), ExceptionCodes.INVALID_PARAMETER), completionHandler);\n return;\n }\n\n const notPermittedErrorMsg = 'This token does not allow forceDisconnect. ' + 'The role must be at least `moderator` to enable this functionality';\n\n if (!permittedTo('forceDisconnect')) {\n dispatchOTError(otError(errors.PERMISSION_DENIED, new Error(notPermittedErrorMsg), ExceptionCodes.UNABLE_TO_FORCE_DISCONNECT), completionHandler);\n return;\n }\n\n _socket.forceDisconnect(connectionId, function (err) {\n if (err) {\n dispatchOTError(otError(errors.INVALID_PARAMETER, new Error(invalidParameterErrorMsg), ExceptionCodes.INVALID_PARAMETER), completionHandler);\n } else if (completionHandler && isFunction(completionHandler)) {\n for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {\n args[_key5 - 1] = arguments[_key5];\n }\n\n completionHandler(err, ...args);\n }\n });\n };\n /**\n * Forces the publisher of the specified stream to stop publishing the stream.\n *\n * \n * Calling this method causes the Session object to dispatch a streamDestroyed
\n * event on all clients that are subscribed to the stream (including the client that is\n * publishing the stream). The reason
property of the StreamEvent object is\n * set to \"forceUnpublished\"
.\n *
\n * \n * The OT object dispatches an exception
event if the user's role\n * does not include permissions required to force other users to unpublish.\n * You define a user's role when you create the user token (see the\n * Token creation overview).\n * You pass the token string as a parameter of the connect()
method of the Session\n * object. See ExceptionEvent and\n * OT.on().\n *
\n *\n * Events dispatched:
\n *\n * \n * exception
(ExceptionEvent) \n * The user's role does not allow forcing other users to unpublish.\n *
\n * \n * streamDestroyed
(StreamEvent) \n * The stream has been unpublished. The Session object dispatches this on all clients\n * subscribed to the stream, as well as on the publisher's client.\n *
\n *\n * @param {Stream} stream The stream to be unpublished.\n *\n * @param {Function} completionHandler (Optional) A function to be called when the call to the\n * forceUnpublish()
method succeeds or fails. This function takes one parameter\n * — error
. On success, the completionHandler
function is\n * not passed any arguments. On error, the function is passed an error
object\n * parameter. The error
object, defined by the Error\n * class, has two properties: code
(an integer)\n * and message
(a string), which identify the cause of the failure. Calling\n * forceUnpublish()
fails if the role assigned to your token is not \"moderator\";\n * in this case the error.name
property is set to \"OT_PERMISSION_DENIED\"
.\n * The following code adds a completion handler when calling the forceUnpublish()
\n * method:\n * \n * session.forceUnpublish(stream, function (error) {\n * if (error) {\n * console.log(error);\n * } else {\n * console.log(\"Connection forced to disconnect: \" + connection.id);\n * }\n * });\n *
\n *\n * @method #forceUnpublish\n * @memberOf Session\n */\n\n\n this.forceUnpublish = function (streamOrStreamId, completionHandler) {\n if (completionHandler === void 0) {\n completionHandler = () => {};\n }\n\n const dispatchError = err => dispatchOTError(otError(err.name, new Error(err.msg), err.code), completionHandler);\n\n const invalidParameterError = {\n msg: 'Invalid Parameter. Check that you have passed valid parameter values into the method call.',\n code: ExceptionCodes.INVALID_PARAMETER,\n name: errors.INVALID_PARAMETER\n };\n const notConnectedError = {\n msg: 'Cannot call forceUnpublish(). You are not connected to the session.',\n code: ExceptionCodes.NOT_CONNECTED,\n name: errors.NOT_CONNECTED\n };\n const notPermittedError = {\n msg: 'This token does not allow forceUnpublish. The role must be at least `moderator` to enable this ' + 'functionality',\n code: ExceptionCodes.UNABLE_TO_FORCE_UNPUBLISH,\n name: errors.PERMISSION_DENIED\n };\n const notFoundError = {\n msg: 'The stream does not exist.',\n name: errors.NOT_FOUND\n };\n const unexpectedError = {\n msg: 'An unexpected error occurred.',\n name: errors.UNEXPECTED_SERVER_RESPONSE,\n code: ExceptionCodes.UNEXPECTED_SERVER_RESPONSE\n };\n\n if (!streamOrStreamId) {\n dispatchError(invalidParameterError);\n return;\n }\n\n if (_this.isNot('connected')) {\n dispatchError(notConnectedError);\n return;\n }\n\n const stream = getStream(streamOrStreamId);\n\n if (!permittedTo('forceUnpublish')) {\n // if this throws an error the handleJsException won't occur\n dispatchError(notPermittedError);\n return;\n }\n\n _socket.forceUnpublish(stream.id, err => {\n if (!err) {\n completionHandler(null);\n return;\n }\n\n if (err.code === '404') {\n dispatchError(notFoundError);\n } else if (err.code === '403') {\n dispatchError(notPermittedError);\n } else {\n dispatchError(unexpectedError);\n }\n });\n };\n /**\n * Forces a the publisher of a specified stream to mute its audio.\n *\n * \n * This is a beta feature.\n *\n *
\n * Calling this method causes the Publisher object in the client publishing the\n * stream to dispatch a muteForced
event.\n *
\n *\n * \n * Check the capabilities.canForceMute
property of the Session object to see if\n * you can call this function successfully. This is reserved for clients that have connected\n * with a token that has been assigned the moderator role (see the\n * Token Creation\n * documentation).\n *\n * @param { Stream } stream The stream to be muted.\n *\n * @method #forceMuteStream\n * @memberOf Session\n * @see Session.capabilities\n *\n * @return {Promise} A promise that resolves with no value when the operation\n * completes successfully. The promise is rejected if there is an error. The\n * name
property of the Error object is set to one of the following\n * values, depending the type of error:\n *\n *
\n *
\n *\n * 'OT_NOT_CONNECTED'
— The client is not connect to the session. \n *\n * 'OT_INVALID_PARAMETER'
— if one or more of the passed parameters are invalid. \n *\n * 'OT_PERMISSION_DENIED'
— The user's role does not\n * include permissions required to force other users to mute.\n * You define a user's role when you create the user token (see the\n * Token creation overview).\n * You pass the token string as a parameter of the connect()
method of the Session\n * object.\n * \n *\n * 'OT_NOT_FOUND'
— The stream wasn't found in this session. \n *\n * 'OT_UNEXPECTED_SERVER_RESPONSE'
— in case of an internal server error. \n *\n * \n * \n */\n\n\n this.forceMuteStream = /*#__PURE__*/function () {\n var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(stream, options) {\n var _ref4, _ref4$active, active, dispatchError, invalidParameterError, notConnectedError, notPermittedError, notFoundError, unexpectedError;\n\n return _regenerator.default.wrap(function _callee$(_context) {\n while (1) switch (_context.prev = _context.next) {\n case 0:\n _ref4 = options || {}, _ref4$active = _ref4.active, active = _ref4$active === void 0 ? false : _ref4$active;\n\n dispatchError = err => {\n dispatchOTError(err);\n throw err;\n };\n\n invalidParameterError = otError(errors.INVALID_PARAMETER, new Error('Invalid Parameter. Check that you have passed valid parameter values into the method call.'), ExceptionCodes.INVALID_PARAMETER);\n notConnectedError = otError(errors.NOT_CONNECTED, new Error('Cannot call forceMuteStream(). You are not connected to the session.'), ExceptionCodes.NOT_CONNECTED);\n notPermittedError = otError(errors.PERMISSION_DENIED, new Error('This token does not allow forceMuteStream. The role must be at least `moderator` to enable this ' + 'functionality'), ExceptionCodes.UNABLE_TO_FORCE_MUTE);\n notFoundError = otError(errors.NOT_FOUND, new Error('The stream does not exist.'));\n unexpectedError = otError(errors.UNEXPECTED_SERVER_RESPONSE, new Error('An unexpected error occurred.'), ExceptionCodes.UNEXPECTED_SERVER_RESPONSE);\n\n if (_this.isNot('connected')) {\n dispatchError(notConnectedError);\n }\n\n if (!permittedTo('forceMute')) {\n // if this throws an error the handleJsException won't occur\n dispatchError(notPermittedError);\n }\n\n if (!stream || !stream.id) {\n dispatchError(invalidParameterError);\n }\n\n if (typeof active !== 'boolean') {\n dispatchError(invalidParameterError);\n }\n\n _socket.forceMuteStream(stream.id, active, err => {\n if (!err) {\n return;\n }\n\n if (err.code === '404') {\n dispatchError(notFoundError);\n } else if (err.code === '403') {\n dispatchError(notPermittedError);\n } else {\n dispatchError(unexpectedError);\n }\n });\n\n case 12:\n case \"end\":\n return _context.stop();\n }\n }, _callee);\n }));\n\n return function (_x, _x2) {\n return _ref3.apply(this, arguments);\n };\n }();\n /**\n * Forces all publishers in the session (except for those publishing excluded streams)\n * to mute audio.\n *\n * \n * This is a beta feature.\n *\n *
\n * Calling this method causes the Publisher objects in the clients publishing\n * the streams to dispatch a muteForced
events. The Session object\n * in each client connected to the session dispatches the muteForced
\n * event.\n *
\n *\n * \n * Check the capabilities.canForceMute
property of the Session object to see if\n * you can call this function successfully. This is reserved for clients that have connected\n * with a token that has been assigned the moderator role (see the\n * Token Creation\n * documentation).\n *\n * @param {Array} excludedStreams An array of Stream objects to be excluded from\n * being muted. Note that if you want to prevent the local client's published\n * streams from being muted, pass in the Stream object(s) for those stream(s).\n *\n * @method #forceMuteAll\n * @memberOf Session\n *\n * @return {Promise} A promise that resolves with no value when the operation\n * completes successfully. The promise is rejected if there is an error. The\n * name
property of the Error object is set to one of the following\n * values, depending the type of error:\n *\n *
\n *
\n *\n * 'OT_NOT_CONNECTED'
— The client is not connect to the session. \n *\n * 'INVALID_PARAMETER'
— if one or more of the passed parameters are invalid. \n *\n * 'OT_PERMISSION_DENIED'
— The user's role does not\n * include permissions required to force other users to mute.\n * You define a user's role when you create the user token (see the\n * Token creation overview).\n * You pass the token string as a parameter of the connect()
method of the Session\n * object.\n * \n *\n * 'OT_UNEXPECTED_SERVER_RESPONSE'
— in case of an internal server error. \n * \n * \n */\n\n\n this.forceMuteAll = /*#__PURE__*/function () {\n var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(excludedStreams, options) {\n var _ref6, _ref6$active, active, dispatchError, invalidParameterError, notConnectedError, notPermittedError, notFoundError, unexpectedError, excludedStreamIds;\n\n return _regenerator.default.wrap(function _callee2$(_context2) {\n while (1) switch (_context2.prev = _context2.next) {\n case 0:\n _ref6 = options || {}, _ref6$active = _ref6.active, active = _ref6$active === void 0 ? false : _ref6$active;\n\n dispatchError = err => {\n dispatchOTError(err);\n throw err;\n };\n\n invalidParameterError = otError(errors.INVALID_PARAMETER, new Error('Invalid Parameter. Check that you have passed valid parameter values into the method call.'), ExceptionCodes.INVALID_PARAMETER);\n notConnectedError = otError(errors.NOT_CONNECTED, new Error('Cannot call forceMuteAll(). You are not connected to the session.'), ExceptionCodes.NOT_CONNECTED);\n notPermittedError = otError(errors.PERMISSION_DENIED, new Error('This token does not allow forceMuteAll. The role must be at least `moderator` to enable this ' + 'functionality'), ExceptionCodes.UNABLE_TO_FORCE_MUTE);\n notFoundError = otError(errors.NOT_FOUND, new Error('The stream does not exist.'));\n unexpectedError = otError(errors.UNEXPECTED_SERVER_RESPONSE, new Error('An unexpected error occurred.'), ExceptionCodes.UNEXPECTED_SERVER_RESPONSE);\n\n if (_this.isNot('connected')) {\n dispatchError(notConnectedError);\n }\n\n if (!permittedTo('forceMute')) {\n // if this throws an error the handleJsException won't occur\n dispatchError(notPermittedError);\n } // Invalid parameter error should occur when an array of elements is passed where 1 or more of these elements is not\n // an instanceof OT.Stream. excludedStreams can be undefined, null or an empty array and this just means all streams\n // in the session need to be muted\n\n\n if (Array.isArray(excludedStreams) && !excludedStreams.every(stream => stream instanceof Stream) || excludedStreams && !Array.isArray(excludedStreams)) {\n dispatchError(invalidParameterError);\n }\n\n if (typeof active !== 'boolean') {\n dispatchError(invalidParameterError);\n }\n\n excludedStreamIds = (excludedStreams || []).map(stream => stream.id);\n\n _socket.forceMuteAll(excludedStreamIds, active, err => {\n if (!err) {\n return;\n }\n\n if (err.code === '404') {\n dispatchError(notFoundError);\n } else if (err.code === '403') {\n dispatchError(notPermittedError);\n } else {\n dispatchError(unexpectedError);\n }\n });\n\n case 13:\n case \"end\":\n return _context2.stop();\n }\n }, _callee2);\n }));\n\n return function (_x3, _x4) {\n return _ref5.apply(this, arguments);\n };\n }();\n\n this.isConnected = () => this.is('connected');\n\n this.capabilities = new Capabilities([]);\n };\n /**\n * Dispatched when an archive recording of the session starts.\n *\n * @name archiveStarted\n * @event\n * @memberof Session\n * @see ArchiveEvent\n * @see Archiving overview\n */\n\n /**\n * Dispatched when an archive recording of the session stops.\n *\n * @name archiveStopped\n * @event\n * @memberof Session\n * @see ArchiveEvent\n * @see Archiving overview\n */\n\n /**\n * Dispatched when a new client (including your own) has connected to the session, and for\n * every client in the session when you first connect. (The Session object also dispatches\n * a sessionConnected
event when your local client connects.)\n *\n * @name connectionCreated\n * @event\n * @memberof Session\n * @see ConnectionEvent\n * @see OT.initSession()\n */\n\n /**\n * A client, other than your own, has disconnected from the session.\n * @name connectionDestroyed\n * @event\n * @memberof Session\n * @see ConnectionEvent\n */\n\n /**\n * A moderator has forced clients publishing streams to the session to mute audio.\n *\n * This is a beta feature.\n *\n * @name muteForced\n * @event\n * @memberof Session\n * @see Event\n */\n\n /**\n * The client has connected to an OpenTok session. This event is dispatched asynchronously\n * in response to a successful call to the connect()
method of a Session\n * object. Before calling the connect()
method, initialize the session by\n * calling the OT.initSession()
method. For a code example and more details,\n * see Session.connect().\n * @name sessionConnected\n * @event\n * @memberof Session\n * @see SessionConnectEvent\n * @see Session.connect()\n * @see OT.initSession()\n */\n\n /**\n * The client has disconnected from the session. This event may be dispatched asynchronously\n * in response to a successful call to the disconnect()
method of the Session object.\n * The event may also be disptached if a session connection is lost inadvertantly, as in the case\n * of a lost network connection.\n * \n * The default behavior is that all Subscriber objects are unsubscribed and removed from the\n * HTML DOM. Each Subscriber object dispatches a destroyed
event when the element is\n * removed from the HTML DOM. If you call the preventDefault()
method in the event\n * listener for the sessionDisconnect
event, the default behavior is prevented, and\n * you can, optionally, clean up Subscriber objects using your own code.\n *
The reason
property of the event object indicates the reason for the client\n * being disconnected.\n * @name sessionDisconnected\n * @event\n * @memberof Session\n * @see Session.disconnect()\n * @see Session.forceDisconnect()\n * @see SessionDisconnectEvent\n */\n\n /**\n * The local client has lost its connection to an OpenTok session and is trying to reconnect.\n * This results from a loss in network connectivity. If the client can reconnect to the session,\n * the Session object dispatches a sessionReconnected
event. Otherwise, if the client\n * cannot reconnect, the Session object dispatches a sessionDisconnected
event.\n *
\n * In response to this event, you may want to provide a user interface notification, to let\n * the user know that the app is trying to reconnect to the session and that audio-video streams\n * are temporarily disconnected.\n *\n * @name sessionReconnecting\n * @event\n * @memberof Session\n * @see Event\n * @see sessionReconnected event\n * @see sessionDisconnected event\n */\n\n /**\n * The local client has reconnected to the OpenTok session after its connection was lost\n * temporarily. When the connection is lost, the Session object dispatches a\n * sessionReconnecting
event, prior to the sessionReconnected
\n * event. If the client cannot reconnect to the session, the Session object dispatches a\n * sessionDisconnected
event instead of this event.\n *
\n * Any existing publishers and subscribers are automatically reconnected when client reconnects\n * and the Session object dispatches this event.\n *
\n * Any signals sent by other clients while your client was disconnected are received upon\n * reconnecting. By default, signals initiated by the local client while disconnected\n * (by calling the Session.signal()
method) are sent when the client reconnects\n * to the OpenTok session. You can prevent this by setting the retryAfterReconnect
\n * property to false
in the signal
object you pass into the\n * Session.signal() method.\n *\n * @name sessionReconnected\n * @event\n * @memberof Session\n * @see Event\n * @see sessionReconnecting event\n * @see sessionDisconnected event\n */\n\n /**\n * A new stream, published by another client, has been created on this session. For streams\n * published by your own client, the Publisher object dispatches a streamCreated
\n * event. For a code example and more details, see {@link StreamEvent}.\n * @name streamCreated\n * @event\n * @memberof Session\n * @see StreamEvent\n * @see Session.publish()\n */\n\n /**\n * A stream from another client has stopped publishing to the session.\n *
\n * The default behavior is that all Subscriber objects that are subscribed to the stream are\n * unsubscribed and removed from the HTML DOM. Each Subscriber object dispatches a\n * destroyed
event when the element is removed from the HTML DOM. If you call the\n * preventDefault()
method in the event listener for the\n * streamDestroyed
event, the default behavior is prevented and you can clean up\n * a Subscriber object for the stream by calling its destroy()
method. See\n * Session.getSubscribersForStream().\n *
\n * For streams published by your own client, the Publisher object dispatches a\n * streamDestroyed
event.\n *
\n * For a code example and more details, see {@link StreamEvent}.\n * @name streamDestroyed\n * @event\n * @memberof Session\n * @see StreamEvent\n */\n\n /**\n * Defines an event dispatched when property of a stream has changed. This can happen in\n * in the following conditions:\n *
\n *
\n * - A stream has started or stopped publishing audio or video (see\n * Publisher.publishAudio() and\n * Publisher.publishVideo()). Note\n * that a subscriber's video can be disabled or enabled for reasons other than\n * the publisher disabling or enabling it. A Subscriber object dispatches\n *
videoDisabled
and videoEnabled
events in all\n * conditions that cause the subscriber's stream to be disabled or enabled.\n * \n * - The
videoDimensions
property of the Stream object has\n * changed (see Stream.videoDimensions).\n * \n * - The
videoType
property of the Stream object has changed.\n * This can happen in a stream published by a mobile device. (See\n * Stream.videoType.)\n * \n *
\n *\n * @name streamPropertyChanged\n * @event\n * @memberof Session\n * @see StreamPropertyChangedEvent\n * @see Publisher.publishAudio()\n * @see Publisher.publishVideo()\n * @see Stream.hasAudio\n * @see Stream.hasVideo\n * @see Stream.videoDimensions\n * @see Subscriber videoDisabled event\n * @see Subscriber videoEnabled event\n */\n\n /**\n * A signal was received from the session. The SignalEvent\n * class defines this event object. It includes the following properties:\n * \n * data
— (String) The data string sent with the signal (if there\n * is one). \n * from
— (Connection) The Connection\n * corresponding to the client that sent the signal. \n * type
— (String) The type assigned to the signal (if there is\n * one). \n *
\n * \n * You can register to receive all signals sent in the session, by adding an event handler\n * for the signal
event. For example, the following code adds an event handler\n * to process all signals sent in the session:\n *
\n * session.on(\"signal\", function(event) {\n * console.log(\"Signal sent from connection: \" + event.from.id);\n * console.log(\"Signal data: \" + event.data);\n * });\n *
\n * You can register for signals of a specfied type by adding an event handler for the\n * signal:type
event (replacing type
with the actual type string\n * to filter on).\n *\n * @name signal\n * @event\n * @memberof Session\n * @see Session.signal()\n * @see SignalEvent\n * @see signal:type event\n */\n\n /**\n * A signal of the specified type was received from the session. The\n * SignalEvent class defines this event object.\n * It includes the following properties:\n *
\n * data
— (String) The data string sent with the signal. \n * from
— (Connection) The Connection\n * corresponding to the client that sent the signal. \n * type
— (String) The type assigned to the signal (if there is one).\n * \n *
\n * \n * You can register for signals of a specfied type by adding an event handler for the\n * signal:type
event (replacing type
with the actual type string\n * to filter on). For example, the following code adds an event handler for signals of\n * type \"foo\":\n *
\n * session.on(\"signal:foo\", function(event) {\n * console.log(\"foo signal sent from connection \" + event.from.id);\n * console.log(\"Signal data: \" + event.data);\n * });\n *
\n * \n * You can register to receive all signals sent in the session, by adding an event\n * handler for the signal
event.\n *\n * @name signal:type\n * @event\n * @memberof Session\n * @see Session.signal()\n * @see SignalEvent\n * @see signal event\n */\n\n\n return Session;\n};\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))\n\n/***/ }),\n/* 308 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst ExceptionCodes = __webpack_require__(9);\n\nconst OTErrorClass = __webpack_require__(20);\n\nconst knownErrorCodes = [400, 403, 409, ExceptionCodes.CONNECTION_LIMIT_EXCEEDED];\nconst reasons = {\n CONNECT: 'ConnectToSession',\n SESSION_STATE: 'GetSessionState'\n};\n/*\n * Converts an error from RumorSocket to use generic exception codes\n * @param {Error} error - Error object with reason property\n * @return {Object} The converted error code and message\n * @property {number} code\n * @property {string} message\n */\n\nmodule.exports = function convertRumorError(error) {\n let code;\n let message;\n\n if (error.reason === reasons.CONNECT && error.code === ExceptionCodes.CONNECT_FAILED) {\n code = error.code;\n message = OTErrorClass.getTitleByCode(error.code);\n } else if (error.code && knownErrorCodes.indexOf(Number(error.code)) > -1) {\n code = ExceptionCodes.CONNECT_FAILED;\n\n switch (error.reason) {\n case reasons.CONNECT:\n switch (error.code) {\n case ExceptionCodes.CONNECTION_LIMIT_EXCEEDED:\n code = error.code;\n message = 'Cannot connect -- the limit for concurrent connections to the session ' + 'has been reached';\n break;\n\n default:\n message = 'Received error response to connection create message.';\n break;\n }\n\n break;\n\n case reasons.SESSION_STATE:\n message = 'Received error response to session read';\n break;\n\n default:\n message = '';\n break;\n }\n } else {\n code = ExceptionCodes.UNEXPECTED_SERVER_RESPONSE;\n message = 'Unexpected server response. Try this operation again later.';\n }\n\n return {\n code,\n message\n };\n};\n\n/***/ }),\n/* 309 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst env = __webpack_require__(2);\n\nconst hasOpenTokSupport = __webpack_require__(59).once;\n\nmodule.exports = () => hasOpenTokSupport() && (env.isChromiumEdge || ['Chrome', 'Safari', 'Firefox', 'Opera'].includes(env.name));\n\n/***/ }),\n/* 310 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function serializeMessage(message) {\n return JSON.stringify(message);\n};\n\n/***/ }),\n/* 311 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\nexports.default = void 0;\n\nvar _default = (_ref) => {\n let logger = _ref.logger,\n obj = _ref.obj,\n eventNames = _ref.eventNames;\n eventNames.forEach(eventName => {\n obj.on(eventName, function () {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return logger.spam('emitted', eventName, args);\n });\n });\n};\n\nexports.default = _default;\n\n/***/ }),\n/* 312 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable prefer-rest-params */\nconst isFunction = __webpack_require__(13);\n\nconst OTErrorClass = __webpack_require__(20);\n\nconst eventing = __webpack_require__(5);\n\nconst logging = __webpack_require__(0)('Dispatcher');\n\nconst RumorMessageTypes = __webpack_require__(169);\n\nconst unboxFromRumorMessage = __webpack_require__(695);\n\nconst Dispatcher = function Dispatcher() {\n eventing(this);\n this.callbacks = {};\n};\n\nmodule.exports = Dispatcher;\n\nDispatcher.prototype.registerCallback = function (transactionId, completion) {\n this.callbacks[transactionId] = completion;\n};\n\nDispatcher.prototype.triggerCallback = function (transactionId) {\n /* , arg1, arg2, argN-1, argN */\n if (!transactionId) {\n return;\n }\n\n const completion = this.callbacks[transactionId];\n\n if (completion && isFunction(completion)) {\n const args = Array.prototype.slice.call(arguments);\n args.shift();\n completion(...args);\n }\n\n delete this.callbacks[transactionId];\n};\n\nDispatcher.prototype.onClose = function (reason) {\n this.emit('close', reason);\n};\n\nDispatcher.prototype.onReconnected = function () {\n this.emit('reconnected');\n};\n\nDispatcher.prototype.onReconnecting = function () {\n this.emit('reconnecting');\n};\n\nDispatcher.prototype.dispatch = function (rumorMessage) {\n // The special casing of STATUS messages is ugly. Need to think about\n // how to better integrate this.\n if (rumorMessage.type === RumorMessageTypes.STATUS) {\n var _data$mute;\n\n logging.debug(rumorMessage);\n let error;\n\n if (rumorMessage.isError) {\n let message;\n\n if (typeof rumorMessage.data === 'string') {\n try {\n const data = JSON.parse(rumorMessage.data);\n\n if (data && typeof data === 'object') {\n message = data.reason;\n }\n } catch (e) {\n logging.warn('Failed to parse rumorMessage.data', e);\n }\n }\n\n error = new OTErrorClass(rumorMessage.status, message);\n }\n\n const data = rumorMessage.data ? JSON.parse(rumorMessage.data) : '';\n\n if (data == null ? void 0 : (_data$mute = data.mute) == null ? void 0 : _data$mute.active) {\n this.dispatchMuteOnEntry();\n }\n\n this.triggerCallback(rumorMessage.transactionId, error, rumorMessage);\n return;\n }\n\n const message = unboxFromRumorMessage(rumorMessage);\n logging.debug(`${message.signature}:`, message);\n\n switch (message.resource) {\n case 'session':\n this.dispatchSession(message);\n break;\n\n case 'connection':\n this.dispatchConnection(message);\n break;\n\n case 'stream':\n this.dispatchStream(message);\n break;\n\n case 'stream_channel':\n this.dispatchStreamChannel(message);\n break;\n\n case 'subscriber':\n this.dispatchSubscriber(message);\n break;\n\n case 'subscriber_channel':\n this.dispatchSubscriberChannel(message);\n break;\n\n case 'signal':\n this.dispatchSignal(message);\n break;\n\n case 'archive':\n this.dispatchArchive(message);\n break;\n\n case 'source':\n this.dispatchSource(message);\n break;\n\n default:\n logging.debug(`Type ${message.resource} is not currently implemented`);\n }\n};\n\nDispatcher.prototype.dispatchSession = function (message) {\n switch (message.method) {\n case 'read':\n this.emit('session#read', message.content, message.transactionId);\n break;\n\n case 'update':\n this.emit('session#update', message.content);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n};\n\nDispatcher.prototype.dispatchMuteOnEntry = function () {\n this.emit('session#muted');\n};\n\nDispatcher.prototype.dispatchConnection = function (message) {\n switch (message.method) {\n case 'created':\n this.emit('connection#created', message.content);\n break;\n\n case 'deleted':\n this.emit('connection#deleted', message.params.connection, message.reason);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n};\n\nDispatcher.prototype.dispatchStream = function (message) {\n switch (message.method) {\n case 'created':\n this.emit('stream#created', message.content, message.transactionId);\n break;\n\n case 'deleted':\n this.emit('stream#deleted', message.params.stream, message.reason, message.content);\n break;\n\n case 'updated':\n this.emit('stream#updated', message.params.stream, message.content);\n break;\n\n case 'update':\n this.emit('stream#update', message.params.stream, message.content);\n break;\n // The JSEP process\n\n case 'generateoffer':\n case 'answer':\n case 'pranswer':\n case 'offer':\n case 'candidate':\n this.dispatchJsep(message.method, message);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n};\n\nDispatcher.prototype.dispatchStreamChannel = function (message) {\n switch (message.method) {\n case 'updated':\n this.emit('streamChannel#updated', message.params.stream, message.params.channel, message.content);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n}; // Dispatch JSEP messages\n//\n// generateoffer:\n// Request to generate a offer for another Peer (or Prism). This kicks\n// off the JSEP process.\n//\n// answer:\n// generate a response to another peers offer, this contains our constraints\n// and requirements.\n//\n// pranswer:\n// a provisional answer, i.e. not the final one.\n//\n// candidate\n//\n//\n\n\nDispatcher.prototype.dispatchJsep = function (method, message) {\n this.emit(`jsep#${method}`, message.params.stream, message.fromAddress, message);\n};\n\nDispatcher.prototype.dispatchSubscriberChannel = function (message) {\n switch (message.method) {\n case 'updated':\n this.emit('subscriberChannel#updated', message.params.stream, message.params.channel, message.content);\n break;\n\n case 'update':\n // subscriberId, streamId, content\n this.emit('subscriberChannel#update', message.params.subscriber, message.params.stream, message.content);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n};\n\nDispatcher.prototype.dispatchSubscriber = function (message) {\n switch (message.method) {\n case 'created':\n this.emit('subscriber#created', message.params.stream, message.fromAddress, message.content.id);\n break;\n\n case 'deleted':\n this.dispatchJsep('unsubscribe', message);\n this.emit('subscriber#deleted', message.params.stream, message.fromAddress);\n break;\n // The JSEP process\n\n case 'generateoffer':\n case 'answer':\n case 'pranswer':\n case 'offer':\n case 'candidate':\n this.dispatchJsep(message.method, message);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n};\n\nDispatcher.prototype.dispatchSignal = function (message) {\n if (message.method !== 'signal') {\n logging.debug(`${message.signature} is not currently implemented`);\n return;\n }\n\n this.emit('signal', message.fromAddress, message.content);\n};\n\nDispatcher.prototype.dispatchArchive = function (message) {\n switch (message.method) {\n case 'created':\n this.emit('archive#created', message.content);\n break;\n\n case 'updated':\n this.emit('archive#updated', message.params.archive, message.content);\n break;\n\n default:\n }\n};\n\nDispatcher.prototype.dispatchSource = function (message) {\n switch (message.method) {\n case 'create':\n this.emit('source#create', message.params.source, message.params.stream, message.reason);\n break;\n\n case 'delete':\n this.emit('source#delete', message.params.source, message.params.stream, message.reason);\n break;\n\n default:\n logging.debug(`${message.signature} is not currently implemented`);\n }\n};\n\n/***/ }),\n/* 313 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-void */\nconst uuid = __webpack_require__(17);\n\nconst OTHelpers = __webpack_require__(4);\n\nconst hasBundleCapability = __webpack_require__(698);\n\nconst hasRTCPMuxCapability = __webpack_require__(699);\n\nconst serializeMessage = __webpack_require__(310);\n\nconst supportedCryptoScheme = __webpack_require__(700);\n\nconst staticConfig = __webpack_require__(42)().onlyLocal();\n\nconst Message = {};\nmodule.exports = Message;\nMessage.connections = {};\n\nMessage.connections.create = function (opt) {\n const apiKey = opt.apiKey;\n const sessionId = opt.sessionId;\n const connectionId = opt.connectionId;\n const connectionEventsSuppressed = opt.connectionEventsSuppressed;\n const capabilities = opt.capabilities;\n return serializeMessage({\n method: 'create',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/connection/${connectionId}`,\n content: {\n userAgent: OTHelpers.env.userAgent,\n clientVersion: staticConfig.clientVersion,\n capabilities: capabilities || [],\n connectionEventsSuppressed\n }\n });\n};\n\nMessage.connections.destroy = function (opt) {\n const apiKey = opt.apiKey;\n const sessionId = opt.sessionId;\n const connectionId = opt.connectionId;\n return serializeMessage({\n method: 'delete',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/connection/${connectionId}`,\n content: {}\n });\n};\n\nMessage.sessions = {};\n\nMessage.sessions.get = function (apiKey, sessionId) {\n return serializeMessage({\n method: 'read',\n uri: `/v2/partner/${apiKey}/session/${sessionId}`,\n content: {}\n });\n};\n\nMessage.streams = {};\n\nMessage.streams.get = function (apiKey, sessionId, streamId) {\n return serializeMessage({\n method: 'read',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}`,\n content: {}\n });\n};\n\nMessage.streams.channelFromOTChannel = function (channel) {\n const raptorChannel = {\n id: channel.id,\n type: channel.type,\n active: channel.active\n };\n\n if (channel.type === 'video') {\n raptorChannel.width = channel.width;\n raptorChannel.height = channel.height;\n raptorChannel.orientation = channel.orientation;\n raptorChannel.frameRate = channel.frameRate;\n\n if (channel.source !== 'default') {\n raptorChannel.source = channel.source;\n }\n\n raptorChannel.fitMode = channel.fitMode;\n }\n\n return raptorChannel;\n};\n\nMessage.streams.create = function (apiKey, sessionId, streamId, name, audioFallbackEnabled, channels, minBitrate, maxBitrate, sourceStreamId) {\n const messageContent = {\n id: streamId,\n name,\n audioFallbackEnabled,\n channel: channels.map(channel => Message.streams.channelFromOTChannel(channel))\n };\n\n if (minBitrate) {\n messageContent.minBitrate = Math.round(minBitrate);\n }\n\n if (maxBitrate) {\n messageContent.maxBitrate = Math.round(maxBitrate);\n }\n\n if (sourceStreamId) {\n messageContent.sourceStreamId = sourceStreamId;\n }\n\n return serializeMessage({\n method: 'create',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}`,\n content: messageContent\n });\n};\n\nMessage.streams.destroy = function (apiKey, sessionId, streamId, sourceStreamId) {\n const messageContent = {};\n\n if (sourceStreamId) {\n messageContent.sourceStreamId = sourceStreamId;\n }\n\n return serializeMessage({\n method: 'delete',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}`,\n content: messageContent\n });\n};\n\nMessage.streamChannels = {};\n\nMessage.streamChannels.update = function (apiKey, sessionId, streamId, channelId, attributes) {\n return serializeMessage({\n method: 'update',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}/channel/${channelId}`,\n content: attributes\n });\n};\n\nMessage.subscribers = {};\n\nMessage.subscribers.create = function (apiKey, sessionId, streamId, subscriberId, connectionId, channelsToSubscribeTo, sourceStreamId) {\n const content = {\n id: subscriberId,\n connection: connectionId,\n keyManagementMethod: supportedCryptoScheme,\n bundleSupport: hasBundleCapability(),\n rtcpMuxSupport: hasRTCPMuxCapability()\n };\n\n if (channelsToSubscribeTo) {\n content.channel = channelsToSubscribeTo;\n }\n\n if (sourceStreamId) {\n content.sourceStreamId = sourceStreamId;\n }\n\n return serializeMessage({\n method: 'create',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}/subscriber/${subscriberId}`,\n content\n });\n};\n\nMessage.subscribers.destroy = function (apiKey, sessionId, streamId, subscriberId) {\n return serializeMessage({\n method: 'delete',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}/subscriber/${subscriberId}`,\n content: {}\n });\n};\n\nMessage.subscribers.update = function (apiKey, sessionId, streamId, subscriberId, attributes) {\n return serializeMessage({\n method: 'update',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}/subscriber/${subscriberId}`,\n content: attributes\n });\n};\n\nMessage.subscriberChannels = {};\n\nMessage.subscriberChannels.update = function (apiKey, sessionId, streamId, subscriberId, channelId, attributes) {\n return serializeMessage({\n method: 'update',\n uri: `/v2/partner/${apiKey}/session/${sessionId}/stream/${streamId}/subscriber/${subscriberId}/channel/${channelId}`,\n content: attributes\n });\n};\n\nMessage.signals = {};\n\nMessage.signals.create = function (apiKey, sessionId, toAddress, type, data) {\n const content = {};\n\n if (type !== void 0) {\n content.type = type;\n }\n\n if (data !== void 0) {\n content.data = data;\n }\n\n return serializeMessage({\n method: 'signal',\n uri: `/v2/partner/${apiKey}/session/${sessionId}${toAddress !== void 0 ? `/connection/${toAddress}` : ''}/signal/${uuid()}`,\n content\n });\n};\n\nMessage.forceMute = {};\n\nMessage.forceMute.update = function (apiKey, sessionId, streamId, active, excludedStreamIds) {\n const content = {\n reason: 'mute',\n channels: ['audio'],\n active,\n locked: false\n };\n\n if (excludedStreamIds) {\n content.exclusion = excludedStreamIds;\n }\n\n return serializeMessage({\n method: 'update',\n uri: `/v2/partner/${apiKey}/session/${sessionId}${streamId ? `/stream/${streamId}` : ''}`,\n content\n });\n};\n\n/***/ }),\n/* 314 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-useless-escape, no-underscore-dangle, no-void, no-prototype-builtins */\nconst clone = __webpack_require__(34);\n\nconst APIKEY = __webpack_require__(71);\n\nconst Connection = __webpack_require__(170);\n\nconst RaptorMessage = __webpack_require__(313);\n\nconst sessionTag = __webpack_require__(315);\n\nconst MAX_SIGNAL_DATA_LENGTH = 8192;\nconst MAX_SIGNAL_TYPE_LENGTH = 128; //\n// Error Codes:\n// 413 - Type too long\n// 400 - Type is invalid\n// 413 - Data too long\n// 400 - Data is invalid (can't be parsed as JSON)\n// 429 - Rate limit exceeded\n// 500 - Websocket connection is down\n// 404 - To connection does not exist\n// 400 - To is invalid\n//\n\nmodule.exports = function Signal(sessionId, fromConnectionId, options) {\n const isInvalidType = function isInvalidType(type) {\n // Our format matches the unreserved characters from the URI RFC:\n // http://www.ietf.org/rfc/rfc3986\n return !/^[a-zA-Z0-9\\-\\._~]+$/.exec(type);\n };\n\n const validateTo = function validateTo(toAddress) {\n if (!toAddress) {\n return {\n code: 400,\n reason: 'The signal to field was invalid. Either set it to a OT.Connection, ' + 'OT.Session, or omit it entirely'\n };\n }\n\n if (!(toAddress instanceof Connection || toAddress._tag === sessionTag)) {\n return {\n code: 400,\n reason: 'The To field was invalid'\n };\n }\n\n return null;\n };\n\n const validateType = function validateType(type) {\n let error = null;\n\n if (type === null || type === void 0) {\n error = {\n code: 400,\n reason: 'The signal type was null or undefined. Either set it to a String value or ' + 'omit it'\n };\n } else if (type.length > MAX_SIGNAL_TYPE_LENGTH) {\n error = {\n code: 413,\n reason: `The signal type was too long, the maximum length of it is ${MAX_SIGNAL_TYPE_LENGTH} characters`\n };\n } else if (isInvalidType(type)) {\n error = {\n code: 400,\n reason: 'The signal type was invalid, it can only contain letters, ' + 'numbers, \\'-\\', \\'_\\', and \\'~\\'.'\n };\n }\n\n return error;\n };\n\n const validateData = function validateData(data) {\n let error = null;\n\n if (data === null || data === void 0) {\n error = {\n code: 400,\n reason: 'The signal data was null or undefined. Either set it to a String value or ' + 'omit it'\n };\n } else {\n try {\n if (JSON.stringify(data).length > MAX_SIGNAL_DATA_LENGTH) {\n error = {\n code: 413,\n reason: `The data field was too long, the maximum size of it is ${MAX_SIGNAL_DATA_LENGTH} characters`\n };\n }\n } catch (e) {\n error = {\n code: 400,\n reason: 'The data field was not valid JSON'\n };\n }\n }\n\n return error;\n };\n\n const validateRetryAfterReconnect = function validateRetryAfterReconnect(retryAfterReconnect) {\n let error = null;\n\n if (!(retryAfterReconnect === true || retryAfterReconnect === false)) {\n error = {\n code: 400,\n reason: 'The signal retryAfterReconnect was not true or false. Either set it to a Boolean ' + 'value or omit it'\n };\n }\n\n return error;\n };\n\n this.toRaptorMessage = function () {\n let to = this.to;\n\n if (to && typeof to !== 'string') {\n to = to.id;\n }\n\n return RaptorMessage.signals.create(APIKEY.value, sessionId, to, this.type, this.data);\n };\n\n this.toHash = function () {\n return options;\n };\n\n this.error = null;\n this.retryAfterReconnect = true;\n\n if (options) {\n if (options.hasOwnProperty('data')) {\n this.data = clone(options.data);\n this.error = validateData(this.data);\n }\n\n if (options.hasOwnProperty('to')) {\n this.to = options.to;\n\n if (!this.error) {\n this.error = validateTo(this.to);\n }\n }\n\n if (options.hasOwnProperty('type')) {\n if (!this.error) {\n this.error = validateType(options.type);\n }\n\n this.type = options.type;\n }\n\n if (options.hasOwnProperty('retryAfterReconnect')) {\n if (!this.error) {\n this.error = validateRetryAfterReconnect(options.retryAfterReconnect);\n }\n\n this.retryAfterReconnect = options.retryAfterReconnect;\n }\n }\n\n this.valid = this.error === null;\n};\n\n/***/ }),\n/* 315 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// This is used to break the dependency Raptor had on Session. It only needs to be able to know\n// whether an object is an instanceof a Session. The dependency was an issue for node because\n// Session depends on get_user_media.js which doesn't work in node.\nmodule.exports = {};\n\n/***/ }),\n/* 316 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nvar _interopRequireDefault = __webpack_require__(3);\n\nvar _regenerator = _interopRequireDefault(__webpack_require__(18));\n\nvar _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(19));\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable no-shadow, prefer-rest-params, prefer-spread, no-param-reassign */\n\n/* eslint-disable no-mixed-operators, no-cond-assign, one-var */\nconst Archive = __webpack_require__(317);\n\nconst now = __webpack_require__(47);\n\nconst Connection = __webpack_require__(170);\n\nconst DelayedEventQueue = __webpack_require__(703);\n\nconst Dispatcher = __webpack_require__(312);\n\nconst logging = __webpack_require__(0)('SessionDispatcher');\n\nconst sessionObjects = __webpack_require__(23);\n\nconst Stream = __webpack_require__(168);\n\nconst StreamChannel = __webpack_require__(149);\n\nfunction constructBareConnection(fromAddress) {\n return {\n id: fromAddress,\n creationTime: new Date().getTime(),\n data: {},\n capablities: {},\n permissions: []\n };\n}\n\nfunction parseStream(dict, session) {\n const channel = dict.channel.map(channel => new StreamChannel(channel));\n const connectionId = dict.connectionId ? dict.connectionId : dict.connection.id;\n return new Stream(dict.id, dict.name, dict.creationTime, session.connections.get(connectionId), session, channel);\n}\n\nfunction parseAndAddStreamToSession(streamParams, session) {\n if (session.streams.has(streamParams.id)) {\n return undefined;\n }\n\n const stream = parseStream(streamParams, session);\n session.streams.add(stream);\n return stream;\n}\n\nfunction parseArchive(archiveParams) {\n return new Archive(archiveParams.id, archiveParams.name, archiveParams.status);\n}\n\nfunction parseAndAddArchiveToSession(archiveParams, session) {\n if (session.archives.has(archiveParams.id)) {\n return undefined;\n }\n\n const archive = parseArchive(archiveParams);\n session.archives.add(archive);\n return archive;\n}\n\nconst DelayedSessionEvents = function DelayedSessionEvents(dispatcher) {\n const eventQueues = {};\n\n this.enqueue = function enqueue()\n /* key, arg1, arg2, ..., argN */\n {\n const key = arguments[0];\n const eventArgs = Array.prototype.slice.call(arguments, 1);\n\n if (!eventQueues[key]) {\n eventQueues[key] = new DelayedEventQueue(dispatcher);\n }\n\n eventQueues[key].enqueue.apply(eventQueues[key], eventArgs);\n };\n\n this.triggerConnectionCreated = function triggerConnectionCreated(connection) {\n if (eventQueues[`connectionCreated${connection.id}`]) {\n eventQueues[`connectionCreated${connection.id}`].triggerAll();\n }\n };\n\n this.triggerSessionConnected = function triggerSessionConnected(connections) {\n if (eventQueues.sessionConnected) {\n eventQueues.sessionConnected.triggerAll();\n }\n\n connections.forEach(function (connection) {\n this.triggerConnectionCreated(connection);\n }, this);\n };\n};\n\nconst unconnectedStreams = {};\n\nmodule.exports = function SessionDispatcher(session, _temp) {\n let _ref = _temp === void 0 ? {} : _temp,\n connectionEventsSuppressed = _ref.connectionEventsSuppressed;\n\n const dispatcher = new Dispatcher();\n let sessionStateReceived = false;\n const delayedSessionEvents = new DelayedSessionEvents(dispatcher);\n dispatcher.on('reconnecting', () => {\n session._.reconnecting();\n });\n dispatcher.on('reconnected', () => {\n session._.reconnected();\n });\n dispatcher.on('close', reason => {\n const connection = session.connection;\n\n if (!connection) {\n return;\n }\n\n if (connection.destroyedReason()) {\n logging.debug(`${'Socket was closed but the connection had already ' + 'been destroyed. Reason: '}${connection.destroyedReason()}`);\n return;\n }\n\n connection.destroy(reason);\n }); // This method adds connections to the session both on a connection#created and\n // on a session#read. In the case of session#read sessionRead is set to true and\n // we include our own connection.\n\n const addConnection = function addConnection(connection, sessionRead) {\n if (session.connections.has(connection.id)) {\n // Don't add a duplicate connection, since we add them not only on connection#created, but\n // also when stream#created or signal has an associated connection.\n return session.connections.get(connection.id);\n }\n\n connection = Connection.fromHash(connection);\n\n if (sessionRead || session.connection && connection.id !== session.connection.id) {\n session.connections.add(connection);\n delayedSessionEvents.triggerConnectionCreated(connection);\n }\n\n Object.keys(unconnectedStreams).forEach(streamId => {\n const stream = unconnectedStreams[streamId];\n\n if (stream && connection.id === stream.connection.id) {\n // dispatch streamCreated event now that the connectionCreated has been dispatched\n parseAndAddStreamToSession(stream, session);\n delete unconnectedStreams[stream.id];\n const payload = {\n debug: sessionRead ? 'connection came in session#read' : 'connection came in connection#created',\n streamId: stream.id,\n connectionId: connection.id\n };\n session.logEvent('streamCreated', 'warning', payload);\n }\n });\n return connection;\n };\n\n dispatcher.on('session#read', (content, transactionId) => {\n let connection;\n const state = {};\n state.streams = [];\n state.connections = [];\n state.archives = [];\n content.connection.forEach(connectionParams => {\n connection = addConnection(connectionParams, true);\n state.connections.push(connection);\n });\n content.stream.forEach(streamParams => {\n state.streams.push(parseAndAddStreamToSession(streamParams, session));\n });\n (content.archive || content.archives).forEach(archiveParams => {\n state.archives.push(parseAndAddArchiveToSession(archiveParams, session));\n });\n dispatcher.triggerCallback(transactionId, null, state);\n sessionStateReceived = true;\n delayedSessionEvents.triggerSessionConnected(session.connections);\n });\n dispatcher.on('session#update', content => {\n if (content.reason !== 'mute') {\n logging.debug('session#update that handle this reason is not currently implemented');\n return;\n }\n\n session._.forceMute();\n\n if (content.active) {\n session._.enableMuteOnEntry();\n } else {\n session._.disableMuteOnEntry();\n }\n });\n dispatcher.on('session#muted', () => {\n session._.enableMuteOnEntry();\n });\n dispatcher.on('connection#created', connection => {\n addConnection(connection);\n });\n dispatcher.on('connection#deleted', (connection, reason) => {\n connection = session.connections.get(connection);\n\n if (!connection) {\n logging.warn('A connection was deleted that we do not know about');\n return;\n }\n\n connection.destroy(reason);\n });\n dispatcher.on('stream#created', (content, transactionId) => {\n const streamId = content.id,\n sourceStreamId = content.sourceStreamId,\n connection = content.connection;\n let stream;\n\n if (connectionEventsSuppressed) {\n if (connection == null || connection.id == null) {\n session.logEvent('SessionDispatcher:stream#created', 'Event', {\n connection,\n info: 'Stream did not contain a connection object. Event ignored'\n });\n return;\n }\n\n addConnection(connection);\n }\n\n const connectionId = content.connectionId || connection.id;\n\n if (session.connections.has(connectionId)) {\n stream = parseAndAddStreamToSession(content, session);\n } else {\n unconnectedStreams[streamId] = content;\n const payload = {\n debug: 'eventOrderError -- streamCreated event before connectionCreated',\n streamId,\n sourceStreamId\n };\n session.logEvent('streamCreated', 'warning', payload);\n }\n\n if (stream) {\n if (stream.publisher) {\n stream.publisher.setStream(stream);\n }\n\n dispatcher.triggerCallback(transactionId, null, stream);\n } else if (session.sessionInfo.isAdaptiveEnabled && sourceStreamId === 'P2P') {\n // A P2P stream was created in an adaptive session, so let's request a new subscriber for it\n sessionObjects.subscribers.where({\n streamId\n }).forEach(subscriber => {\n subscriber._.startRoutedToRelayedTransition();\n });\n }\n });\n dispatcher.on('stream#deleted', (streamId, reason, content) => {\n const stream = session.streams.get(streamId);\n const sourceStreamId = content.sourceStreamId;\n const subscribers = sessionObjects.subscribers;\n const isAdaptiveEnabled = session.sessionInfo.isAdaptiveEnabled;\n const isAdaptiveP2pStream = isAdaptiveEnabled && sourceStreamId === 'P2P';\n\n if (!stream && !isAdaptiveP2pStream) {\n logging.error(`A stream does not exist with the id of ${streamId}, for stream#deleted message!`); // @todo error\n\n return;\n }\n\n if (isAdaptiveP2pStream) {\n // In adaptive sessions, when the P2P stream is destroyed we need to start the transition\n // from relayed to routed.\n subscribers.where({\n streamId\n }).forEach(subscriber => {\n subscriber._.startRelayedToRoutedTransition();\n });\n } else {\n stream.destroy(reason);\n }\n });\n dispatcher.on('stream#updated', (streamId, content) => {\n const stream = session.streams.get(streamId);\n\n if (!stream) {\n logging.error(`A stream does not exist with the id of ${streamId}, for stream#updated message!`); // @todo error\n\n return;\n }\n\n stream._.update(content);\n });\n dispatcher.on('stream#update', (streamId, content) => {\n const stream = session.streams.get(streamId);\n\n if (!stream) {\n logging.error(`A stream does not exist with the id of ${streamId}, for stream#updated message!`); // @todo error\n\n return;\n }\n\n if (content.reason !== 'mute') {\n logging.debug('stream#update that handle this reason is not currently implemented');\n return;\n }\n\n stream._.forceMute(content);\n });\n dispatcher.on('streamChannel#updated', (streamId, channelId, content) => {\n let stream;\n\n if (!(streamId && (stream = session.streams.get(streamId)))) {\n logging.error('Unable to determine streamId, or the stream does not ' + 'exist, for streamChannel message!'); // @todo error\n\n return;\n }\n\n stream._.updateChannel(channelId, content);\n }); // Dispatch JSEP messages\n //\n // generateoffer:\n // Request to generate a offer for another Peer (or Prism). This kicks\n // off the JSEP process.\n //\n // answer:\n // generate a response to another peers offer, this contains our constraints\n // and requirements.\n //\n // pranswer:\n // a provisional answer, i.e. not the final one.\n //\n // candidate\n //\n //\n\n const jsepHandler = (method, streamId, fromAddress, message) => {\n let subscriberFilter;\n let actors;\n const hasStreamId = {\n streamId\n };\n const subscribers = sessionObjects.subscribers;\n const publishers = sessionObjects.publishers;\n\n if (message.params.subscriber) {\n subscriberFilter = {\n widgetId: message.params.subscriber\n };\n } else {\n // if we don't know the subscriber, we will just match the stream id\n subscriberFilter = hasStreamId;\n } // Determine which subscriber/publisher objects should receive this message.\n\n\n switch (method) {\n case 'offer':\n actors = [].concat(subscribers.where(subscriberFilter), publishers.where(hasStreamId)).slice(0, 1);\n break;\n\n case 'answer':\n case 'pranswer':\n actors = [].concat(publishers.where(hasStreamId), subscribers.where(subscriberFilter)).slice(0, 1);\n break;\n\n case 'generateoffer':\n case 'unsubscribe':\n actors = publishers.where(hasStreamId);\n break;\n\n case 'candidate':\n actors = [].concat(subscribers.where(subscriberFilter), publishers.where(hasStreamId));\n break;\n\n default:\n logging.debug(`jsep#${method} is not currently implemented`);\n return;\n }\n\n if (actors.length === 0) {\n return;\n }\n\n let fromConnection = session.connections.get(fromAddress);\n\n if (!fromConnection && fromAddress.match(/^symphony\\./)) {\n fromConnection = Connection.fromHash({\n id: fromAddress,\n creationTime: Math.floor(now())\n });\n session.connections.add(fromConnection);\n }\n\n actors.forEach( /*#__PURE__*/function () {\n var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(actor) {\n return _regenerator.default.wrap(function _callee$(_context) {\n while (1) switch (_context.prev = _context.next) {\n case 0:\n _context.prev = 0;\n _context.next = 3;\n return actor.processMessage(method, fromAddress, message);\n\n case 3:\n _context.next = 8;\n break;\n\n case 5:\n _context.prev = 5;\n _context.t0 = _context[\"catch\"](0);\n logging.error('Error occurred during processMessage', _context.t0);\n\n case 8:\n case \"end\":\n return _context.stop();\n }\n }, _callee, null, [[0, 5]]);\n }));\n\n return function (_x) {\n return _ref2.apply(this, arguments);\n };\n }());\n };\n\n dispatcher.on('jsep#offer', jsepHandler.bind(null, 'offer'));\n dispatcher.on('jsep#answer', jsepHandler.bind(null, 'answer'));\n dispatcher.on('jsep#pranswer', jsepHandler.bind(null, 'pranswer'));\n dispatcher.on('jsep#generateoffer', jsepHandler.bind(null, 'generateoffer'));\n dispatcher.on('jsep#unsubscribe', jsepHandler.bind(null, 'unsubscribe'));\n dispatcher.on('jsep#candidate', jsepHandler.bind(null, 'candidate'));\n dispatcher.on('subscriberChannel#updated', (streamId, channelId, content) => {\n if (!streamId || !session.streams.has(streamId)) {\n logging.error('Unable to determine streamId, or the stream does not ' + 'exist, for subscriberChannel#updated message!'); // @todo error\n\n return;\n }\n\n session.streams.get(streamId)._.updateChannel(channelId, content);\n });\n dispatcher.on('subscriberChannel#update', (subscriberId, streamId, content) => {\n if (!streamId || !session.streams.has(streamId)) {\n logging.error('Unable to determine streamId, or the stream does not ' + 'exist, for subscriberChannel#update message!'); // @todo error\n\n return;\n } // Hint to update for congestion control from the Media Server\n\n\n if (!sessionObjects.subscribers.has(subscriberId)) {\n logging.error('Unable to determine subscriberId, or the subscriber ' + 'does not exist, for subscriberChannel#update message!'); // @todo error\n\n return;\n } // We assume that an update on a Subscriber channel is to disableVideo\n // we may need to be more specific in the future\n\n\n sessionObjects.subscribers.get(subscriberId).disableVideo(content.active);\n }); // Note: subscriber#created and subscriber#deleted messages are available but we currently\n // don't have a use for them.\n\n dispatcher.on('signal', (fromAddress, content) => {\n if (connectionEventsSuppressed) {\n let connection = content.connection || content.fromConnection;\n\n if (connection == null || connection.id == null) {\n connection = constructBareConnection(fromAddress);\n session.logEvent('SessionDispatcher:Signal', 'Event', {\n fromAddress,\n connection: content.connection,\n fromConnection: content.fromConnection,\n info: 'Signal did not contain a connection object. One has been constructed',\n constructedConnection: connection\n });\n }\n\n addConnection(connection);\n }\n\n const signalType = content.type;\n const data = content.data;\n const fromConnection = session.connections.get(fromAddress);\n\n if (session.connection && fromAddress === session.connection.connectionId) {\n if (sessionStateReceived) {\n session._.dispatchSignal(fromConnection, signalType, data);\n } else {\n delayedSessionEvents.enqueue('sessionConnected', 'signal', fromAddress, signalType, data);\n }\n } else if (session.connections.get(fromAddress)) {\n session._.dispatchSignal(fromConnection, signalType, data);\n } else if (fromAddress === '') {\n // Server originated signal\n session._.dispatchSignal(null, signalType, data);\n } else {\n delayedSessionEvents.enqueue(`connectionCreated${fromAddress}`, 'signal', fromAddress, signalType, data);\n }\n });\n dispatcher.on('archive#created', archive => {\n parseAndAddArchiveToSession(archive, session);\n });\n dispatcher.on('archive#updated', (archiveId, update) => {\n const archive = session.archives.get(archiveId);\n\n if (!archive) {\n logging.error(`An archive does not exist with the id of ${archiveId}, for archive#updated message!`); // @todo error\n\n return;\n }\n\n archive._.update(update);\n });\n dispatcher.on('source#create', (sourceStreamId, streamId, reason) => {\n session.logEvent('SessionDispatcher:source#create', 'Event', {\n sourceStreamId,\n streamId,\n reason\n });\n logging.debug('Received a request from RUMOR to start a transition to ' + `${sourceStreamId} for the stream ID ${streamId} with the reason: ${reason}`);\n sessionObjects.publishers.where({\n streamId\n }).forEach(publisher => {\n publisher._.startRoutedToRelayedTransition();\n });\n });\n dispatcher.on('source#delete', (sourceStreamId, streamId, reason) => {\n session.logEvent('SessionDispatcher:source#delete', 'Event', {\n sourceStreamId,\n streamId,\n reason\n });\n logging.debug('Received a request from RUMOR to start a transition from P2P ' + `to MANTIS for the stream ID ${streamId} with the reason: ${reason}`);\n sessionObjects.publishers.where({\n streamId\n }).forEach(publisher => {\n publisher._.startRelayedToRoutedTransition();\n });\n });\n return dispatcher;\n};\n\n/***/ }),\n/* 317 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst Events = __webpack_require__(21)();\n\nconst eventing = __webpack_require__(5);\n\nmodule.exports = function Archive(id, name, status) {\n this.id = id;\n this.name = name;\n this.status = status;\n this._ = {};\n eventing(this); // Mass update, called by Raptor.Dispatcher\n\n this._.update = attributes => {\n Object.keys(attributes).forEach(key => {\n const oldValue = this[key];\n this[key] = attributes[key];\n const event = new Events.ArchiveUpdatedEvent(this, key, oldValue, this[key]);\n this.dispatchEvent(event);\n });\n };\n\n this.destroy = () => {};\n};\n\n/***/ }),\n/* 318 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nconst _require = __webpack_require__(105)(),\n getMediaDevices = _require.getMediaDevices;\n/**\n * Enumerates the audio input devices (such as microphones) and video input devices\n * (cameras) available to the browser.\n *
\n * The array of devices is passed in as the devices
parameter of\n * the callback
function passed into the method.\n *\n * @param callback {Function} The callback function invoked when the list of devices\n * devices is available. This function takes two parameters:\n *
\n * error
— This is set to an error object when\n * there is an error in calling this method; it is set to null
\n * when the call succeeds. \n *\n * devices
— An array of objects corresponding to\n * available microphones and cameras. Each object has three properties: kind
,\n * deviceId
, and label
, each of which are strings.\n *
\n * The kind
property is set to \"audioInput\"
for audio input\n * devices or \"videoInput\"
for video input devices.\n *
\n * The deviceId
property is a unique ID for the device. You can pass\n * the deviceId
in as the audioSource
or videoSource
\n * property of the options
parameter of the\n * OT.initPublisher() method.\n *
\n * The label
property identifies the device. The label
\n * property is set to an empty string if the user has not previously granted access to\n * a camera and microphone.
\n *
\n * Note: The browser may limit the number of returned audio and video input devices\n * to one each before the user has granted accees to a camera or microphone.\n *\n * @see OT.initPublisher()\n * @method OT.getDevices\n * @memberof OT\n */\n\n\nmodule.exports = function getDevices(callback) {\n getMediaDevices().then(devices => callback(undefined, devices), callback);\n};\n\n/***/ }),\n/* 319 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n// @todo enable the following disabled rules see OPENTOK-31136 for more info\n\n/* eslint-disable import/newline-after-import, no-param-reassign */\n__webpack_require__(320);\n\nconst clone = __webpack_require__(34);\n\nconst eventNames = __webpack_require__(24);\n\nconst eventing = __webpack_require__(5);\n\nconst Event = __webpack_require__(126);\n\nconst StaticConfig = __webpack_require__(42)();\n\nconst AnalyticsHelper = __webpack_require__(46);\n\nconst setDeprecatedProperty = __webpack_require__(247);\n\nconst runtimeProperties = __webpack_require__(536);\n\nconst mutableRuntimeProperties = __webpack_require__(537);\n\nconst staticConfig = StaticConfig.onlyLocal(); // We need to do this first because of circular dependency issues with otplugin.js. @TODO: fix this.\n\nconst OT = {}; // noConflict is constructed before we override global.OT so we keep the original value\n\nOT.noConflict = __webpack_require__(538)();\n(typeof window !== undefined ? window : global).OT = OT;\nmodule.exports = OT;\n\n(() => {\n let TB = OT;\n Object.defineProperty((typeof window !== undefined ? window : global), 'TB', {\n get: () => {\n if (TB === OT) {\n // eslint-disable-next-line no-console\n console.warn('window.TB is deprecated, and will be removed in the future. Please access via window.OT');\n }\n\n return TB;\n },\n set: _TB => {\n TB = _TB;\n },\n // We provide a setter so noConflict works\n configurable: true\n });\n})();\n\nconst windowMock = __webpack_require__(142)((typeof window !== undefined ? window : global));\n\nconst OTHelpers = __webpack_require__(4);\n\nconst APIKEY = __webpack_require__(71);\n\nconst AudioLevelTransformer = __webpack_require__(143);\n\nconst calculateCapableSimulcastStreams = __webpack_require__(250);\n\nconst Chrome = __webpack_require__(144);\n\nconst WebAudioAudioLevelSampler = __webpack_require__(145);\n\nconst GetAudioLevelSampler = __webpack_require__(251);\n\nconst EnvironmentLoader = __webpack_require__(146);\n\nconst Errors = __webpack_require__(6);\n\nconst ExceptionCodes = __webpack_require__(9);\n\nconst generateConstraintInfo = __webpack_require__(252);\n\nconst guidStorage = __webpack_require__(238);\n\nconst logging = __webpack_require__(0)('OT');\n\nconst logLevels = __webpack_require__(119);\n\nconst setLogLevel = __webpack_require__(565);\n\nconst OTErrorClass = __webpack_require__(20);\n\nconst _require = __webpack_require__(100),\n parseIceServers = _require.parseIceServers;\n\nconst PUBLISH_MAX_DELAY = __webpack_require__(265);\n\nconst sessionObjects = __webpack_require__(23);\n\nconst StreamChannel = __webpack_require__(149);\n\nconst StylableComponent = __webpack_require__(151);\n\nconst systemRequirements = __webpack_require__(152);\n\nconst PeerConnection = __webpack_require__(101)({\n global: windowMock\n});\n\nconst otError = __webpack_require__(11)({});\n\nconst audioContext = __webpack_require__(157)();\n\nconst chromeExtensionHelper = __webpack_require__(278)({\n otError\n});\n\nconst createChromeMixin = __webpack_require__(279)();\n\nconst getNativeEnumerateDevices = __webpack_require__(284)({\n global: windowMock\n});\n\nconst deviceHelpers = __webpack_require__(105)({\n getNativeEnumerateDevices\n});\n\nconst Events = __webpack_require__(21)();\n\nconst generateSimpleStateMachine = __webpack_require__(159)();\n\nconst getUserMediaHelper = __webpack_require__(617)({\n otError,\n global: windowMock\n});\n\nconst screenSharing = __webpack_require__(160)({\n chromeExtensionHelper,\n otError\n});\n\nconst processPubOptions = __webpack_require__(622)({\n deviceHelpers,\n generateConstraintInfo,\n getUserMediaHelper,\n global: windowMock,\n OTHelpers,\n screenSharing\n});\n\nconst getUserMedia = __webpack_require__(625)({\n otError,\n processPubOptions\n});\n\nconst interpretPeerConnectionError = __webpack_require__(161)({\n otError\n});\n\nconst IntervalRunner = __webpack_require__(74);\n\nconst Microphone = __webpack_require__(289)();\n\nconst videoElementErrorMap = __webpack_require__(290)({\n otError\n});\n\nconst NativeVideoElementWrapper = __webpack_require__(291)({\n global: windowMock,\n videoElementErrorMap\n});\n\nconst setCertificates = __webpack_require__(162)({\n global: windowMock\n});\n\nconst PublisherPeerConnection = __webpack_require__(293)({\n PeerConnection,\n setCertificates\n});\n\nconst SubscriberPeerConnection = __webpack_require__(294)({\n PeerConnection,\n setCertificates\n});\n\nconst PublishingState = __webpack_require__(295)();\n\nconst reportIssue = __webpack_require__(635)({\n otError\n});\n\nconst VideoElementFacade = __webpack_require__(107)({\n NativeVideoElementWrapper\n});\n\nconst VideoOrientation = __webpack_require__(150)();\n\nconst WidgetView = __webpack_require__(163)({\n VideoElementFacade\n});\n\nconst Subscriber = __webpack_require__(298)({\n interpretPeerConnectionError,\n otError,\n SubscriberPeerConnection,\n WidgetView\n});\n\nconst Publisher = __webpack_require__(166)({\n APIKEY,\n createChromeMixin,\n deviceHelpers,\n EnvironmentLoader,\n Errors,\n Events,\n ExceptionCodes,\n calculateCapableSimulcastStreams,\n global: windowMock,\n interpretPeerConnectionError,\n IntervalRunner,\n Microphone,\n otError,\n OTErrorClass,\n OTHelpers,\n parseIceServers,\n processPubOptions,\n PUBLISH_MAX_DELAY,\n PublisherPeerConnection,\n PublishingState,\n StreamChannel,\n systemRequirements,\n VideoOrientation,\n WidgetView\n});\n\nconst initPublisher = __webpack_require__(306)({\n otError,\n Publisher\n});\n\nconst Session = __webpack_require__(307)({\n global: windowMock,\n initPublisher,\n otError,\n Publisher,\n Subscriber\n});\n\nconst initSession = __webpack_require__(707)({\n Session\n}); // Allow events to be bound on OT\n\n\neventing(OT);\n\nconst getSupportedCodecs = __webpack_require__(708);\n\nsetDeprecatedProperty(OT, '$', {\n name: 'OT.$',\n getMessage: 'Please use an external library like jQuery to select elements from the page.',\n value: OTHelpers\n}); // Define the APIKEY this is a global parameter which should not change\n\nOT.APIKEY = APIKEY.value;\nOT.AnalyserAudioLevelSampler = WebAudioAudioLevelSampler;\nOT.Archive = __webpack_require__(317);\nOT.ArchiveEvent = Events.ArchiveEvent;\nOT.ArchiveUpdatedEvent = Events.ArchiveUpdatedEvent;\nOT.AudioLevelTransformer = AudioLevelTransformer;\nOT.AudioLevelUpdatedEvent = Events.AudioLevelUpdatedEvent;\nOT.Capabilities = __webpack_require__(167);\nOT.Chrome = Chrome;\nOT.Connection = __webpack_require__(170);\nOT.ConnectionCapabilities = OT.Connection.Capabilities;\nOT.ConnectionEvent = Events.ConnectionEvent;\nObject.keys(logLevels).forEach(name => {\n OT[name.toUpperCase()] = logLevels[name].priority;\n});\nOT.NONE = 0;\nOT.debug = logging.debug;\nOT.error = logging.error;\nOT.info = logging.info;\nOT.log = logging.log;\nOT.warn = logging.warn;\nOT.DestroyedEvent = Events.DestroyedEvent;\nOT.EnvLoadedEvent = Events.EnvLoadedEvent; // TODO: re-expose old screenSharing api\n\nOT.Error = OTErrorClass;\nOT.Error.on(eventNames.EXCEPTION, exceptionEvent => {\n if (exceptionEvent.target === OT.Error) {\n // Rebind target to OT if it's set to OT.Error to preserve old behaviour.\n const exceptionEventClone = clone(exceptionEvent);\n exceptionEventClone.target = OT;\n OT.dispatchEvent(exceptionEventClone);\n } else {\n OT.dispatchEvent(exceptionEvent);\n }\n});\nOT.Event = Event;\nOT.ExceptionCodes = ExceptionCodes;\nOT.ExceptionEvent = Events.ExceptionEvent;\nOT.getDevices = __webpack_require__(318);\nOT.GetAudioLevelSampler = GetAudioLevelSampler;\nOT.HAS_REQUIREMENTS = 1;\nOT.IntervalRunner = IntervalRunner;\nOT.IssueReportedEvent = Events.IssueReportedEvent;\nOT.MediaStoppedEvent = Events.MediaStoppedEvent;\nOT.Microphone = Microphone;\nOT.NOT_HAS_REQUIREMENTS = 0;\nOT.PeerConnection = PeerConnection; // TODO: this is here for repel, but it doesn't belong here\n\nOT.PeerConnection.QOS = __webpack_require__(276).default;\nOT.Publisher = Publisher;\nOT.PublisherPeerConnection = PublisherPeerConnection;\nOT.PublishingState = PublishingState;\nOT.MuteForcedEvent = Events.MuteForcedEvent;\nOT.Session = Session;\nOT.SessionConnectEvent = Events.SessionConnectEvent;\nOT.SessionDisconnectEvent = Events.SessionDisconnectEvent;\nOT.SessionDispatcher = __webpack_require__(316);\nOT.Signal = __webpack_require__(314);\nOT.SignalEvent = Events.SignalEvent;\nOT.Stream = __webpack_require__(168);\nOT.StreamChannel = StreamChannel;\nOT.StreamEvent = Events.StreamEvent;\nOT.StreamPropertyChangedEvent = Events.StreamPropertyChangedEvent;\nOT.StreamUpdatedEvent = Events.StreamUpdatedEvent;\nOT.StylableComponent = StylableComponent;\nOT.Subscriber = Subscriber;\nOT.SubscriberPeerConnection = SubscriberPeerConnection;\nOT.SubscribingState = __webpack_require__(301);\nOT.VideoDimensionsChangedEvent = Events.VideoDimensionsChangedEvent;\nOT.VideoDisableWarningEvent = Events.VideoDisableWarningEvent;\nOT.VideoElement = VideoElementFacade;\nOT.VideoEnabledChangedEvent = Events.VideoEnabledChangedEvent;\nOT.VideoOrientation = VideoOrientation;\nOT.WidgetView = WidgetView;\nOT.getSupportedCodecs = getSupportedCodecs;\nOT._ = {\n AnalyticsHelper,\n getClientGuid: guidStorage.get,\n StaticConfig\n};\n/**\n *\n * The currently loaded version of OpenTok.js.\n *\n * @property {string} version\n * @readonly\n * @memberof OT\n */\n\nObject.defineProperty(OT, 'version', {\n value: staticConfig.version\n});\nconst properties = {};\n\nconst canPropertyBeMutated = property => mutableRuntimeProperties.includes(property);\n\nsetDeprecatedProperty(properties, 'version', {\n value: staticConfig.version,\n name: 'OT.properties.version',\n getWarning: 'Please use OT.version instead',\n warnOnSet: true,\n setWarning: 'Mutating version has no effect'\n});\nruntimeProperties.forEach(keyMap => {\n let value;\n let key;\n\n if (Array.isArray(keyMap)) {\n const propKey = keyMap[0],\n staticConfigKey = keyMap[1];\n key = propKey;\n value = staticConfig[staticConfigKey] || staticConfigKey;\n } else {\n value = staticConfig[keyMap];\n key = keyMap;\n }\n\n const isPropertyMutable = canPropertyBeMutated(key);\n setDeprecatedProperty(properties, key, {\n value,\n name: `OT.properties.${key}`,\n warnOnSet: true,\n canSet: isPropertyMutable,\n setWarning: isPropertyMutable ? `Mutating ${key} can cause side effects` : `Mutating ${key} has no effect`\n });\n});\nOT.properties = properties; // OT.addEventListener comes from eventing(OT)\n\nOT.audioContext = audioContext;\nOT.checkScreenSharingCapability = screenSharing.checkCapability;\nOT.checkSystemRequirements = systemRequirements.check;\nOT.components = {}; // OT.dispatchEvent comes from eventing(OT)\n// OT.emit comes from eventing(OT)\n\nOT.generateSimpleStateMachine = generateSimpleStateMachine;\nOT.getDevices = __webpack_require__(318);\nOT.getErrorTitleByCode = OTErrorClass.getTitleByCode;\nOT.getLogs = logging.getLogs; // This is misspelled in production too, being compatible here.\n\nOT.getStatsAdpater = __webpack_require__(272);\nOT.getStatsHelpers = __webpack_require__(75);\nOT.getUserMedia = getUserMedia;\nOT.handleJsException = OTErrorClass.handleJsException;\nOT.initPublisher = initPublisher;\nOT.setProxyUrl = __webpack_require__(139).setProxyUrl;\n\nOT.initSession = function (apiKey, sessionId, opt) {\n if (sessionId == null) {\n sessionId = apiKey;\n apiKey = null;\n } // Ugly hack, make sure OT.APIKEY is set\n // TODO: Yep, sure is ugly. It appears to be needed by raptor. We should fix this situation.\n // UPDATE: This hack is the only reason why we need to wrap the actual initSession.\n\n\n if (APIKEY.value.length === 0 && apiKey) {\n APIKEY.value = apiKey;\n OT.APIKEY = apiKey;\n }\n\n return initSession(apiKey, sessionId, opt);\n};\n\nconst deprecatedMessage = key => `${key} is deprecated and will be removed in a future version of OpenTok`;\n\nconst warnAndReturn = (name, value) => () => {\n // eslint-disable-next-line no-console\n console.warn(deprecatedMessage(name));\n return value;\n}; // discourage us and others from using these OT methods\n\n\nObject.defineProperties(OT, {\n isUnloaded: {\n get: warnAndReturn('OT.isUnloaded', EnvironmentLoader.isUnloaded)\n },\n onLoad: {\n get: warnAndReturn('OT.onLoad', EnvironmentLoader.onLoad)\n },\n onUnload: {\n get: warnAndReturn('OT.onUnload', EnvironmentLoader.onUnload)\n },\n overrideGuidStorage: {\n get: warnAndReturn('OT.overrideGuidStorage', guidStorage.override)\n }\n}); // OT.off comes from eventing(OT)\n// OT.on comes from eventing(OT)\n// OT.once comes from eventing(OT)\n// OT.removeEventListener comes from eventing(OT)\n// OT.trigger comes from eventing(OT)\n// Exposed here for partner usage.\n\nOT.pickScreenSharingHelper = screenSharing.pickHelper;\nOT.publishers = sessionObjects.publishers;\nOT.registerScreenSharingExtension = screenSharing.registerExtension;\nOT.registerScreenSharingExtensionHelper = screenSharing.registerExtensionHelper;\nOT.reportIssue = reportIssue;\nOT.sessions = sessionObjects.sessions;\nOT.setLogLevel = setLogLevel;\nOT.shouldLog = logging.shouldLog;\nOT.subscribers = sessionObjects.subscribers;\nOT.unblockAudio = __webpack_require__(165);\nOT.upgradeSystemRequirements = systemRequirements.upgrade; // Tidy up everything on unload\n\nEnvironmentLoader.onUnload(() => {\n sessionObjects.publishers.destroy();\n sessionObjects.subscribers.destroy();\n sessionObjects.sessions.destroy('unloaded');\n});\n/**\n * This method is deprecated. Use on() or once() instead.\n *\n * \n * Registers a method as an event listener for a specific event.\n *
\n *\n * \n * The OT object dispatches one type of event an exception
event. The\n * following code adds an event listener for the exception
event:\n *
\n *\n * \n * OT.addEventListener(\"exception\", exceptionHandler);\n *\n * function exceptionHandler(event) {\n * alert(\"exception event. \\n code == \" + event.code + \"\\n message == \" + event.message);\n * }\n *
\n *\n * \n * If a handler is not registered for an event, the event is ignored locally. If the event\n * listener function does not exist, the event is ignored locally.\n *
\n * \n * Throws an exception if the listener
name is invalid.\n *
\n *\n * @param {String} type The string identifying the type of event.\n *\n * @param {Function} listener The function to be invoked when the OT object dispatches the event.\n * @see on()\n * @see once()\n * @memberof OT\n * @method addEventListener\n *\n */\n\n/**\n * This method is deprecated. Use off() instead.\n *\n * \n * Removes an event listener for a specific event.\n *
\n *\n * \n * Throws an exception if the listener
name is invalid.\n *
\n *\n * @param {String} type The string identifying the type of event.\n *\n * @param {Function} listener The event listener function to remove.\n *\n * @see off()\n * @memberof OT\n * @method removeEventListener\n */\n\n/**\n* Adds an event handler function for one or more events.\n*\n* \n* The OT object dispatches one type of event an exception
event. The following\n* code adds an event\n* listener for the exception
event:\n*
\n*\n* \n* OT.on(\"exception\", function (event) {\n* // This is the event handler.\n* });\n*
\n*\n* You can also pass in a third context
parameter (which is optional) to define the\n* value of\n* this
in the handler method:
\n*\n* \n* OT.on(\"exception\",\n* function (event) {\n* // This is the event handler.\n* }),\n* session\n* );\n*
\n*\n* \n* If you do not add a handler for an event, the event is ignored locally.\n*
\n*\n* @param {String} type The string identifying the type of event.\n* @param {Function} handler The handler function to process the event. This function takes the event\n* object as a parameter.\n* @param {Object} context (Optional) Defines the value of this
in the event handler\n* function.\n*\n* @memberof OT\n* @method on\n* @see off()\n* @see once()\n* @see Events\n*/\n\n/**\n* Adds an event handler function for an event. Once the handler is called, the specified handler\n* method is\n* removed as a handler for this event. (When you use the OT.on()
method to add an event\n* handler, the handler\n* is not removed when it is called.) The OT.once()
method is the equivilent of\n* calling the OT.on()
\n* method and calling OT.off()
the first time the handler is invoked.\n*\n* \n* The following code adds a one-time event handler for the exception
event:\n*
\n*\n* \n* OT.once(\"exception\", function (event) {\n* console.log(event);\n* }\n*
\n*\n* You can also pass in a third context
parameter (which is optional) to define the\n* value of\n* this
in the handler method:
\n*\n* \n* OT.once(\"exception\",\n* function (event) {\n* // This is the event handler.\n* },\n* session\n* );\n*
\n*\n* \n* The method also supports an alternate syntax, in which the first parameter is an object that is a\n* hash map of\n* event names and handler functions and the second parameter (optional) is the context for this in\n* each handler:\n*
\n* \n* OT.once(\n* {exeption: function (event) {\n* // This is the event handler.\n* }\n* },\n* session\n* );\n*
\n*\n* @param {String} type The string identifying the type of event. You can specify multiple event\n* names in this string,\n* separating them with a space. The event handler will process the first occurence of the events.\n* After the first event,\n* the handler is removed (for all specified events).\n* @param {Function} handler The handler function to process the event. This function takes the event\n* object as a parameter.\n* @param {Object} context (Optional) Defines the value of this
in the event handler\n* function.\n*\n* @memberof OT\n* @method once\n* @see on()\n* @see once()\n* @see Events\n*/\n\n/**\n * Removes an event handler.\n *\n * Pass in an event name and a handler method, the handler is removed for that event:
\n *\n * OT.off(\"exceptionEvent\", exceptionEventHandler);
\n *\n * If you pass in an event name and no handler method, all handlers are removed for that\n * events:
\n *\n * OT.off(\"exceptionEvent\");
\n *\n * \n * The method also supports an alternate syntax, in which the first parameter is an object that is a\n * hash map of\n * event names and handler functions and the second parameter (optional) is the context for matching\n * handlers:\n *
\n * \n * OT.off(\n * {\n * exceptionEvent: exceptionEventHandler\n * },\n * this\n * );\n *
\n *\n * @param {String} type (Optional) The string identifying the type of event. You can use a space to\n * specify multiple events, as in \"eventName1 eventName2 eventName3\". If you pass in no\n * type
value (or other arguments), all event handlers are removed for the object.\n * @param {Function} handler (Optional) The event handler function to remove. If you pass in no\n * handler
, all event handlers are removed for the specified event type
.\n * @param {Object} context (Optional) If you specify a context
, the event handler is\n * removed for all specified events and handlers that use the specified context.\n *\n * @memberof OT\n * @method off\n * @see on()\n * @see once()\n * @see Events\n */\n\n/**\n * Dispatched by the OT class when the app encounters an exception.\n * Note that you set up an event handler for the exception
event by calling the\n * OT.on()
method.\n *\n * @name exception\n * @event\n * @borrows ExceptionEvent#message as this.message\n * @memberof OT\n * @see ExceptionEvent\n */\n\n/***/ }),\n/* 320 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\nvar content = __webpack_require__(321);\n\nif(typeof content === 'string') content = [[module.i, content, '']];\n\nvar transform;\nvar insertInto;\n\n\n\nvar options = {\"hmr\":true}\n\noptions.transform = transform\noptions.insertInto = undefined;\n\nvar update = __webpack_require__(335)(content, options);\n\nif(content.locals) module.exports = content.locals;\n\nif(false) {}\n\n/***/ }),\n/* 321 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(322)(false);\n// Imports\nvar urlEscape = __webpack_require__(323);\nvar ___CSS_LOADER_URL___0___ = urlEscape(__webpack_require__(324));\nvar ___CSS_LOADER_URL___1___ = urlEscape(__webpack_require__(325));\nvar ___CSS_LOADER_URL___2___ = urlEscape(__webpack_require__(326));\nvar ___CSS_LOADER_URL___3___ = urlEscape(__webpack_require__(327));\nvar ___CSS_LOADER_URL___4___ = urlEscape(__webpack_require__(328));\nvar ___CSS_LOADER_URL___5___ = urlEscape(__webpack_require__(329));\nvar ___CSS_LOADER_URL___6___ = urlEscape(__webpack_require__(330));\nvar ___CSS_LOADER_URL___7___ = urlEscape(__webpack_require__(331));\nvar ___CSS_LOADER_URL___8___ = urlEscape(__webpack_require__(332));\nvar ___CSS_LOADER_URL___9___ = urlEscape(__webpack_require__(333));\nvar ___CSS_LOADER_URL___10___ = urlEscape(__webpack_require__(334));\n\n// Module\nexports.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 letter-spacing: normal;\\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 letter-spacing: 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 -o-object-fit: cover;\\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 -o-object-fit: contain !important;\\n object-fit: contain !important;\\n }\\n}\\n\\n.OT_fit-mode-contain .OT_video-element {\\n -o-object-fit: contain;\\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 .OT_alert {\\n border-top-right-radius: 5px;\\n border-bottom-right-radius: 5px;\\n background-color: #333;\\n position: relative;\\n padding: 30px;\\n overflow: visible;\\n margin-left: 70px;\\n border-bottom-color: rgba(255, 255, 255, 0.1);\\n border-left-color: rgba(255, 255, 255, 0.1);\\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 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 .OT_alert-exclamation {\\n background-color: #9c1408;\\n background-image: linear-gradient(0deg, #dd0c0a 0%, #9c1408 100%);\\n box-shadow: 0 2px 2px rgba(0, 0, 0, 0.15);\\n border: 1px solid rgba(255, 255, 255, 0.3);\\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 .OT_alert-exclamation:after,\\n#tb_alert .OT_alert-exclamation: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 .OT_alert-exclamation:before {\\n border-left-color: #bc3428;\\n top: 39%;\\n}\\n\\n#tb_alert .OT_alert-body {\\n color: #c8c5c5;\\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 .continue-text {\\n margin-top: 12px\\n}\\n\\n#tb_alert > * a {\\n color: #a7d8df;\\n text-decoration: none;\\n}\\n\\n#tb_alert .OT_alert-header {\\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 .OT_alert-header::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 .OT_alert-close-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\", \"\"]);\n\n\n\n/***/ }),\n/* 322 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\nmodule.exports = function (useSourceMap) {\n var list = []; // return the list of modules as css string\n\n list.toString = function toString() {\n return this.map(function (item) {\n var content = cssWithMappingToString(item, useSourceMap);\n\n if (item[2]) {\n return '@media ' + item[2] + '{' + content + '}';\n } else {\n return content;\n }\n }).join('');\n }; // import a list of modules into the list\n\n\n list.i = function (modules, mediaQuery) {\n if (typeof modules === 'string') {\n modules = [[null, modules, '']];\n }\n\n var alreadyImportedModules = {};\n\n for (var i = 0; i < this.length; i++) {\n var id = this[i][0];\n\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n\n for (i = 0; i < modules.length; i++) {\n var item = modules[i]; // skip already imported module\n // this implementation is not 100% perfect for weird media query combinations\n // when a module is imported multiple times with different media queries.\n // I hope this will never occur (Hey this way we have smaller bundles)\n\n if (item[0] == null || !alreadyImportedModules[item[0]]) {\n if (mediaQuery && !item[2]) {\n item[2] = mediaQuery;\n } else if (mediaQuery) {\n item[2] = '(' + item[2] + ') and (' + mediaQuery + ')';\n }\n\n list.push(item);\n }\n }\n };\n\n return list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n var content = item[1] || '';\n var cssMapping = item[3];\n\n if (!cssMapping) {\n return content;\n }\n\n if (useSourceMap && typeof btoa === 'function') {\n var sourceMapping = toComment(cssMapping);\n var sourceURLs = cssMapping.sources.map(function (source) {\n return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */';\n });\n return [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n }\n\n return [content].join('\\n');\n} // Adapted from convert-source-map (MIT)\n\n\nfunction toComment(sourceMap) {\n // eslint-disable-next-line no-undef\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\n return '/*# ' + data + ' */';\n}\n\n/***/ }),\n/* 323 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function escape(url, needQuotes) {\n if (typeof url !== 'string') {\n return url;\n } // If url is already wrapped in quotes, remove them\n\n\n if (/^['\"].*['\"]$/.test(url)) {\n url = url.slice(1, -1);\n } // Should url be wrapped?\n // See https://drafts.csswg.org/css-values-3/#urls\n\n\n if (/[\"'() \\t\\n]/.test(url) || needQuotes) {\n return '\"' + url.replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"';\n }\n\n return url;\n};\n\n/***/ }),\n/* 324 */\n/***/ (function(module, exports) {\n\nmodule.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=\"\n\n/***/ }),\n/* 325 */\n/***/ (function(module, exports) {\n\nmodule.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==\"\n\n/***/ }),\n/* 326 */\n/***/ (function(module, exports) {\n\nmodule.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=\"\n\n/***/ }),\n/* 327 */\n/***/ (function(module, exports) {\n\nmodule.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==\"\n\n/***/ }),\n/* 328 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0yMCAtMjAgMjQwIDI0MCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4Mj0iMCIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjA4Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxIiB4Mj0iMCIgeTE9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMDgiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjE2Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgyPSIwIiB5MT0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii4xNiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuMzMiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iZSIgeDI9IjEiIHkxPSIxIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iLjMzIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIiBzdG9wLW9wYWNpdHk9Ii42NiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJmIiB4Mj0iMSIgeTI9IjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIuNjYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxtYXNrIGlkPSJnIj48ZyBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjQwIj48cGF0aCBzdHJva2U9InVybCgjYSkiIGQ9Ik04Ni42LTUwYTEwMCAxMDAgMCAwMTAgMTAwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDAgMTAwKSIvPjxwYXRoIHN0cm9rZT0idXJsKCNiKSIgZD0iTTg2LjYgNTBBMTAwIDEwMCAwIDAxMCAxMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2MpIiBkPSJNMCAxMDBhMTAwIDEwMCAwIDAxLTg2LjYtNTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2QpIiBkPSJNLTg2LjYgNTBhMTAwIDEwMCAwIDAxMC0xMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2UpIiBkPSJNLTg2LjYtNTBBMTAwIDEwMCAwIDAxMC0xMDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwMCAxMDApIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2YpIiBkPSJNMC0xMDBhMTAwIDEwMCAwIDAxODYuNiA1MCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAwIDEwMCkiLz48L2c+PC9tYXNrPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB4PSItMjAiIHk9Ii0yMCIgbWFzaz0idXJsKCNnKSIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==\"\n\n/***/ }),\n/* 329 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNDcxIDQ2NCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48bGluZWFyR3JhZGllbnQgaWQ9ImEiIHgxPSIwIiB4Mj0iMCIgeTE9IjAiIHkyPSIxIj48c3RvcCBvZmZzZXQ9IjY2LjY2JSIgc3RvcC1jb2xvcj0iI2ZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBmaWxsPSJ1cmwoI2EpIiBkPSJNNzkgMzA4YzE0LjI1LTYuNSA1NC4yNS0xOS43NSA3MS0yOSA5LTMuMjUgMjUtMjEgMjUtMjFzMy43NS0xMyAzLTIyYy0xLjc1LTYuNzUtMTUtNDMtMTUtNDMtMi41IDMtNC43NDEgMy4yNTktNyAxLTMuMjUtNy41LTIwLjUtNDQuNS0xNi01NyAxLjI1LTcuNSAxMC02IDEwLTYtMTEuMjUtMzMuNzUtOC02Ny04LTY3cy4wNzMtNy4zNDYgNi0xNWMtMy40OC42MzctOSA0LTkgNCAyLjU2My0xMS43MjcgMTUtMjEgMTUtMjEgLjE0OC0uMzEyLTEuMzIxLTEuNDU0LTEwIDEgMS41LTIuNzggMTYuNjc1LTguNjU0IDMwLTExIDMuNzg3LTkuMzYxIDEyLjc4Mi0xNy4zOTggMjItMjItMi4zNjUgMy4xMzMtMyA2LTMgNnMxNS42NDctOC4wODggNDEtNmMtMTkuNzUgMi0yNCA2LTI0IDZzNzQuNS0xMC43NSAxMDQgMzdjNy41IDkuNSAyNC43NSA1NS43NSAxMCA4OSAzLjc1LTEuNSA0LjUtNC41IDkgMSAuMjUgMTQuNzUtMTEuNSA2My0xOSA2Mi0yLjc1IDEtNC0zLTQtMy0xMC43NSAyOS41LTE0IDM4LTE0IDM4LTIgNC4yNS0zLjc1IDE4LjUtMSAyMiAxLjI1IDQuNSAyMyAyMyAyMyAyM2wxMjcgNTNjMzcgMzUgMjMgMTM1IDIzIDEzNUwwIDQ2NHMtMy05Ni43NSAxNC0xMjBjNS4yNS02LjI1IDIxLjc1LTE5Ljc1IDY1LTM2eiIvPjwvc3ZnPg==\"\n\n/***/ }),\n/* 330 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzkgODYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTkuNzU3IDQwLjkyNGMzLjczOC01LjE5MSAxMi43MTEtNC4zMDggMTIuNzExLTQuMzA4IDIuMjIzIDMuMDE0IDUuMTI2IDI0LjU4NiAzLjYyNCAyOC43MTgtMS40MDEgMS4zMDEtMTEuNjExIDEuNjI5LTEzLjM4LTEuNDM2LTEuMjI2LTguODA0LTIuOTU1LTIyLjk3NS0yLjk1NS0yMi45NzV6bTU4Ljc4NSAwYy0zLjczNy01LjE5MS0xMi43MTEtNC4zMDgtMTIuNzExLTQuMzA4LTIuMjIzIDMuMDE0LTUuMTI2IDI0LjU4Ni0zLjYyNCAyOC43MTggMS40MDEgMS4zMDEgMTEuNjExIDEuNjI5IDEzLjM4LTEuNDM2IDEuMjI2LTguODA0IDIuOTU1LTIyLjk3NSAyLjk1NS0yMi45NzV6Ii8+PHBhdGggZD0iTTY4LjY0NyA1OC42Yy43MjktNC43NTMgMi4zOC05LjU2MSAyLjM4LTE0LjgwNCAwLTIxLjQxMi0xNC4xMTUtMzguNzctMzEuNTI4LTM4Ljc3LTE3LjQxMiAwLTMxLjUyNyAxNy4zNTgtMzEuNTI3IDM4Ljc3IDAgNC41NDEuNTE1IDguOTM2IDEuODAyIDEyLjk1IDEuNjk4IDUuMjk1LTUuNTQyIDYuOTkxLTYuNjE2IDIuMDczQzIuNDEgNTUuMzk0IDAgNTEuNzg3IDAgNDguMTAzIDAgMjEuNTM2IDE3LjY4NSAwIDM5LjUgMCA2MS4zMTYgMCA3OSAyMS41MzYgNzkgNDguMTAzYzAgLjcxOC0yLjg5OSA5LjY5My0zLjI5MiAxMS40MDgtLjc1NCAzLjI5My03Ljc1MSAzLjU4OS03LjA2MS0uOTEyeiIvPjxwYXRoIGQ9Ik01LjA4NCA1MS4zODVjLS44MDQtMy43ODIuNTY5LTcuMzM1IDMuMTM0LTcuOTIxIDIuNjM2LS42MDMgNS40ODUgMi4xNSA2LjI4OSA2LjEzMi43OTcgMy45NDgtLjc1MiA3LjQ1Ny0zLjM4OCA3Ljg1OS0yLjU2Ni4zOTEtNS4yMzctMi4zMTgtNi4wMzQtNi4wN3ptNjguODM0IDBjLjgwNC0zLjc4Mi0uNTY4LTcuMzM1LTMuMTMzLTcuOTIxLTIuNjM2LS42MDMtNS40ODUgMi4xNS02LjI4OSA2LjEzMi0uNzk3IDMuOTQ4Ljc1MiA3LjQ1NyAzLjM4OSA3Ljg1OSAyLjU2NS4zOTEgNS4yMzctMi4zMTggNi4wMzQtNi4wN3ptLTIuMDM4IDguMjg4Yy0uOTI2IDE5LjY1OS0xNS4xMTIgMjQuNzU5LTI1Ljg1OSAyMC40NzUtNS40MDUtLjYwNi0zLjAzNCAxLjI2Mi0zLjAzNCAxLjI2MiAxMy42NjEgMy41NjIgMjYuMTY4IDMuNDk3IDMxLjI3My0yMC41NDktLjU4NS00LjUxMS0yLjM3OS0xLjE4Ny0yLjM3OS0xLjE4N3oiLz48cGF0aCBkPSJNNDEuNjYyIDc4LjQyMmw3LjU1My41NWMxLjE5Mi4xMDcgMi4xMiAxLjE1MyAyLjA3MiAyLjMzNWwtLjEwOSAyLjczOGMtLjA0NyAxLjE4Mi0xLjA1MSAyLjA1NC0yLjI0MyAxLjk0NmwtNy41NTMtLjU1Yy0xLjE5MS0uMTA3LTIuMTE5LTEuMTUzLTIuMDcyLTIuMzM1bC4xMDktMi43MzdjLjA0Ny0xLjE4MiAxLjA1Mi0yLjA1NCAyLjI0My0xLjk0N3oiLz48L2c+PC9zdmc+\"\n\n/***/ }),\n/* 331 */\n/***/ (function(module, exports) {\n\nmodule.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==\"\n\n/***/ }),\n/* 332 */\n/***/ (function(module, exports) {\n\nmodule.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\"\n\n/***/ }),\n/* 333 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjkwIj48ZGVmcz48cGF0aCBkPSJNNjcgMTJMNi40NDggNzIuNTUyIDAgMzFWMThMMjYgMGw0MSAxMnptMyA3bDYgNDctMjkgMTgtMzUuNTAyLTYuNDk4TDcwIDE5eiIgaWQ9ImEiLz48L2RlZnM+PHJlY3Qgd2lkdGg9IjE1MCIgaGVpZ2h0PSI5MCIgcng9IjM1IiByeT0iNDUiIG9wYWNpdHk9Ii41Ii8+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNikiPjxtYXNrIGlkPSJiIiBmaWxsPSIjZmZmIj48dXNlIHhsaW5rOmhyZWY9IiNhIi8+PC9tYXNrPjxwYXRoIGQ9Ik0zOS4yNDkgNTEuMzEyYy42OTcgMTAuMzcgMi43ODUgMTcuODk3IDUuMjUxIDE3Ljg5NyAzLjAzOCAwIDUuNS0xMS40MTcgNS41LTI1LjVzLTIuNDYyLTI1LjUtNS41LTI1LjVjLTIuNTEgMC00LjYyOCA3Ljc5Ny01LjI4NyAxOC40NTNBOC45ODkgOC45ODkgMCAwMTQzIDQ0YTguOTg4IDguOTg4IDAgMDEtMy43NTEgNy4zMTJ6TTIwLjk4NSAzMi4yMjRsMTUuNzQ2LTE2Ljg3N2E3LjM4NSA3LjM4NSAwIDAxMTAuMzc0LS40MkM1MS43MDIgMTkuMTE0IDU0IDI5LjIwOCA1NCA0NS4yMDhjMCAxNC41MjctMi4zNDMgMjMuODgtNy4wMyAyOC4wNThhNy4yOCA3LjI4IDAgMDEtMTAuMTY4LS40NjhMMjAuNDA1IDU1LjIyNEgxMmE1IDUgMCAwMS01LTV2LTEzYTUgNSAwIDAxNS01aDguOTg1eiIgZmlsbD0iI0ZGRiIgbWFzaz0idXJsKCNiKSIvPjwvZz48cGF0aCBkPSJNMTA2LjUgMTMuNUw0NC45OTggNzUuMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg==\"\n\n/***/ }),\n/* 334 */\n/***/ (function(module, exports) {\n\nmodule.exports = \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTciIGhlaWdodD0iOTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxwYXRoIGQ9Ik03MCAxMkw5LjQ0OCA3Mi41NTIgMCA2MmwzLTQ0TDI5IDBsNDEgMTJ6bTggMmwxIDUyLTI5IDE4LTM1LjUwMi02LjQ5OEw3OCAxNHoiIGlkPSJhIi8+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAzKSI+PG1hc2sgaWQ9ImIiIGZpbGw9IiNmZmYiPjx1c2UgeGxpbms6aHJlZj0iI2EiLz48L21hc2s+PHBhdGggZD0iTTkuMTEgMjAuOTY4SDQ4LjFhNSA1IDAgMDE1IDVWNTguMThhNSA1IDAgMDEtNSA1SDkuMTFhNSA1IDAgMDEtNS01VjI1Ljk3YTUgNSAwIDAxNS01em00Ny4wOCAxMy4zOTRjMC0uMzQ1IDUuNDcyLTMuMTU5IDE2LjQxNS04LjQ0M2EzIDMgMCAwMTQuMzA0IDIuNzAydjI2LjgzNWEzIDMgMCAwMS00LjMwNSAyLjcwMWMtMTAuOTQyLTUuMjg2LTE2LjQxMy04LjEtMTYuNDEzLTguNDQ2VjM0LjM2MnoiIGZpbGw9IiNGRkYiIG1hc2s9InVybCgjYikiLz48L2c+PHBhdGggZD0iTTgxLjUgMTYuNUwxOS45OTggNzguMDAyIiBzdHJva2U9IiNGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvc3ZnPg==\"\n\n/***/ }),\n/* 335 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n\nvar stylesInDom = {};\n\nvar\tmemoize = function (fn) {\n\tvar memo;\n\n\treturn function () {\n\t\tif (typeof memo === \"undefined\") memo = fn.apply(this, arguments);\n\t\treturn memo;\n\t};\n};\n\nvar isOldIE = memoize(function () {\n\t// Test for IE <= 9 as proposed by Browserhacks\n\t// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805\n\t// Tests for existence of standard globals is to allow style-loader\n\t// to operate correctly into non-standard environments\n\t// @see https://github.com/webpack-contrib/style-loader/issues/177\n\treturn window && document && document.all && !window.atob;\n});\n\nvar getTarget = function (target, parent) {\n if (parent){\n return parent.querySelector(target);\n }\n return document.querySelector(target);\n};\n\nvar getElement = (function (fn) {\n\tvar memo = {};\n\n\treturn function(target, parent) {\n // If passing function in options, then use it for resolve \"head\" element.\n // Useful for Shadow Root style i.e\n // {\n // insertInto: function () { return document.querySelector(\"#foo\").shadowRoot }\n // }\n if (typeof target === 'function') {\n return target();\n }\n if (typeof memo[target] === \"undefined\") {\n\t\t\tvar styleTarget = getTarget.call(this, target, parent);\n\t\t\t// Special case to return head of iframe instead of iframe itself\n\t\t\tif (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n\t\t\t\ttry {\n\t\t\t\t\t// This will throw an exception if access to iframe is blocked\n\t\t\t\t\t// due to cross-origin restrictions\n\t\t\t\t\tstyleTarget = styleTarget.contentDocument.head;\n\t\t\t\t} catch(e) {\n\t\t\t\t\tstyleTarget = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmemo[target] = styleTarget;\n\t\t}\n\t\treturn memo[target]\n\t};\n})();\n\nvar singleton = null;\nvar\tsingletonCounter = 0;\nvar\tstylesInsertedAtTop = [];\n\nvar\tfixUrls = __webpack_require__(336);\n\nmodule.exports = function(list, options) {\n\tif (typeof DEBUG !== \"undefined\" && DEBUG) {\n\t\tif (typeof document !== \"object\") throw new Error(\"The style-loader cannot be used in a non-browser environment\");\n\t}\n\n\toptions = options || {};\n\n\toptions.attrs = typeof options.attrs === \"object\" ? options.attrs : {};\n\n\t// Force single-tag solution on IE6-9, which has a hard limit on the # of