From fb6ad8cffd1358c6fad08f38f6e9072c4a922c46 Mon Sep 17 00:00:00 2001 From: George Politis Date: Wed, 20 May 2015 15:55:30 +0200 Subject: [PATCH] Updates app.bundle.js and restores latest sdp-interop. --- index.html | 2 +- libs/app.bundle.js | 1560 +++++++++++++++++++++++--------------------- 2 files changed, 829 insertions(+), 733 deletions(-) diff --git a/index.html b/index.html index 670a48dbc..563cab48d 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,7 @@ - + diff --git a/libs/app.bundle.js b/libs/app.bundle.js index 514737391..f04fb4a21 100644 --- a/libs/app.bundle.js +++ b/libs/app.bundle.js @@ -1,63 +1,4 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.APP=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (!draining) { + setTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],65:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}],66:[function(require,module,exports){ // i18next, v1.7.7 // Copyright (c)2014 Jan Mühlemann (jamuhl). // Distributed under MIT license @@ -21262,7 +21598,7 @@ module.exports = XMPP; i18n.options = o; })(); -},{"jquery":"jquery"}],66:[function(require,module,exports){ +},{"jquery":"jquery"}],67:[function(require,module,exports){ // Top level file is just a mixin of submodules & constants 'use strict'; @@ -21277,7 +21613,7 @@ var pako = {}; assign(pako, deflate, inflate, constants); module.exports = pako; -},{"./lib/deflate":67,"./lib/inflate":68,"./lib/utils/common":69,"./lib/zlib/constants":72}],67:[function(require,module,exports){ +},{"./lib/deflate":68,"./lib/inflate":69,"./lib/utils/common":70,"./lib/zlib/constants":73}],68:[function(require,module,exports){ 'use strict'; @@ -21642,7 +21978,7 @@ exports.Deflate = Deflate; exports.deflate = deflate; exports.deflateRaw = deflateRaw; exports.gzip = gzip; -},{"./utils/common":69,"./utils/strings":70,"./zlib/deflate.js":74,"./zlib/messages":79,"./zlib/zstream":81}],68:[function(require,module,exports){ +},{"./utils/common":70,"./utils/strings":71,"./zlib/deflate.js":75,"./zlib/messages":80,"./zlib/zstream":82}],69:[function(require,module,exports){ 'use strict'; @@ -22011,7 +22347,7 @@ exports.inflate = inflate; exports.inflateRaw = inflateRaw; exports.ungzip = inflate; -},{"./utils/common":69,"./utils/strings":70,"./zlib/constants":72,"./zlib/gzheader":75,"./zlib/inflate.js":77,"./zlib/messages":79,"./zlib/zstream":81}],69:[function(require,module,exports){ +},{"./utils/common":70,"./utils/strings":71,"./zlib/constants":73,"./zlib/gzheader":76,"./zlib/inflate.js":78,"./zlib/messages":80,"./zlib/zstream":82}],70:[function(require,module,exports){ 'use strict'; @@ -22114,7 +22450,7 @@ exports.setTyped = function (on) { }; exports.setTyped(TYPED_OK); -},{}],70:[function(require,module,exports){ +},{}],71:[function(require,module,exports){ // String encode/decode helpers 'use strict'; @@ -22301,7 +22637,7 @@ exports.utf8border = function(buf, max) { return (pos + _utf8len[buf[pos]] > max) ? pos : max; }; -},{"./common":69}],71:[function(require,module,exports){ +},{"./common":70}],72:[function(require,module,exports){ 'use strict'; // Note: adler32 takes 12% for level 0 and 2% for level 6. @@ -22334,7 +22670,7 @@ function adler32(adler, buf, len, pos) { module.exports = adler32; -},{}],72:[function(require,module,exports){ +},{}],73:[function(require,module,exports){ module.exports = { /* Allowed flush values; see deflate() and inflate() below for details */ @@ -22382,7 +22718,7 @@ module.exports = { Z_DEFLATED: 8 //Z_NULL: null // Use -1 or null inline, depending on var type }; -},{}],73:[function(require,module,exports){ +},{}],74:[function(require,module,exports){ 'use strict'; // Note: we can't get significant speed boost here. @@ -22424,7 +22760,7 @@ function crc32(crc, buf, len, pos) { module.exports = crc32; -},{}],74:[function(require,module,exports){ +},{}],75:[function(require,module,exports){ 'use strict'; var utils = require('../utils/common'); @@ -24190,7 +24526,7 @@ exports.deflatePending = deflatePending; exports.deflatePrime = deflatePrime; exports.deflateTune = deflateTune; */ -},{"../utils/common":69,"./adler32":71,"./crc32":73,"./messages":79,"./trees":80}],75:[function(require,module,exports){ +},{"../utils/common":70,"./adler32":72,"./crc32":74,"./messages":80,"./trees":81}],76:[function(require,module,exports){ 'use strict'; @@ -24231,7 +24567,7 @@ function GZheader() { } module.exports = GZheader; -},{}],76:[function(require,module,exports){ +},{}],77:[function(require,module,exports){ 'use strict'; // See state defs from inflate.js @@ -24558,7 +24894,7 @@ module.exports = function inflate_fast(strm, start) { return; }; -},{}],77:[function(require,module,exports){ +},{}],78:[function(require,module,exports){ 'use strict'; @@ -26062,7 +26398,7 @@ exports.inflateSync = inflateSync; exports.inflateSyncPoint = inflateSyncPoint; exports.inflateUndermine = inflateUndermine; */ -},{"../utils/common":69,"./adler32":71,"./crc32":73,"./inffast":76,"./inftrees":78}],78:[function(require,module,exports){ +},{"../utils/common":70,"./adler32":72,"./crc32":74,"./inffast":77,"./inftrees":79}],79:[function(require,module,exports){ 'use strict'; @@ -26389,7 +26725,7 @@ module.exports = function inflate_table(type, lens, lens_index, codes, table, ta return 0; }; -},{"../utils/common":69}],79:[function(require,module,exports){ +},{"../utils/common":70}],80:[function(require,module,exports){ 'use strict'; module.exports = { @@ -26403,7 +26739,7 @@ module.exports = { '-5': 'buffer error', /* Z_BUF_ERROR (-5) */ '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ }; -},{}],80:[function(require,module,exports){ +},{}],81:[function(require,module,exports){ 'use strict'; @@ -27603,7 +27939,7 @@ exports._tr_stored_block = _tr_stored_block; exports._tr_flush_block = _tr_flush_block; exports._tr_tally = _tr_tally; exports._tr_align = _tr_align; -},{"../utils/common":69}],81:[function(require,module,exports){ +},{"../utils/common":70}],82:[function(require,module,exports){ 'use strict'; @@ -27633,9 +27969,9 @@ function ZStream() { } module.exports = ZStream; -},{}],82:[function(require,module,exports){ +},{}],83:[function(require,module,exports){ module.exports = require('./lib/retry'); -},{"./lib/retry":83}],83:[function(require,module,exports){ +},{"./lib/retry":84}],84:[function(require,module,exports){ var RetryOperation = require('./retry_operation'); exports.operation = function(options) { @@ -27686,7 +28022,7 @@ exports._createTimeout = function(attempt, opts) { return timeout; }; -},{"./retry_operation":84}],84:[function(require,module,exports){ +},{"./retry_operation":85}],85:[function(require,module,exports){ function RetryOperation(timeouts) { this._timeouts = timeouts; this._fn = null; @@ -27796,7 +28132,7 @@ RetryOperation.prototype.mainError = function() { return mainError; }; -},{}],85:[function(require,module,exports){ +},{}],86:[function(require,module,exports){ module.exports = function arrayEquals(array) { // if the other array is a falsy value, return if (!array) @@ -27822,27 +28158,32 @@ module.exports = function arrayEquals(array) { } -},{}],86:[function(require,module,exports){ +},{}],87:[function(require,module,exports){ exports.Interop = require('./interop'); -},{"./interop":87}],87:[function(require,module,exports){ +},{"./interop":88}],88:[function(require,module,exports){ +"use strict"; + var transform = require('./transform'); var arrayEquals = require('./array-equals'); -function Interop() { } +function Interop() { + + /** + * This map holds the most recent Unified Plan offer/answer SDP that was + * converted to Plan B, with the SDP type ('offer' or 'answer') as keys and + * the SDP string as values. + * + * @type {{}} + */ + this.cache = {}; +} + module.exports = Interop; -/** - * This map holds the most recent Plan A offer/answer SDP that was converted - * to Plan B, with the SDP type ('offer' or 'answer') as keys and the SDP - * string as values. - * - * @type {{}} - */ -var cache = {}; /** - * This method transforms a Plan A SDP to an equivalent Plan B SDP. A + * This method transforms a Unified Plan SDP to an equivalent Plan B SDP. A * PeerConnection wrapper transforms the SDP to Plan B before passing it to the * application. * @@ -27869,110 +28210,122 @@ Interop.prototype.toPlanB = function(desc) { return desc; } - // Try some heuristics to "make sure" this is a Plan A SDP. Plan B SDP has - // a video, an audio and a data "channel" at most. + // Try some heuristics to "make sure" this is a Unified Plan SDP. Plan B + // SDP has a video, an audio and a data "channel" at most. if (session.media.length <= 3 && session.media.every(function(m) { return ['video', 'audio', 'data'].indexOf(m.mid) !== -1; })) { - console.warn('This description does not look like Plan A.'); + console.warn('This description does not look like Unified Plan.'); return desc; } //#endregion - // Plan A SDP is our "precious". Cache it for later use in the Plan B -> - // Plan A transformation. - cache[desc.type] = desc.sdp; + // Unified Plan SDP is our "precious". Cache it for later use in the Plan B + // -> Unified Plan transformation. + this.cache[desc.type] = desc.sdp; - //#region Convert from Plan A to Plan B. + //#region Convert from Unified Plan to Plan B. // We rebuild the session.media array. var media = session.media; session.media = []; // Associative array that maps channel types to channel objects for fast - // access to channel objects by their type, e.g. channels['audio']->channel + // access to channel objects by their type, e.g. type2bl['audio']->channel // obj. - var channels = {}; + var type2bl = {}; // Used to build the group:BUNDLE value after the channels construction // loop. var types = []; - // Implode the Plan A m-lines/tracks into Plan B "channels". - media.forEach(function(mLine) { + // Implode the Unified Plan m-lines/tracks into Plan B channels. + media.forEach(function(unifiedLine) { // rtcp-mux is required in the Plan B SDP. - if (typeof mLine.rtcpMux !== 'string' || - mLine.rtcpMux !== 'rtcp-mux') { + if ((typeof unifiedLine.rtcpMux !== 'string' || + unifiedLine.rtcpMux !== 'rtcp-mux') && + unifiedLine.direction !== 'inactive') { throw new Error('Cannot convert to Plan B because m-lines ' + 'without the rtcp-mux attribute were found.'); } - // If we don't have a channel for this mLine.type, then use this mLine + if (unifiedLine.type === 'application') { + session.media.push(unifiedLine); + types.push(unifiedLine.mid); + return; + } + + // If we don't have a channel for this unifiedLine.type, then use this unifiedLine // as the channel basis. - if (typeof channels[mLine.type] === 'undefined') { - channels[mLine.type] = mLine; + if (typeof type2bl[unifiedLine.type] === 'undefined') { + type2bl[unifiedLine.type] = unifiedLine; } // Add sources to the channel and handle a=msid. - if (typeof mLine.sources === 'object') { - Object.keys(mLine.sources).forEach(function(ssrc) { + if (typeof unifiedLine.sources === 'object') { + Object.keys(unifiedLine.sources).forEach(function(ssrc) { + if (typeof type2bl[unifiedLine.type].sources !== 'object') + type2bl[unifiedLine.type].sources = {}; + // Assign the sources to the channel. - channels[mLine.type].sources[ssrc] = mLine.sources[ssrc]; - - // In Plan B the msid is an SSRC attribute. Also, we don't care - // about the obsolete label and mslabel attributes. - channels[mLine.type].sources[ssrc].msid = mLine.msid; + type2bl[unifiedLine.type].sources[ssrc] = unifiedLine.sources[ssrc]; + if (typeof unifiedLine.msid !== 'undefined') { + // In Plan B the msid is an SSRC attribute. Also, we don't + // care about the obsolete label and mslabel attributes. + // + // Note that it is not guaranteed that the unifiedLine will have + // an msid. recvonly channels in particular don't have one. + type2bl[unifiedLine.type].sources[ssrc].msid = unifiedLine.msid; + } // NOTE ssrcs in ssrc groups will share msids, as // draft-uberti-rtcweb-plan-00 mandates. }); } // Add ssrc groups to the channel. - if (typeof mLine.ssrcGroups !== 'undefined' && - Array.isArray(mLine.ssrcGroups)) { + if (typeof unifiedLine.ssrcGroups !== 'undefined' && + Array.isArray(unifiedLine.ssrcGroups)) { // Create the ssrcGroups array, if it's not defined. - if (typeof channel.ssrcGroups === 'undefined' || - !Array.isArray(channel.ssrcGroups)) { - channel.ssrcGroups = []; + if (typeof type2bl[unifiedLine.type].ssrcGroups === 'undefined' || + !Array.isArray(type2bl[unifiedLine.type].ssrcGroups)) { + type2bl[unifiedLine.type].ssrcGroups = []; } - channel.ssrcGroups = channel.ssrcGroups.concat(mLine.ssrcGroups); + type2bl[unifiedLine.type].ssrcGroups = type2bl[unifiedLine.type].ssrcGroups.concat(unifiedLine.ssrcGroups); } - if (channels[mLine.type] === mLine) { + if (type2bl[unifiedLine.type] === unifiedLine) { // Copy ICE related stuff from the principal media line. - mLine.candidates = media[0].candidates; - mLine.iceUfrag = media[0].iceUfrag; - mLine.icePwd = media[0].icePwd; - mLine.fingerprint = media[0].fingerprint; + unifiedLine.candidates = media[0].candidates; + unifiedLine.iceUfrag = media[0].iceUfrag; + unifiedLine.icePwd = media[0].icePwd; + unifiedLine.fingerprint = media[0].fingerprint; // Plan B mids are in ['audio', 'video', 'data'] - mLine.mid = mLine.type; + unifiedLine.mid = unifiedLine.type; // Plan B doesn't support/need the bundle-only attribute. - delete mLine.bundleOnly; + delete unifiedLine.bundleOnly; // In Plan B the msid is an SSRC attribute. - delete mLine.msid; + delete unifiedLine.msid; // Used to build the group:BUNDLE value after this loop. - types.push(mLine.type); + types.push(unifiedLine.type); // Add the channel to the new media array. - session.media.push(mLine); + session.media.push(unifiedLine); } }); // We regenerate the BUNDLE group with the new mids. - session.groups.every(function(group) { + session.groups.some(function(group) { if (group.type === 'BUNDLE') { group.mids = types.join(' '); - return false; - } else { return true; } }); @@ -27994,13 +28347,14 @@ Interop.prototype.toPlanB = function(desc) { }; /** - * This method transforms a Plan B SDP to an equivalent Plan A SDP. A - * PeerConnection wrapper transforms the SDP to Plan A before passing it to FF. + * This method transforms a Plan B SDP to an equivalent Unified Plan SDP. A + * PeerConnection wrapper transforms the SDP to Unified Plan before passing it + * to FF. * * @param desc * @returns {*} */ -Interop.prototype.toPlanA = function(desc) { +Interop.prototype.toUnifiedPlan = function(desc) { //#region Preliminary input validation. @@ -28028,7 +28382,7 @@ Interop.prototype.toPlanA = function(desc) { return desc; } - // Make sure this Plan B SDP can be converted to a Plan A SDP. + // Make sure this Plan B SDP can be converted to a Unified Plan SDP. var mids = []; session.media.forEach(function(m) { mids.push(m.mid); @@ -28044,73 +28398,79 @@ Interop.prototype.toPlanA = function(desc) { } if (!hasBundle) { - throw new Error("Cannot convert to Plan A because m-lines that are " + - "not bundled were found."); + throw new Error("Cannot convert to Unified Plan because m-lines that" + + " are not bundled were found."); } //#endregion - //#region Convert from Plan B to Plan A. + //#region Convert from Plan B to Unified Plan. // Unfortunately, a Plan B offer/answer doesn't have enough information to - // rebuild an equivalent Plan A offer/answer. + // rebuild an equivalent Unified Plan offer/answer. // - // For example, if this is a local answer (in Plan A style) that we convert - // to Plan B prior to handing it over to the application (the + // For example, if this is a local answer (in Unified Plan style) that we + // convert to Plan B prior to handing it over to the application (the // PeerConnection wrapper called us, for instance, after a successful // createAnswer), we want to remember the m-line at which we've seen the // (local) SSRC. That's because when the application wants to do call the // SLD method, forcing us to do the inverse transformation (from Plan B to - // Plan A), we need to know to which m-line to assign the (local) SSRC. We - // also need to know all the other m-lines that the original answer had and - // include them in the transformed answer as well. + // Unified Plan), we need to know to which m-line to assign the (local) + // SSRC. We also need to know all the other m-lines that the original + // answer had and include them in the transformed answer as well. // // Another example is if this is a remote offer that we convert to Plan B // prior to giving it to the application, we want to remember the mid at // which we've seen the (remote) SSRC. // - // In the iteration that follows, we use the cached Plan A (if it exists) - // to assign mids to ssrcs. + // In the iteration that follows, we use the cached Unified Plan (if it + // exists) to assign mids to ssrcs. var cached; - if (typeof cache[desc.type] !== 'undefined') { - cached = transform.parse(cache[desc.type]); + if (typeof this.cache[desc.type] !== 'undefined') { + cached = transform.parse(this.cache[desc.type]); } // A helper map that sends mids to m-line objects. We use it later to - // rebuild the Plan A style session.media array. - var media = {}; - session.media.forEach(function(channel) { - if (typeof channel.rtcpMux !== 'string' || - channel.rtcpMux !== 'rtcp-mux') { - throw new Error("Cannot convert to Plan A because m-lines " + + // rebuild the Unified Plan style session.media array. + var mid2ul = {}; + session.media.forEach(function(bLine) { + if ((typeof bLine.rtcpMux !== 'string' || + bLine.rtcpMux !== 'rtcp-mux') && + bLine.direction !== 'inactive') { + throw new Error("Cannot convert to Unified Plan because m-lines " + "without the rtcp-mux attribute were found."); } + if (bLine.type === 'application') { + mid2ul[bLine.mid] = bLine; + return; + } + // With rtcp-mux and bundle all the channels should have the same ICE // stuff. - var sources = channel.sources; - var ssrcGroups = channel.ssrcGroups; - var candidates = channel.candidates; - var iceUfrag = channel.iceUfrag; - var icePwd = channel.icePwd; - var fingerprint = channel.fingerprint; - var port = channel.port; + var sources = bLine.sources; + var ssrcGroups = bLine.ssrcGroups; + var candidates = bLine.candidates; + var iceUfrag = bLine.iceUfrag; + var icePwd = bLine.icePwd; + var fingerprint = bLine.fingerprint; + var port = bLine.port; - // We'll use the "channel" object as a prototype for each new "mLine" + // We'll use the "bLine" object as a prototype for each new "mLine" // that we create, but first we need to clean it up a bit. - delete channel.sources; - delete channel.ssrcGroups; - delete channel.candidates; - delete channel.iceUfrag; - delete channel.icePwd; - delete channel.fingerprint; - delete channel.port; - delete channel.mid; + delete bLine.sources; + delete bLine.ssrcGroups; + delete bLine.candidates; + delete bLine.iceUfrag; + delete bLine.icePwd; + delete bLine.fingerprint; + delete bLine.port; + delete bLine.mid; // inverted ssrc group map - var invertedGroups = {}; + var ssrc2group = {}; if (typeof ssrcGroups !== 'undefined' && Array.isArray(ssrcGroups)) { ssrcGroups.forEach(function (ssrcGroup) { @@ -28123,105 +28483,114 @@ Interop.prototype.toPlanA = function(desc) { if (typeof ssrcGroup.ssrcs !== 'undefined' && Array.isArray(ssrcGroup.ssrcs)) { ssrcGroup.ssrcs.forEach(function (ssrc) { - if (typeof invertedGroups[ssrc] === 'undefined') { - invertedGroups[ssrc] = []; + if (typeof ssrc2group[ssrc] === 'undefined') { + ssrc2group[ssrc] = []; } - invertedGroups[ssrc].push(ssrcGroup); + ssrc2group[ssrc].push(ssrcGroup); }); } }); } // ssrc to m-line index. - var mLines = {}; + var ssrc2ml = {}; if (typeof sources === 'object') { // Explode the Plan B channel sources with one m-line per source. Object.keys(sources).forEach(function(ssrc) { - var mLine; - if (typeof invertedGroups[ssrc] !== 'undefined' && - Array.isArray(invertedGroups[ssrc])) { - invertedGroups[ssrc].every(function (ssrcGroup) { + // The (unified) m-line for this SSRC. We either create it from + // scratch or, if it's a grouped SSRC, we re-use a related + // mline. In other words, if the source is grouped with another + // source, put the two together in the same m-line. + var unifiedLine; + if (typeof ssrc2group[ssrc] !== 'undefined' && + Array.isArray(ssrc2group[ssrc])) { + ssrc2group[ssrc].some(function (ssrcGroup) { // ssrcGroup.ssrcs *is* an Array, no need to check // again here. - return ssrcGroup.ssrcs.every(function (related) { - if (typeof mLines[related] === 'object') { - mLine = mLines[related]; - return false; - } else { + return ssrcGroup.ssrcs.some(function (related) { + if (typeof ssrc2ml[related] === 'object') { + unifiedLine = ssrc2ml[related]; return true; } }); }); } - if (typeof mLine === 'object') { + if (typeof unifiedLine === 'object') { // the m-line already exists. Just add the source. - mLine.sources[ssrc] = sources[ssrc]; + unifiedLine.sources[ssrc] = sources[ssrc]; delete sources[ssrc].msid; } else { - // Use the "channel" as a prototype for the "mLine". - mLine = Object.create(channel); - mLines[ssrc] = mLine; + // Use the "bLine" as a prototype for the "unifiedLine". + unifiedLine = Object.create(bLine); + ssrc2ml[ssrc] = unifiedLine; - // Assign the msid of the source to the m-line. - mLine.msid = sources[ssrc].msid; - delete sources[ssrc].msid; - - // We assign one SSRC per media line. - mLine.sources = {}; - mLine.sources[ssrc] = sources[ssrc]; - mLine.ssrcGroups = invertedGroups[ssrc]; - - // Use the cached Plan A SDP (if it exists) to assign SSRCs to - // mids. - if (typeof cached !== 'undefined' && - typeof cached.media !== 'undefined' && - Array.isArray(cached.media)) { - - cached.media.forEach(function(m) { - if (typeof m.sources === 'object') { - Object.keys(m.sources).forEach(function(s) { - if (s === ssrc) { - mLine.mid = m.mid; - } - }); - } - }); - } - - if (typeof mLine.mid === 'undefined') { - - // If this is an SSRC that we see for the first time assign - // it a new mid. This is typically the case when this - // method is called to transform a remote description for - // the first time or when there is a new SSRC in the remote - // description because a new peer has joined the - // conference. Local SSRCs should have already been added - // to the map in the toPlanB method. - // - // Because FF generates answers in Plan A style, we MUST - // already have a cached answer with all the local SSRCs - // mapped to some mLine/mid. - - if (desc.type === 'answer') { - throw new Error("An unmapped SSRC was found."); + if (typeof sources[ssrc].msid !== 'undefined') { + // Assign the msid of the source to the m-line. Note + // that it is not guaranteed that the source will have + // msid. In particular "recvonly" sources don't have an + // msid. Note that "recvonly" is a term only defined + // for m-lines. + unifiedLine.msid = sources[ssrc].msid; + delete sources[ssrc].msid; } - mLine.mid = [channel.type, '-', ssrc].join(''); - } + // We assign one SSRC per media line. + unifiedLine.sources = {}; + unifiedLine.sources[ssrc] = sources[ssrc]; + unifiedLine.ssrcGroups = ssrc2group[ssrc]; - // Include the candidates in the 1st media line. - mLine.candidates = candidates; - mLine.iceUfrag = iceUfrag; - mLine.icePwd = icePwd; - mLine.fingerprint = fingerprint; - mLine.port = port; + // Use the cached Unified Plan SDP (if it exists) to assign + // SSRCs to mids. + if (typeof cached !== 'undefined' && + typeof cached.media !== 'undefined' && + Array.isArray(cached.media)) { - media[mLine.mid] = mLine; + cached.media.forEach(function (m) { + if (typeof m.sources === 'object') { + Object.keys(m.sources).forEach(function (s) { + if (s === ssrc) { + unifiedLine.mid = m.mid; + } + }); + } + }); + } + + if (typeof unifiedLine.mid === 'undefined') { + + // If this is an SSRC that we see for the first time + // assign it a new mid. This is typically the case when + // this method is called to transform a remote + // description for the first time or when there is a + // new SSRC in the remote description because a new + // peer has joined the conference. Local SSRCs should + // have already been added to the map in the toPlanB + // method. + // + // Because FF generates answers in Unified Plan style, + // we MUST already have a cached answer with all the + // local SSRCs mapped to some m-line/mid. + + if (desc.type === 'answer') { + throw new Error("An unmapped SSRC was found."); + } + + unifiedLine.mid = [bLine.type, '-', ssrc].join(''); + } + + // Include the candidates in the 1st media line. + unifiedLine.candidates = candidates; + unifiedLine.iceUfrag = iceUfrag; + unifiedLine.icePwd = icePwd; + unifiedLine.fingerprint = fingerprint; + unifiedLine.port = port; + + mid2ul[unifiedLine.mid] = unifiedLine; } }); } @@ -28235,54 +28604,57 @@ Interop.prototype.toPlanA = function(desc) { if (desc.type === 'answer') { // The media lines in the answer must match the media lines in the - // offer. The order is important too. Here we use the cached offer to - // find the m-lines that are missing (from the converted answer), and - // use the cached answer to complete the converted answer. + // offer. The order is important too. Here we assume that Firefox is the + // answerer, so we merely have to use the reconstructed (unified) answer + // to update the cached (unified) answer accordingly. + // + // In the general case, one would have to use the cached (unified) offer + // to find the m-lines that are missing from the reconstructed answer, + // potentially grabbing them from the cached (unified) answer. One has + // to be carefull with this approach because inactive m-lines do not + // always have an mid, making it tricky (impossible?) to find where + // exactly and which m-lines are missing from the reconstructed answer. - if (typeof cache['offer'] === 'undefined') { - throw new Error("An answer is being processed but we couldn't " + - "find a cached offer."); - } + for (var i = 0; i < cached.media.length; i++) { + var unifiedLine = cached.media[i]; - var cachedOffer = transform.parse(cache['offer']); + if (typeof mid2ul[unifiedLine.mid] === 'undefined') { - if (typeof cachedOffer === 'undefined' || - typeof cachedOffer.media === 'undefined' || - !Array.isArray(cachedOffer.media)) { - // FIXME(gp) is this really a problem in the general case? - throw new Error("The cached offer has no media."); - } + // The mid isn't in the reconstructed (unified) answer. + // This is either a (unified) m-line containing a remote + // track only, or a (unified) m-line containing a remote + // track and a local track that has been removed. + // In either case, it MUST exist in the cached + // (unified) answer. + // + // In case this is a removed local track, clean-up + // the (unified) m-line and make sure it's 'recvonly' or + // 'inactive'. - cachedOffer.media.forEach(function(mo) { - - var mLine; - if (typeof media[mo.mid] === 'undefined') { - - // This is probably an m-line containing a remote track only. - // It MUST exist in the cached answer as a remote track only - // mLine. - - cached.media.every(function(ma) { - if (mo.mid == ma.mid) { - mLine = ma; - return false; - } else { - return true; - } - }); + delete unifiedLine.msid; + delete unifiedLine.sources; + delete unifiedLine.ssrcGroups; + if (!unifiedLine.direction + || unifiedLine.direction === 'sendrecv') + unifiedLine.direction = 'recvonly'; + if (!unifiedLine.direction + || unifiedLine.direction === 'sendonly') + unifiedLine.direction = 'inactive'; } else { - mLine = media[mo.mid]; + // This is an (unified) m-line/channel that contains a local + // track (sendrecv or sendonly channel) or it's a unified + // recvonly m-line/channel. In either case, since we're + // going from PlanB -> Unified Plan this m-line MUST + // exist in the cached answer. } - if (typeof mLine === 'undefined') { - throw new Error("The cached offer contains an m-line that " + - "doesn't exist neither in the cached answer nor in " + - "the converted answer."); - } + session.media.push(unifiedLine); - session.media.push(mLine); - mids.push(mLine.mid); - }); + if (typeof unifiedLine.mid === 'string') { + // inactive lines don't/may not have an mid. + mids.push(unifiedLine.mid); + } + } } else { // SDP offer/answer (and the JSEP spec) forbids removing an m-section @@ -28296,35 +28668,57 @@ Interop.prototype.toPlanA = function(desc) { if (typeof cached !== 'undefined' && typeof cached.media !== 'undefined' && Array.isArray(cached.media)) { - cached.media.forEach(function(pm) { - mids.push(pm.mid); - if (typeof media[pm.mid] !== 'undefined') { - session.media.push(media[pm.mid]); + cached.media.forEach(function(unifiedLine) { + mids.push(unifiedLine.mid); + if (typeof mid2ul[unifiedLine.mid] !== 'undefined') { + session.media.push(mid2ul[unifiedLine.mid]); } else { - delete pm.msid; - delete pm.sources; - delete pm.ssrcGroups; - pm.direction = 'recvonly'; - session.media.push(pm); + delete unifiedLine.msid; + delete unifiedLine.sources; + delete unifiedLine.ssrcGroups; + if (!unifiedLine.direction + || unifiedLine.direction === 'sendrecv') + unifiedLine.direction = 'recvonly'; + if (!unifiedLine.direction + || unifiedLine.direction === 'sendonly') + unifiedLine.direction = 'inactive'; + session.media.push(unifiedLine); } }); } // Add all the remaining (new) m-lines of the transformed SDP. - Object.keys(media).forEach(function(mid) { + Object.keys(mid2ul).forEach(function(mid) { if (mids.indexOf(mid) === -1) { mids.push(mid); - session.media.push(media[mid]); + if (typeof mid2ul[mid].direction === 'recvonly') { + // This is a remote recvonly channel. Add its SSRC to the + // appropriate sendrecv or sendonly channel. + // TODO(gp) what if we don't have sendrecv/sendonly channel? + session.media.some(function (unifiedLine) { + if ((unifiedLine.direction === 'sendrecv' || + unifiedLine.direction === 'sendonly') && + unifiedLine.type === mid2ul[mid].type) { + + // mid2ul[mid] shouldn't have any ssrc-groups + Object.keys(mid2ul[mid].sources).forEach(function (ssrc) { + unifiedLine.sources[ssrc] = mid2ul[mid].sources[ssrc]; + }); + + return true; + } + }); + } else { + session.media.push(mid2ul[mid]); + } } }); } // We regenerate the BUNDLE group (since we regenerated the mids) - session.groups.every(function(group) { + session.groups.some(function(group) { if (group.type === 'BUNDLE') { group.mids = mids.join(' '); - return false; - } else { return true; } }); @@ -28337,8 +28731,9 @@ Interop.prototype.toPlanA = function(desc) { var resStr = transform.write(session); - // Cache the transformed SDP (Plan A) for later re-use in this function. - cache[desc.type] = resStr; + // Cache the transformed SDP (Unified Plan) for later re-use in this + // function. + this.cache[desc.type] = resStr; return new RTCSessionDescription({ type: desc.type, @@ -28348,7 +28743,7 @@ Interop.prototype.toPlanA = function(desc) { //#endregion }; -},{"./array-equals":85,"./transform":88}],88:[function(require,module,exports){ +},{"./array-equals":86,"./transform":89}],89:[function(require,module,exports){ var transform = require('sdp-transform'); exports.write = function(session, opts) { @@ -28447,7 +28842,7 @@ exports.parse = function(sdp) { }; -},{"sdp-transform":90}],89:[function(require,module,exports){ +},{"sdp-transform":91}],90:[function(require,module,exports){ var grammar = module.exports = { v: [{ name: 'version', @@ -28672,6 +29067,10 @@ var grammar = module.exports = { name: 'rtcpMux', reg: /^(rtcp-mux)/ }, + { //a=rtcp-rsize + name: 'rtcpRsize', + reg: /^(rtcp-rsize)/ + }, { // any a= that we don't understand is kepts verbatim on media.invalid push: 'invalid', names: ["value"] @@ -28692,7 +29091,7 @@ Object.keys(grammar).forEach(function (key) { }); }); -},{}],90:[function(require,module,exports){ +},{}],91:[function(require,module,exports){ var parser = require('./parser'); var writer = require('./writer'); @@ -28702,7 +29101,7 @@ exports.parseFmtpConfig = parser.parseFmtpConfig; exports.parsePayloads = parser.parsePayloads; exports.parseRemoteCandidates = parser.parseRemoteCandidates; -},{"./parser":91,"./writer":92}],91:[function(require,module,exports){ +},{"./parser":92,"./writer":93}],92:[function(require,module,exports){ var toIntIfInt = function (v) { return String(Number(v)) === v ? Number(v) : v; }; @@ -28797,7 +29196,7 @@ exports.parseRemoteCandidates = function (str) { return candidates; }; -},{"./grammar":89}],92:[function(require,module,exports){ +},{"./grammar":90}],93:[function(require,module,exports){ var grammar = require('./grammar'); // customized util.format - discards excess arguments and can void middle ones @@ -28913,14 +29312,14 @@ module.exports = function (session, opts) { return sdp.join('\r\n') + '\r\n'; }; -},{"./grammar":89}],93:[function(require,module,exports){ +},{"./grammar":90}],94:[function(require,module,exports){ var MediaStreamType = { VIDEO_TYPE: "Video", AUDIO_TYPE: "Audio" }; module.exports = MediaStreamType; -},{}],94:[function(require,module,exports){ +},{}],95:[function(require,module,exports){ var RTCBrowserType = { RTC_BROWSER_CHROME: "rtc_browser.chrome", @@ -28928,7 +29327,7 @@ var RTCBrowserType = { }; module.exports = RTCBrowserType; -},{}],95:[function(require,module,exports){ +},{}],96:[function(require,module,exports){ var RTCEvents = { LASTN_CHANGED: "rtc.lastn_changed", DOMINANTSPEAKER_CHANGED: "rtc.dominantspeaker_changed", @@ -28941,7 +29340,7 @@ var RTCEvents = { }; module.exports = RTCEvents; -},{}],96:[function(require,module,exports){ +},{}],97:[function(require,module,exports){ var Resolutions = { "1080": { width: 1920, @@ -28995,7 +29394,7 @@ var Resolutions = { } }; module.exports = Resolutions; -},{}],97:[function(require,module,exports){ +},{}],98:[function(require,module,exports){ var StreamEventTypes = { EVENT_TYPE_LOCAL_CREATED: "stream.local_created", @@ -29011,14 +29410,14 @@ var StreamEventTypes = { }; module.exports = StreamEventTypes; -},{}],98:[function(require,module,exports){ +},{}],99:[function(require,module,exports){ var UIEvents = { NICKNAME_CHANGED: "UI.nickname_changed", SELECTED_ENDPOINT: "UI.selected_endpoint", PINNED_ENDPOINT: "UI.pinned_endpoint" }; module.exports = UIEvents; -},{}],99:[function(require,module,exports){ +},{}],100:[function(require,module,exports){ var AuthenticationEvents = { /** * Event callback arguments: @@ -29032,7 +29431,7 @@ var AuthenticationEvents = { }; module.exports = AuthenticationEvents; -},{}],100:[function(require,module,exports){ +},{}],101:[function(require,module,exports){ var CQEvents = { LOCALSTATS_UPDATED: "cq.localstats_updated", REMOTESTATS_UPDATED: "cq.remotestats_updated", @@ -29040,7 +29439,7 @@ var CQEvents = { }; module.exports = CQEvents; -},{}],101:[function(require,module,exports){ +},{}],102:[function(require,module,exports){ var DesktopSharingEventTypes = { INIT: "ds.init", @@ -29050,14 +29449,14 @@ var DesktopSharingEventTypes = { }; module.exports = DesktopSharingEventTypes; -},{}],102:[function(require,module,exports){ +},{}],103:[function(require,module,exports){ var Events = { DTMF_SUPPORT_CHANGED: "members.dtmf_support_changed" }; module.exports = Events; -},{}],103:[function(require,module,exports){ +},{}],104:[function(require,module,exports){ module.exports = { getLanguages : function () { var languages = []; @@ -29073,7 +29472,7 @@ module.exports = { DE: "de", TR: "tr" } -},{}],104:[function(require,module,exports){ +},{}],105:[function(require,module,exports){ var XMPPEvents = { CONNECTION_FAILED: "xmpp.connection.failed", CONFERENCE_CREATED: "xmpp.conferenceCreated.jingle", @@ -29109,367 +29508,64 @@ var XMPPEvents = { START_MUTED: "xmpp.start_muted" }; module.exports = XMPPEvents; -},{}],105:[function(require,module,exports){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -function EventEmitter() { - this._events = this._events || {}; - this._maxListeners = this._maxListeners || undefined; -} -module.exports = EventEmitter; - -// Backwards-compat with node 0.10.x -EventEmitter.EventEmitter = EventEmitter; - -EventEmitter.prototype._events = undefined; -EventEmitter.prototype._maxListeners = undefined; - -// By default EventEmitters will print a warning if more than 10 listeners are -// added to it. This is a useful default which helps finding memory leaks. -EventEmitter.defaultMaxListeners = 10; - -// Obviously not all Emitters should be limited to 10. This function allows -// that to be increased. Set to zero for unlimited. -EventEmitter.prototype.setMaxListeners = function(n) { - if (!isNumber(n) || n < 0 || isNaN(n)) - throw TypeError('n must be a positive number'); - this._maxListeners = n; - return this; -}; - -EventEmitter.prototype.emit = function(type) { - var er, handler, len, args, i, listeners; - - if (!this._events) - this._events = {}; - - // If there is no 'error' event listener then throw. - if (type === 'error') { - if (!this._events.error || - (isObject(this._events.error) && !this._events.error.length)) { - er = arguments[1]; - if (er instanceof Error) { - throw er; // Unhandled 'error' event - } - throw TypeError('Uncaught, unspecified "error" event.'); - } - } - - handler = this._events[type]; - - if (isUndefined(handler)) - return false; - - if (isFunction(handler)) { - switch (arguments.length) { - // fast cases - case 1: - handler.call(this); - break; - case 2: - handler.call(this, arguments[1]); - break; - case 3: - handler.call(this, arguments[1], arguments[2]); - break; - // slower - default: - len = arguments.length; - args = new Array(len - 1); - for (i = 1; i < len; i++) - args[i - 1] = arguments[i]; - handler.apply(this, args); - } - } else if (isObject(handler)) { - len = arguments.length; - args = new Array(len - 1); - for (i = 1; i < len; i++) - args[i - 1] = arguments[i]; - - listeners = handler.slice(); - len = listeners.length; - for (i = 0; i < len; i++) - listeners[i].apply(this, args); - } - - return true; -}; - -EventEmitter.prototype.addListener = function(type, listener) { - var m; - - if (!isFunction(listener)) - throw TypeError('listener must be a function'); - - if (!this._events) - this._events = {}; - - // To avoid recursion in the case that type === "newListener"! Before - // adding it to the listeners, first emit "newListener". - if (this._events.newListener) - this.emit('newListener', type, - isFunction(listener.listener) ? - listener.listener : listener); - - if (!this._events[type]) - // Optimize the case of one listener. Don't need the extra array object. - this._events[type] = listener; - else if (isObject(this._events[type])) - // If we've already got an array, just append. - this._events[type].push(listener); - else - // Adding the second element, need to change to array. - this._events[type] = [this._events[type], listener]; - - // Check for listener leak - if (isObject(this._events[type]) && !this._events[type].warned) { - var m; - if (!isUndefined(this._maxListeners)) { - m = this._maxListeners; - } else { - m = EventEmitter.defaultMaxListeners; - } - - if (m && m > 0 && this._events[type].length > m) { - this._events[type].warned = true; - console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); - if (typeof console.trace === 'function') { - // not supported in IE 10 - console.trace(); - } - } - } - - return this; -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.once = function(type, listener) { - if (!isFunction(listener)) - throw TypeError('listener must be a function'); - - var fired = false; - - function g() { - this.removeListener(type, g); - - if (!fired) { - fired = true; - listener.apply(this, arguments); - } - } - - g.listener = listener; - this.on(type, g); - - return this; -}; - -// emits a 'removeListener' event iff the listener was removed -EventEmitter.prototype.removeListener = function(type, listener) { - var list, position, length, i; - - if (!isFunction(listener)) - throw TypeError('listener must be a function'); - - if (!this._events || !this._events[type]) - return this; - - list = this._events[type]; - length = list.length; - position = -1; - - if (list === listener || - (isFunction(list.listener) && list.listener === listener)) { - delete this._events[type]; - if (this._events.removeListener) - this.emit('removeListener', type, listener); - - } else if (isObject(list)) { - for (i = length; i-- > 0;) { - if (list[i] === listener || - (list[i].listener && list[i].listener === listener)) { - position = i; - break; - } - } - - if (position < 0) - return this; - - if (list.length === 1) { - list.length = 0; - delete this._events[type]; - } else { - list.splice(position, 1); - } - - if (this._events.removeListener) - this.emit('removeListener', type, listener); - } - - return this; -}; - -EventEmitter.prototype.removeAllListeners = function(type) { - var key, listeners; - - if (!this._events) - return this; - - // not listening for removeListener, no need to emit - if (!this._events.removeListener) { - if (arguments.length === 0) - this._events = {}; - else if (this._events[type]) - delete this._events[type]; - return this; - } - - // emit removeListener for all listeners on all events - if (arguments.length === 0) { - for (key in this._events) { - if (key === 'removeListener') continue; - this.removeAllListeners(key); - } - this.removeAllListeners('removeListener'); - this._events = {}; - return this; - } - - listeners = this._events[type]; - - if (isFunction(listeners)) { - this.removeListener(type, listeners); - } else { - // LIFO order - while (listeners.length) - this.removeListener(type, listeners[listeners.length - 1]); - } - delete this._events[type]; - - return this; -}; - -EventEmitter.prototype.listeners = function(type) { - var ret; - if (!this._events || !this._events[type]) - ret = []; - else if (isFunction(this._events[type])) - ret = [this._events[type]]; - else - ret = this._events[type].slice(); - return ret; -}; - -EventEmitter.listenerCount = function(emitter, type) { - var ret; - if (!emitter._events || !emitter._events[type]) - ret = 0; - else if (isFunction(emitter._events[type])) - ret = 1; - else - ret = emitter._events[type].length; - return ret; -}; - -function isFunction(arg) { - return typeof arg === 'function'; -} - -function isNumber(arg) { - return typeof arg === 'number'; -} - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} - -function isUndefined(arg) { - return arg === void 0; -} - },{}],106:[function(require,module,exports){ -// shim for using process in browser +/* jshint -W117 */ +/* application specific logic */ -var process = module.exports = {}; -var queue = []; -var draining = false; +var APP = +{ + init: function () { + this.UI = require("./modules/UI/UI"); + this.API = require("./modules/API/API"); + this.connectionquality = require("./modules/connectionquality/connectionquality"); + this.statistics = require("./modules/statistics/statistics"); + this.RTC = require("./modules/RTC/RTC"); + this.simulcast = require("./modules/simulcast/simulcast"); + this.desktopsharing = require("./modules/desktopsharing/desktopsharing"); + this.xmpp = require("./modules/xmpp/xmpp"); + this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut"); + this.translation = require("./modules/translation/translation"); + this.settings = require("./modules/settings/Settings"); + this.DTMF = require("./modules/DTMF/DTMF"); + this.members = require("./modules/members/MemberList"); + } +}; -function drainQueue() { - if (draining) { - return; - } - draining = true; - var currentQueue; - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - var i = -1; - while (++i < len) { - currentQueue[i](); - } - len = queue.length; - } - draining = false; +function init() { + + APP.RTC.start(); + APP.xmpp.start(); + APP.statistics.start(); + APP.connectionquality.init(); + + // Set default desktop sharing method + APP.desktopsharing.init(); + + APP.keyboardshortcut.init(); + APP.members.start(); } -process.nextTick = function (fun) { - queue.push(fun); - if (!draining) { - setTimeout(drainQueue, 0); - } -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -function noop() {} +$(document).ready(function () { -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; + APP.init(); -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; + APP.translation.init(); -// TODO(shtylman) -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; + if(APP.API.isEnabled()) + APP.API.init(); -},{}]},{},[1])(1) + APP.UI.start(init); + +}); + +$(window).bind('beforeunload', function () { + if(APP.API.isEnabled()) + APP.API.dispose(); +}); + +module.exports = APP; + + +},{"./modules/API/API":1,"./modules/DTMF/DTMF":2,"./modules/RTC/RTC":6,"./modules/UI/UI":8,"./modules/connectionquality/connectionquality":35,"./modules/desktopsharing/desktopsharing":36,"./modules/keyboardshortcut/keyboardshortcut":37,"./modules/members/MemberList":38,"./modules/settings/Settings":39,"./modules/simulcast/simulcast":44,"./modules/statistics/statistics":47,"./modules/translation/translation":48,"./modules/xmpp/xmpp":62}]},{},[106])(106) }); \ No newline at end of file