--- a/browser/components/loop/content/shared/libs/sdk.js
+++ b/browser/components/loop/content/shared/libs/sdk.js
@@ -1,25 +1,25 @@
/**
- * @license OpenTok JavaScript Library v2.2.9.1
+ * @license OpenTok JavaScript Library v2.2.9.7
* http://www.tokbox.com/
*
* Copyright (c) 2014 TokBox, Inc.
* Released under the MIT license
* http://opensource.org/licenses/MIT
*
- * Date: September 08 10:17:05 2014
+ * Date: January 26 03:18:02 2015
*/
(function(window) {
if (!window.OT) window.OT = {};
OT.properties = {
- version: 'v2.2.9.1', // The current version (eg. v2.0.4) (This is replaced by gradle)
- build: '72b534e', // The current build hash (This is replaced by gradle)
+ version: 'v2.2.9.7', // The current version (eg. v2.0.4) (This is replaced by gradle)
+ build: '59e99bc', // The current build hash (This is replaced by gradle)
// Whether or not to turn on debug logging by default
debug: 'false',
// The URL of the tokbox website
websiteURL: 'http://www.tokbox.com',
// The URL of the CDN
cdnURL: 'http://static.opentok.com',
@@ -3098,16 +3098,24 @@
* @memberof OT
* @function
* @see <a href="#setLogLevel">OT.setLogLevel()</a>
*/
})(window);
!(function() {
+ var adjustModal = function(callback) {
+ return function setFullHeightDocument(window, document) {
+ // required in IE8
+ document.querySelector('html').style.height = document.body.style.height = '100%';
+ callback(window, document);
+ };
+ };
+
var addCss = function(document, url, callback) {
var head = document.head || document.getElementsByTagName('head')[0];
var cssTag = OT.$.createElement('link', {
type: 'text/css',
media: 'screen',
rel: 'stylesheet',
href: url
});
@@ -3162,213 +3170,20 @@
var linkElement = function(children, href, classes) {
var link = templateElement.call(this, classes || '', children, 'a');
link.setAttribute('href', href);
return link;
};
OT.Dialogs = {};
- OT.Dialogs.AllowDeny = {
- Chrome: {},
- Firefox: {}
- };
-
- OT.Dialogs.AllowDeny.Chrome.initialPrompt = function() {
- var modal = new OT.$.Modal(function(window, document) {
-
- var el = OT.$.bind(templateElement, document),
- close, root;
-
- close = el('OT_closeButton', '×')
- .on('click', function() {
- modal.trigger('closeButtonClicked');
- modal.close();
- });
-
- root = el('OT_root OT_dialog OT_dialog-allow-deny-chrome-first', [
- close,
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', 'Allow camera and mic access'),
- el('OT_dialog-messages-minor', 'Click the Allow button in the upper-right corner ' +
- 'of your browser to enable real-time communication.'),
- el('OT_dialog-allow-highlight-chrome')
- ])
- ]);
-
- addDialogCSS(document, [], function() {
- document.body.appendChild(root);
- });
-
- });
- return modal;
- };
-
- OT.Dialogs.AllowDeny.Chrome.previouslyDenied = function(website) {
- var modal = new OT.$.Modal(function(window, document) {
-
- var el = OT.$.bind(templateElement, document),
- close,
- root;
-
- close = el('OT_closeButton', '×')
- .on('click', function() {
- modal.trigger('closeButtonClicked');
- modal.close();
- });
-
- root = el('OT_root OT_dialog OT_dialog-allow-deny-chrome-pre-denied', [
- close,
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', 'Allow camera and mic access'),
- el('OT_dialog-messages-minor', [
- 'To interact with this app, follow these 3 steps:',
- el('OT_dialog-3steps', [
- el('OT_dialog-3steps-step', [
- el('OT_dialog-3steps-step-num', '1'),
- 'Find this icon in the URL bar and click it',
- el('OT_dialog-allow-camera-icon')
- ]),
- el('OT_dialog-3steps-seperator'),
- el('OT_dialog-3steps-step', [
- el('OT_dialog-3steps-step-num', '2'),
- 'Select "Ask if ' + website + ' wants to access your camera and mic" ' +
- 'and then click Done.'
- ]),
- el('OT_dialog-3steps-seperator'),
- el('OT_dialog-3steps-step', [
- el('OT_dialog-3steps-step-num', '3'),
- 'Refresh your browser.'
- ])
- ])
- ])
- ])
- ]);
-
- addDialogCSS(document, [], function() {
- document.body.appendChild(root);
- });
-
- });
- return modal;
- };
-
- OT.Dialogs.AllowDeny.Chrome.deniedNow = function() {
- var modal = new OT.$.Modal(function(window, document) {
-
- var el = OT.$.bind(templateElement, document),
- root;
-
- root = el('OT_root OT_dialog-blackout',
- el('OT_dialog OT_dialog-allow-deny-chrome-now-denied', [
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main ',
- el('OT_dialog-allow-camera-icon')
- ),
- el('OT_dialog-messages-minor',
- 'Find & click this icon to allow camera and mic access.'
- )
- ])
- ])
- );
-
- addDialogCSS(document, [], function() {
- document.body.appendChild(root);
- });
-
- });
- return modal;
- };
-
- OT.Dialogs.AllowDeny.Firefox.maybeDenied = function() {
- var modal = new OT.$.Modal(function(window, document) {
-
- var el = OT.$.bind(templateElement, document),
- close,
- root;
-
- close = el('OT_closeButton', '×')
- .on('click', function() {
- modal.trigger('closeButtonClicked');
- modal.close();
- });
-
- root = el('OT_root OT_dialog OT_dialog-allow-deny-firefox-maybe-denied', [
- close,
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', 'Please allow camera & mic access'),
- el('OT_dialog-messages-minor', [
- 'To interact with this app, follow these 3 steps:',
- el('OT_dialog-3steps', [
- el('OT_dialog-3steps-step', [
- el('OT_dialog-3steps-step-num', '1'),
- 'Reload the page, or click the camera icon ' +
- 'in the browser URL bar.'
- ]),
- el('OT_dialog-3steps-seperator'),
- el('OT_dialog-3steps-step', [
- el('OT_dialog-3steps-step-num', '2'),
- 'In the menu, select your camera & mic.'
- ]),
- el('OT_dialog-3steps-seperator'),
- el('OT_dialog-3steps-step', [
- el('OT_dialog-3steps-step-num', '3'),
- 'Click "Share Selected Devices."'
- ])
- ])
- ])
- ])
- ]);
-
- addDialogCSS(document, [], function() {
- document.body.appendChild(root);
- });
-
- });
- return modal;
- };
-
- OT.Dialogs.AllowDeny.Firefox.denied = function() {
- var modal = new OT.$.Modal(function(window, document) {
-
- var el = OT.$.bind(templateElement, document),
- btn = OT.$.bind(templateElement, document, 'OT_dialog-button OT_dialog-button-large'),
- root,
- refreshButton;
-
- refreshButton = btn('Reload')
- .on('click', function() {
- modal.trigger('refresh');
- });
-
- root = el('OT_root OT_dialog-blackout',
- el('OT_dialog OT_dialog-allow-deny-firefox-denied', [
- el('OT_dialog-messages', [
- el('OT_dialog-messages-minor',
- 'Access to camera and microphone has been denied. ' +
- 'Click the button to reload page.'
- )
- ]),
- el('OT_dialog-single-button', refreshButton)
- ])
- );
-
- addDialogCSS(document, [], function() {
- document.body.appendChild(root);
- });
-
- });
-
- return modal;
- };
-
OT.Dialogs.Plugin = {};
OT.Dialogs.Plugin.promptToInstall = function() {
- var modal = new OT.$.Modal(function(window, document) {
+ var modal = new OT.$.Modal(adjustModal(function(window, document) {
var el = OT.$.bind(templateElement, document),
btn = function(children, size) {
var classes = 'OT_dialog-button ' +
(size ? 'OT_dialog-button-' + size : 'OT_dialog-button-large'),
b = el(classes, children);
b.enable = function() {
@@ -3386,22 +3201,25 @@
downloadButton = btn('Download plugin'),
cancelButton = btn('cancel', 'small'),
refreshButton = btn('Refresh browser'),
acceptEULA,
checkbox,
close,
root;
+ OT.$.addClass(cancelButton, 'OT_dialog-no-natural-margin OT_dialog-button-block');
+ OT.$.addClass(refreshButton, 'OT_dialog-no-natural-margin');
+
function onDownload() {
modal.trigger('download');
setTimeout(function() {
root.querySelector('.OT_dialog-messages-main').innerHTML =
'Plugin installation successful';
- var sections = root.querySelectorAll('.OT_dialog-single-button-wide');
+ var sections = root.querySelectorAll('.OT_dialog-section');
OT.$.addClass(sections[0], 'OT_dialog-hidden');
OT.$.removeClass(sections[1], 'OT_dialog-hidden');
}, 3000);
}
function onRefresh() {
modal.trigger('refresh');
}
@@ -3446,127 +3264,143 @@
});
acceptEULA = linkElement.call(document,
'end-user license agreement',
'http://tokbox.com/support/ie-eula');
checkbox = checkBoxElement.call(document, null, 'acceptEULA', onToggleEULA);
- root = el('OT_root OT_dialog OT_dialog-plugin-prompt', [
- close,
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', 'This app requires real-time communication')
- ]),
- el('OT_dialog-single-button-wide', [
- el('OT_dialog-single-button-with-title', [
- el('OT_dialog-button-title', [
- checkbox,
- (function() {
- var x = el('', 'accept', 'label');
- x.setAttribute('for', checkbox.id);
- x.style.margin = '0 5px';
- return x;
- })(),
- acceptEULA
+ root = el('OT_dialog-centering', [
+ el('OT_dialog-centering-child', [
+ el('OT_root OT_dialog OT_dialog-plugin-prompt', [
+ close,
+ el('OT_dialog-messages', [
+ el('OT_dialog-messages-main', 'This app requires real-time communication')
]),
- downloadButton,
- cancelButton
- ])
- ]),
- el('OT_dialog-single-button-wide OT_dialog-hidden', [
- el('OT_dialog-single-button-with-title', [
- el('OT_dialog-button-title', [
- 'You can now enjoy webRTC enabled video via Internet Explorer.'
+ el('OT_dialog-section', [
+ el('OT_dialog-single-button-with-title', [
+ el('OT_dialog-button-title', [
+ checkbox,
+ (function() {
+ var x = el('', 'accept', 'label');
+ x.setAttribute('for', checkbox.id);
+ x.style.margin = '0 5px';
+ return x;
+ })(),
+ acceptEULA
+ ]),
+ el('OT_dialog-actions-card', [
+ downloadButton,
+ cancelButton
+ ])
+ ])
]),
- refreshButton
+ el('OT_dialog-section OT_dialog-hidden', [
+ el('OT_dialog-button-title', [
+ 'You can now enjoy webRTC enabled video via Internet Explorer.'
+ ]),
+ refreshButton
+ ])
])
])
]);
addDialogCSS(document, [], function() {
document.body.appendChild(root);
});
- });
+ }));
return modal;
};
OT.Dialogs.Plugin.promptToReinstall = function() {
- var modal = new OT.$.Modal(function(window, document) {
+ var modal = new OT.$.Modal(adjustModal(function(window, document) {
var el = OT.$.bind(templateElement, document),
close,
okayButton,
root;
close = el('OT_closeButton', '×');
- okayButton = el('OT_dialog-button', 'Okay');
+ okayButton =
+ el('OT_dialog-button OT_dialog-button-large OT_dialog-no-natural-margin', 'Okay');
OT.$.on(okayButton, 'click', function() {
modal.trigger('okay');
});
OT.$.on(close, 'click', function() {
modal.trigger('closeButtonClicked');
modal.close();
});
- root = el('OT_ROOT OT_dialog OT_dialog-plugin-reinstall', [
- close,
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', 'Reinstall Opentok Plugin'),
- el('OT_dialog-messages-minor', 'Uh oh! Try reinstalling the OpenTok plugin again to ' +
- 'enable real-time video communication for Internet Explorer.')
- ]),
- el('OT_dialog-single-button', okayButton)
+ root = el('OT_dialog-centering', [
+ el('OT_dialog-centering-child', [
+ el('OT_ROOT OT_dialog OT_dialog-plugin-reinstall', [
+ close,
+ el('OT_dialog-messages', [
+ el('OT_dialog-messages-main', 'Reinstall Opentok Plugin'),
+ el('OT_dialog-messages-minor', 'Uh oh! Try reinstalling the OpenTok plugin ' +
+ 'again to enable real-time video communication for Internet Explorer.')
+ ]),
+ el('OT_dialog-section', [
+ el('OT_dialog-single-button', okayButton)
+ ])
+ ])
+ ])
]);
addDialogCSS(document, [], function() {
document.body.appendChild(root);
});
- });
+ }));
return modal;
};
OT.Dialogs.Plugin.updateInProgress = function() {
var progressBar,
progressText,
progressValue = 0;
- var modal = new OT.$.Modal(function(window, document) {
+ var modal = new OT.$.Modal(adjustModal(function(window, document) {
var el = OT.$.bind(templateElement, document),
root;
progressText = el('OT_dialog-plugin-upgrade-percentage', '0%', 'strong');
progressBar = el('OT_dialog-progress-bar-fill');
- root = el('OT_ROOT OT_dialog OT_dialog-plugin-upgrading', [
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', [
- 'One moment please... ',
- progressText
- ]),
- el('OT_dialog-progress-bar', progressBar),
- el('OT_dialog-messages-minor', 'Please wait while the OpenTok plugin is updated')
+ root = el('OT_dialog-centering', [
+ el('OT_dialog-centering-child', [
+ el('OT_ROOT OT_dialog OT_dialog-plugin-upgrading', [
+ el('OT_dialog-messages', [
+ el('OT_dialog-messages-main', [
+ 'One moment please... ',
+ progressText
+ ]),
+ el('OT_dialog-progress-bar', progressBar),
+ el('OT_dialog-messages-minor OT_dialog-no-natural-margin',
+ 'Please wait while the OpenTok plugin is updated')
+ ])
+ ])
])
]);
addDialogCSS(document, [], function() {
document.body.appendChild(root);
if(progressValue != null) {
modal.setUpdateProgress(progressValue);
}
});
- });
+ }));
modal.setUpdateProgress = function(newProgress) {
if(progressBar && progressText) {
if(newProgress > 99) {
OT.$.css(progressBar, 'width', '');
progressText.innerHTML = '100%';
} else if(newProgress < 1) {
OT.$.css(progressBar, 'width', '0%');
@@ -3579,48 +3413,54 @@
progressValue = newProgress;
}
};
return modal;
};
OT.Dialogs.Plugin.updateComplete = function(error) {
- var modal = new OT.$.Modal(function(window, document) {
+ var modal = new OT.$.Modal(adjustModal(function(window, document) {
var el = OT.$.bind(templateElement, document),
reloadButton,
root;
- reloadButton = el('OT_dialog-button', 'Reload').on('click', function() {
- modal.trigger('reload');
- });
+ reloadButton =
+ el('OT_dialog-button OT_dialog-button-large OT_dialog-no-natural-margin', 'Reload')
+ .on('click', function() {
+ modal.trigger('reload');
+ });
var msgs;
if(error) {
msgs = ['Update Failed.', error + '' || 'NO ERROR'];
} else {
msgs = ['Update Complete.',
'The OpenTok plugin has been succesfully updated. ' +
'Please reload your browser.'];
}
- root = el('OT_root OT_dialog OT_dialog-plugin-upgraded', [
- el('OT_dialog-messages', [
- el('OT_dialog-messages-main', msgs[0]),
- el('OT_dialog-messages-minor', msgs[1])
- ]),
- el('OT_dialog-single-button', reloadButton)
+ root = el('OT_dialog-centering', [
+ el('OT_dialog-centering-child', [
+ el('OT_root OT_dialog OT_dialog-plugin-upgraded', [
+ el('OT_dialog-messages', [
+ el('OT_dialog-messages-main', msgs[0]),
+ el('OT_dialog-messages-minor', msgs[1])
+ ]),
+ el('OT_dialog-single-button', reloadButton)
+ ])
+ ])
]);
addDialogCSS(document, [], function() {
document.body.appendChild(root);
});
- });
+ }));
return modal;
};
})();
!(function(window) {
@@ -3806,24 +3646,22 @@
OT.$.eventing(_this);
return _this;
})();
})(window);
/**
- * @license TB Plugin 0.4.0.8 72b534e HEAD
+ * @license TB Plugin 0.4.0.8 59e99bc HEAD
* http://www.tokbox.com/
*
- * Copyright (c) 2014 TokBox, Inc.
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- * Date: September 08 10:17:49 2014
+ * Copyright (c) 2015 TokBox, Inc.
+ *
+ * Date: January 26 03:18:16 2015
*
*/
/* jshint globalstrict: true, strict: false, undef: true, unused: false,
trailing: true, browser: true, smarttabs:true */
/* global scope:true, OT:true */
/* exported TBPlugin */
@@ -3950,16 +3788,82 @@ var shim = function shim () {
return res;
};
}
};
// tb_require('./header.js')
// tb_require('./shims.js')
+/* global OT:true */
+/* exported PluginRumorSocket */
+
+var PluginRumorSocket = function(plugin, server) {
+ var connected = false,
+ rumorID;
+
+ try {
+ rumorID = plugin._.RumorInit(server, '');
+ }
+ catch(e) {
+ OT.error('Error creating the Rumor Socket: ', e.message);
+ }
+
+ if(!rumorID) {
+ throw new Error('Could not initialise plugin rumor connection');
+ }
+
+ var socket = {
+ open: function() {
+ connected = true;
+ plugin._.RumorOpen(rumorID);
+ },
+
+ close: function(code, reason) {
+ if (!connected) return;
+ connected = false;
+
+ plugin._.RumorClose(rumorID, code, reason);
+ plugin.removeRef(this);
+ },
+
+ destroy: function() {
+ this.close();
+ },
+
+ send: function(msg) {
+ plugin._.RumorSend(rumorID, msg.type, msg.toAddress,
+ JSON.parse(JSON.stringify(msg.headers)), msg.data);
+ },
+
+ onOpen: function(callback) {
+ plugin._.SetOnRumorOpen(rumorID, callback);
+ },
+
+ onClose: function(callback) {
+ plugin._.SetOnRumorClose(rumorID, callback);
+ },
+
+ onError: function(callback) {
+ plugin._.SetOnRumorError(rumorID, callback);
+ },
+
+ onMessage: function(callback) {
+ plugin._.SetOnRumorMessage(rumorID, callback);
+ }
+ };
+
+ plugin.addRef(socket);
+ return socket;
+
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
/* global OT:true, TBPlugin:true, pluginInfo:true, debug:true, scope:true,
_document:true */
/* exported createMediaCaptureController:true, createPeerController:true,
injectObject:true, plugins:true, mediaCaptureObject:true,
removeAllObjects:true, curryCallAsync:true */
@@ -4351,470 +4255,16 @@ var VideoContainer = function VideoConta
};
// tb_require('./header.js')
// tb_require('./shims.js')
// tb_require('./plugin_object.js')
/* jshint globalstrict: true, strict: false, undef: true, unused: true,
trailing: true, browser: true, smarttabs:true */
-/* global OT:true, TBPlugin:true, pluginInfo:true, ActiveXObject:true,
- injectObject:true, curryCallAsync:true */
-
-/* exported AutoUpdater:true */
-var AutoUpdater;
-
-(function() {
-
- var autoUpdaterController,
- updaterMimeType, // <- cached version, use getInstallerMimeType instead
- installedVersion = -1; // <- cached version, use getInstallerMimeType instead
-
-
- var versionGreaterThan = function versionGreaterThan (version1,version2) {
- if (version1 === version2) return false;
-
- var v1 = version1.split('.'),
- v2 = version2.split('.');
-
- v1 = parseFloat(parseInt(v1.shift(), 10) + '.' +
- v1.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
-
- v2 = parseFloat(parseInt(v2.shift(), 10) + '.' +
- v2.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
-
-
- return v1 > v2;
- };
-
-
- // Work out the full mimeType (including the currently installed version)
- // of the installer.
- var findMimeTypeAndVersion = function findMimeTypeAndVersion () {
-
- if (updaterMimeType !== void 0) {
- return updaterMimeType;
- }
-
- var activeXControlId = 'TokBox.otiePluginInstaller',
- unversionedMimeType = 'application/x-otieplugininstaller',
- plugin = navigator.plugins[activeXControlId];
-
- installedVersion = -1;
-
-
- if (plugin) {
- // Look through the supported mime-types for the version
- // There should only be one mime-type in our use case, and
- // if there's more than one they should all have the same
- // version.
- var numMimeTypes = plugin.length,
- extractVersion = new RegExp(unversionedMimeType.replace('-', '\\-') +
- ',version=([0-9]+)', 'i'),
- mimeType,
- bits;
-
- for (var i=0; i<numMimeTypes; ++i) {
- mimeType = plugin[i];
-
- // Look through the supported mimeTypes and find
- // the newest one.
- if (mimeType && mimeType.enabledPlugin &&
- (mimeType.enabledPlugin.name === plugin.name) &&
- mimeType.type.indexOf(unversionedMimeType) !== -1) {
-
- bits = extractVersion.exec(mimeType.type);
-
- if (bits !== null && versionGreaterThan(bits[1], installedVersion)) {
- installedVersion = bits[1];
- }
- }
- }
- }
- else {
- // This may mean that the installer plugin is not installed.
- // Although it could also mean that we're on IE 9 and below,
- // which does not support navigator.plugins. Fallback to
- // using 'ActiveXObject' instead.
- try {
- plugin = new ActiveXObject(activeXControlId);
- installedVersion = plugin.getMasterVersion();
- } catch(e) {
- }
- }
-
- updaterMimeType = installedVersion !== -1 ?
- unversionedMimeType + ',version=' + installedVersion :
- null;
- };
-
- var getInstallerMimeType = function getInstallerMimeType () {
- if (updaterMimeType === void 0) {
- findMimeTypeAndVersion();
- }
-
- return updaterMimeType;
- };
-
- var getInstalledVersion = function getInstalledVersion () {
- if (installedVersion === void 0) {
- findMimeTypeAndVersion();
- }
-
- return installedVersion;
- };
-
- // Version 0.4.0.4 autoupdate was broken. We want to prompt
- // for install on 0.4.0.4 or earlier. We're also including
- // earlier versions just in case...
- var hasBrokenUpdater = function () {
- var _broken = !versionGreaterThan(getInstalledVersion(), '0.4.0.4');
-
- hasBrokenUpdater = function() { return _broken; };
- return _broken;
- };
-
-
- AutoUpdater = function (plugin) {
-
- // Returns true if the version of the plugin installed on this computer
- // does not match the one expected by this version of TBPlugin.
- this.isOutOfDate = function () {
- return versionGreaterThan(pluginInfo.version, getInstalledVersion());
- };
-
- this.autoUpdate = function () {
- var modal = OT.Dialogs.Plugin.updateInProgress(),
- analytics = new OT.Analytics(),
- payload = {
- ieVersion: OT.$.browserVersion().version,
- pluginOldVersion: TBPlugin.installedVersion(),
- pluginNewVersion: TBPlugin.version()
- };
-
- var success = curryCallAsync(function() {
- analytics.logEvent({
- action: 'OTPluginAutoUpdate',
- variation: 'Success',
- partnerId: OT.APIKEY,
- payload: JSON.stringify(payload)
- });
-
- plugin.destroy();
-
- modal.close();
- OT.Dialogs.Plugin.updateComplete().on({
- reload: function() {
- window.location.reload();
- }
- });
- }),
-
- error = curryCallAsync(function(errorCode, errorMessage, systemErrorCode) {
- payload.errorCode = errorCode;
- payload.systemErrorCode = systemErrorCode;
-
- analytics.logEvent({
- action: 'OTPluginAutoUpdate',
- variation: 'Failure',
- partnerId: OT.APIKEY,
- payload: JSON.stringify(payload)
- });
-
- plugin.destroy();
-
- modal.close();
- var updateMessage = errorMessage + ' (' + errorCode +
- '). Please restart your browser and try again.';
-
- modal = OT.Dialogs.Plugin.updateComplete(updateMessage).on({
- 'reload': function() {
- modal.close();
- }
- });
-
- OT.error('autoUpdate failed: ' + errorMessage + ' (' + errorCode +
- '). Please restart your browser and try again.');
- // TODO log client event
- }),
-
- progress = curryCallAsync(function(progress) {
- modal.setUpdateProgress(progress.toFixed());
- // modalBody.innerHTML = 'Updating...' + progress.toFixed() + '%';
- });
-
- plugin._.updatePlugin(TBPlugin.pathToInstaller(), success, error, progress);
- };
-
- this.destroy = function() {
- plugin.destroy();
- };
- };
-
- AutoUpdater.get = function (completion) {
- if (autoUpdaterController) {
- completion.call(null, void 0, autoUpdaterController);
- return;
- }
-
- if (!this.isinstalled()) {
- completion.call(null, 'Plugin was not installed');
- return;
- }
-
- injectObject(getInstallerMimeType(), false, {windowless: false}, function(err, plugin) {
- if (plugin) autoUpdaterController = new AutoUpdater(plugin);
- completion.call(null, err, autoUpdaterController);
- });
- };
-
- AutoUpdater.isinstalled = function () {
- return getInstallerMimeType() !== null && !hasBrokenUpdater();
- };
-
- AutoUpdater.installedVersion = function () {
- return getInstalledVersion();
- };
-
-})();
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-// tb_require('./plugin_object.js')
-// tb_require('./video_container.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
- trailing: true, browser: true, smarttabs:true */
-/* global OT:true, VideoContainer:true */
-/* exported MediaStream */
-
-var MediaStreamTrack = function MediaStreamTrack (mediaStreamId, options, plugin) {
- this.id = options.id;
- this.kind = options.kind;
- this.label = options.label;
- this.enabled = OT.$.castToBoolean(options.enabled);
- this.streamId = mediaStreamId;
-
- this.setEnabled = function (enabled) {
- this.enabled = OT.$.castToBoolean(enabled);
-
- if (this.enabled) {
- plugin._.enableMediaStreamTrack(mediaStreamId, this.id);
- }
- else {
- plugin._.disableMediaStreamTrack(mediaStreamId, this.id);
- }
- };
-};
-
-var MediaStream = function MediaStream (options, plugin) {
- var audioTracks = [],
- videoTracks = [];
-
- this.id = options.id;
- plugin.addRef(this);
-
- // TODO
- // this.ended =
- // this.onended =
-
- if (options.videoTracks) {
- options.videoTracks.map(function(track) {
- videoTracks.push( new MediaStreamTrack(options.id, track, plugin) );
- });
- }
-
- if (options.audioTracks) {
- options.audioTracks.map(function(track) {
- audioTracks.push( new MediaStreamTrack(options.id, track, plugin) );
- });
- }
-
- var hasTracksOfType = function (type) {
- var tracks = type === 'video' ? videoTracks : audioTracks;
-
- return OT.$.some(tracks, function(track) {
- return track.enabled;
- });
- };
-
- this.getVideoTracks = function () { return videoTracks; };
- this.getAudioTracks = function () { return audioTracks; };
-
- this.getTrackById = function (id) {
- videoTracks.concat(audioTracks).forEach(function(track) {
- if (track.id === id) return track;
- });
-
- return null;
- };
-
- this.hasVideo = function () {
- return hasTracksOfType('video');
- };
-
- this.hasAudio = function () {
- return hasTracksOfType('audio');
- };
-
- this.addTrack = function (/* MediaStreamTrack */) {
- // TODO
- };
-
- this.removeTrack = function (/* MediaStreamTrack */) {
- // TODO
- };
-
- this.stop = function() {
- plugin._.stopMediaStream(this.id);
- plugin.removeRef(this);
- };
-
- this.destroy = function() {
- this.stop();
- };
-
- // Private MediaStream API
- this._ = {
- plugin: plugin,
-
- // Get a VideoContainer to render the stream in.
- render: OT.$.bind(function() {
- return new VideoContainer(plugin, this);
- }, this)
- };
-};
-
-
-MediaStream.fromJson = function (json, plugin) {
- if (!json) return null;
- return new MediaStream( JSON.parse(json), plugin );
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-
-/* global OT:true */
-/* exported PluginRumorSocket */
-
-var PluginRumorSocket = function(plugin, server) {
- var connected = false,
- rumorID;
-
- try {
- rumorID = plugin._.RumorInit(server, '');
- }
- catch(e) {
- OT.error('Error creating the Rumor Socket: ', e.message);
- }
-
- if(!rumorID) {
- throw new Error('Could not initialise plugin rumor connection');
- }
-
- var socket = {
- open: function() {
- connected = true;
- plugin._.RumorOpen(rumorID);
- },
-
- close: function(code, reason) {
- if (!connected) return;
- connected = false;
-
- plugin._.RumorClose(rumorID, code, reason);
- plugin.removeRef(this);
- },
-
- destroy: function() {
- this.close();
- },
-
- send: function(msg) {
- plugin._.RumorSend(rumorID, msg.type, msg.toAddress,
- JSON.parse(JSON.stringify(msg.headers)), msg.data);
- },
-
- onOpen: function(callback) {
- plugin._.SetOnRumorOpen(rumorID, callback);
- },
-
- onClose: function(callback) {
- plugin._.SetOnRumorClose(rumorID, callback);
- },
-
- onError: function(callback) {
- plugin._.SetOnRumorError(rumorID, callback);
- },
-
- onMessage: function(callback) {
- plugin._.SetOnRumorMessage(rumorID, callback);
- }
- };
-
- plugin.addRef(socket);
- return socket;
-
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-// tb_require('./plugin_object.js')
-// tb_require('./video_container.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
- trailing: true, browser: true, smarttabs:true */
-/* global OT:true */
-/* exported MediaConstraints */
-
-var MediaConstraints = function(userConstraints) {
- var constraints = OT.$.clone(userConstraints);
-
- this.hasVideo = constraints.video !== void 0 && constraints.video !== false;
- this.hasAudio = constraints.audio !== void 0 && constraints.audio !== false;
-
- if (constraints.video === true) constraints.video = {};
- if (constraints.audio === true) constraints.audio = {};
-
- if (this.hasVideo && !constraints.video.mandatory) {
- constraints.video.mandatory = {};
- }
-
- if (this.hasAudio && !constraints.audio.mandatory) {
- constraints.audio.mandatory = {};
- }
-
- this.screenSharing = this.hasVideo &&
- ( constraints.video.mandatory.chromeMediaSource === 'screen' ||
- constraints.video.mandatory.chromeMediaSource === 'window' );
-
- this.audio = constraints.audio;
- this.video = constraints.video;
-
- this.setVideoSource = function(sourceId) {
- if (sourceId !== void 0) constraints.video.mandatory.sourceId = sourceId;
- else delete constraints.video;
- };
-
- this.setAudioSource = function(sourceId) {
- if (sourceId !== void 0) constraints.audio.mandatory.sourceId = sourceId;
- else delete constraints.audio;
- };
-
- this.toHash = function() {
- return constraints;
- };
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-// tb_require('./plugin_object.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
- trailing: true, browser: true, smarttabs:true */
/* exported RTCStatsReport */
var RTCStatsReport = function (reports) {
this.forEach = function (callback, context) {
for (var id in reports) {
callback.call(context, reports[id]);
}
};
@@ -5033,16 +4483,404 @@ var PeerConnection = function PeerConnec
};
// tb_require('./header.js')
// tb_require('./shims.js')
// tb_require('./plugin_object.js')
+// tb_require('./video_container.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+ trailing: true, browser: true, smarttabs:true */
+/* global OT:true, VideoContainer:true */
+/* exported MediaStream */
+
+var MediaStreamTrack = function MediaStreamTrack (mediaStreamId, options, plugin) {
+ this.id = options.id;
+ this.kind = options.kind;
+ this.label = options.label;
+ this.enabled = OT.$.castToBoolean(options.enabled);
+ this.streamId = mediaStreamId;
+
+ this.setEnabled = function (enabled) {
+ this.enabled = OT.$.castToBoolean(enabled);
+
+ if (this.enabled) {
+ plugin._.enableMediaStreamTrack(mediaStreamId, this.id);
+ }
+ else {
+ plugin._.disableMediaStreamTrack(mediaStreamId, this.id);
+ }
+ };
+};
+
+var MediaStream = function MediaStream (options, plugin) {
+ var audioTracks = [],
+ videoTracks = [];
+
+ this.id = options.id;
+ plugin.addRef(this);
+
+ // TODO
+ // this.ended =
+ // this.onended =
+
+ if (options.videoTracks) {
+ options.videoTracks.map(function(track) {
+ videoTracks.push( new MediaStreamTrack(options.id, track, plugin) );
+ });
+ }
+
+ if (options.audioTracks) {
+ options.audioTracks.map(function(track) {
+ audioTracks.push( new MediaStreamTrack(options.id, track, plugin) );
+ });
+ }
+
+ var hasTracksOfType = function (type) {
+ var tracks = type === 'video' ? videoTracks : audioTracks;
+
+ return OT.$.some(tracks, function(track) {
+ return track.enabled;
+ });
+ };
+
+ this.getVideoTracks = function () { return videoTracks; };
+ this.getAudioTracks = function () { return audioTracks; };
+
+ this.getTrackById = function (id) {
+ videoTracks.concat(audioTracks).forEach(function(track) {
+ if (track.id === id) return track;
+ });
+
+ return null;
+ };
+
+ this.hasVideo = function () {
+ return hasTracksOfType('video');
+ };
+
+ this.hasAudio = function () {
+ return hasTracksOfType('audio');
+ };
+
+ this.addTrack = function (/* MediaStreamTrack */) {
+ // TODO
+ };
+
+ this.removeTrack = function (/* MediaStreamTrack */) {
+ // TODO
+ };
+
+ this.stop = function() {
+ plugin._.stopMediaStream(this.id);
+ plugin.removeRef(this);
+ };
+
+ this.destroy = function() {
+ this.stop();
+ };
+
+ // Private MediaStream API
+ this._ = {
+ plugin: plugin,
+
+ // Get a VideoContainer to render the stream in.
+ render: OT.$.bind(function() {
+ return new VideoContainer(plugin, this);
+ }, this)
+ };
+};
+
+
+MediaStream.fromJson = function (json, plugin) {
+ if (!json) return null;
+ return new MediaStream( JSON.parse(json), plugin );
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+// tb_require('./plugin_object.js')
+// tb_require('./video_container.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+ trailing: true, browser: true, smarttabs:true */
+/* global OT:true */
+/* exported MediaConstraints */
+
+var MediaConstraints = function(userConstraints) {
+ var constraints = OT.$.clone(userConstraints);
+
+ this.hasVideo = constraints.video !== void 0 && constraints.video !== false;
+ this.hasAudio = constraints.audio !== void 0 && constraints.audio !== false;
+
+ if (constraints.video === true) constraints.video = {};
+ if (constraints.audio === true) constraints.audio = {};
+
+ if (this.hasVideo && !constraints.video.mandatory) {
+ constraints.video.mandatory = {};
+ }
+
+ if (this.hasAudio && !constraints.audio.mandatory) {
+ constraints.audio.mandatory = {};
+ }
+
+ this.screenSharing = this.hasVideo &&
+ ( constraints.video.mandatory.chromeMediaSource === 'screen' ||
+ constraints.video.mandatory.chromeMediaSource === 'window' );
+
+ this.audio = constraints.audio;
+ this.video = constraints.video;
+
+ this.setVideoSource = function(sourceId) {
+ if (sourceId !== void 0) constraints.video.mandatory.sourceId = sourceId;
+ else delete constraints.video;
+ };
+
+ this.setAudioSource = function(sourceId) {
+ if (sourceId !== void 0) constraints.audio.mandatory.sourceId = sourceId;
+ else delete constraints.audio;
+ };
+
+ this.toHash = function() {
+ return constraints;
+ };
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+// tb_require('./plugin_object.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+ trailing: true, browser: true, smarttabs:true */
+/* global OT:true, TBPlugin:true, pluginInfo:true, ActiveXObject:true,
+ injectObject:true, curryCallAsync:true */
+
+/* exported AutoUpdater:true */
+var AutoUpdater;
+
+(function() {
+
+ var autoUpdaterController,
+ updaterMimeType, // <- cached version, use getInstallerMimeType instead
+ installedVersion = -1; // <- cached version, use getInstallerMimeType instead
+
+
+ var versionGreaterThan = function versionGreaterThan (version1,version2) {
+ if (version1 === version2) return false;
+
+ var v1 = version1.split('.'),
+ v2 = version2.split('.');
+
+ v1 = parseFloat(parseInt(v1.shift(), 10) + '.' +
+ v1.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
+
+ v2 = parseFloat(parseInt(v2.shift(), 10) + '.' +
+ v2.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
+
+
+ return v1 > v2;
+ };
+
+
+ // Work out the full mimeType (including the currently installed version)
+ // of the installer.
+ var findMimeTypeAndVersion = function findMimeTypeAndVersion () {
+
+ if (updaterMimeType !== void 0) {
+ return updaterMimeType;
+ }
+
+ var activeXControlId = 'TokBox.otiePluginInstaller',
+ unversionedMimeType = 'application/x-otieplugininstaller',
+ plugin = navigator.plugins[activeXControlId];
+
+ installedVersion = -1;
+
+
+ if (plugin) {
+ // Look through the supported mime-types for the version
+ // There should only be one mime-type in our use case, and
+ // if there's more than one they should all have the same
+ // version.
+ var numMimeTypes = plugin.length,
+ extractVersion = new RegExp(unversionedMimeType.replace('-', '\\-') +
+ ',version=([0-9]+)', 'i'),
+ mimeType,
+ bits;
+
+ for (var i=0; i<numMimeTypes; ++i) {
+ mimeType = plugin[i];
+
+ // Look through the supported mimeTypes and find
+ // the newest one.
+ if (mimeType && mimeType.enabledPlugin &&
+ (mimeType.enabledPlugin.name === plugin.name) &&
+ mimeType.type.indexOf(unversionedMimeType) !== -1) {
+
+ bits = extractVersion.exec(mimeType.type);
+
+ if (bits !== null && versionGreaterThan(bits[1], installedVersion)) {
+ installedVersion = bits[1];
+ }
+ }
+ }
+ }
+ else {
+ // This may mean that the installer plugin is not installed.
+ // Although it could also mean that we're on IE 9 and below,
+ // which does not support navigator.plugins. Fallback to
+ // using 'ActiveXObject' instead.
+ try {
+ plugin = new ActiveXObject(activeXControlId);
+ installedVersion = plugin.getMasterVersion();
+ } catch(e) {
+ }
+ }
+
+ updaterMimeType = installedVersion !== -1 ?
+ unversionedMimeType + ',version=' + installedVersion :
+ null;
+ };
+
+ var getInstallerMimeType = function getInstallerMimeType () {
+ if (updaterMimeType === void 0) {
+ findMimeTypeAndVersion();
+ }
+
+ return updaterMimeType;
+ };
+
+ var getInstalledVersion = function getInstalledVersion () {
+ if (installedVersion === void 0) {
+ findMimeTypeAndVersion();
+ }
+
+ return installedVersion;
+ };
+
+ // Version 0.4.0.4 autoupdate was broken. We want to prompt
+ // for install on 0.4.0.4 or earlier. We're also including
+ // earlier versions just in case...
+ var hasBrokenUpdater = function () {
+ var _broken = !versionGreaterThan(getInstalledVersion(), '0.4.0.4');
+
+ hasBrokenUpdater = function() { return _broken; };
+ return _broken;
+ };
+
+
+ AutoUpdater = function (plugin) {
+
+ // Returns true if the version of the plugin installed on this computer
+ // does not match the one expected by this version of TBPlugin.
+ this.isOutOfDate = function () {
+ return versionGreaterThan(pluginInfo.version, getInstalledVersion());
+ };
+
+ this.autoUpdate = function () {
+ var modal = OT.Dialogs.Plugin.updateInProgress(),
+ analytics = new OT.Analytics(),
+ payload = {
+ ieVersion: OT.$.browserVersion().version,
+ pluginOldVersion: TBPlugin.installedVersion(),
+ pluginNewVersion: TBPlugin.version()
+ };
+
+ var success = curryCallAsync(function() {
+ analytics.logEvent({
+ action: 'OTPluginAutoUpdate',
+ variation: 'Success',
+ partnerId: OT.APIKEY,
+ payload: JSON.stringify(payload)
+ });
+
+ plugin.destroy();
+
+ modal.close();
+ OT.Dialogs.Plugin.updateComplete().on({
+ reload: function() {
+ window.location.reload();
+ }
+ });
+ }),
+
+ error = curryCallAsync(function(errorCode, errorMessage, systemErrorCode) {
+ payload.errorCode = errorCode;
+ payload.systemErrorCode = systemErrorCode;
+
+ analytics.logEvent({
+ action: 'OTPluginAutoUpdate',
+ variation: 'Failure',
+ partnerId: OT.APIKEY,
+ payload: JSON.stringify(payload)
+ });
+
+ plugin.destroy();
+
+ modal.close();
+ var updateMessage = errorMessage + ' (' + errorCode +
+ '). Please restart your browser and try again.';
+
+ modal = OT.Dialogs.Plugin.updateComplete(updateMessage).on({
+ 'reload': function() {
+ modal.close();
+ }
+ });
+
+ OT.error('autoUpdate failed: ' + errorMessage + ' (' + errorCode +
+ '). Please restart your browser and try again.');
+ // TODO log client event
+ }),
+
+ progress = curryCallAsync(function(progress) {
+ modal.setUpdateProgress(progress.toFixed());
+ // modalBody.innerHTML = 'Updating...' + progress.toFixed() + '%';
+ });
+
+ plugin._.updatePlugin(TBPlugin.pathToInstaller(), success, error, progress);
+ };
+
+ this.destroy = function() {
+ plugin.destroy();
+ };
+ };
+
+ AutoUpdater.get = function (completion) {
+ if (autoUpdaterController) {
+ completion.call(null, void 0, autoUpdaterController);
+ return;
+ }
+
+ if (!this.isinstalled()) {
+ completion.call(null, 'Plugin was not installed');
+ return;
+ }
+
+ injectObject(getInstallerMimeType(), false, {windowless: false}, function(err, plugin) {
+ if (plugin) autoUpdaterController = new AutoUpdater(plugin);
+ completion.call(null, err, autoUpdaterController);
+ });
+ };
+
+ AutoUpdater.isinstalled = function () {
+ return getInstallerMimeType() !== null && !hasBrokenUpdater();
+ };
+
+ AutoUpdater.installedVersion = function () {
+ return getInstalledVersion();
+ };
+
+})();
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+// tb_require('./plugin_object.js')
// tb_require('./auto_updater.js')
// tb_require('./media_constraints.js')
// tb_require('./peer_connection.js')
// tb_require('./media_stream.js')
// tb_require('./video_container.js')
// tb_require('./rumor.js')
/* jshint globalstrict: true, strict: false, undef: true,
@@ -7012,17 +6850,17 @@ waitForDomReady();
// Map of camel-cased keys to underscored
camelCasedKeys,
browser = OT.$.browserVersion(),
send = function(data, isQos, callback) {
OT.$.post((isQos ? endPointQos : endPoint) + '?_=' + OT.$.uuid.v4(), {
body: data,
- xdomainrequest: (browser.browser === 'IE' & browser.version < 10),
+ xdomainrequest: (browser.browser === 'IE' && browser.version < 10),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}, callback);
},
throttledPost = function() {
// Throttle logs so that they only happen 1 at a time
@@ -9168,17 +9006,17 @@ waitForDomReady();
*
* Original source: https://github.com/inexorabletash/text-encoding
***/
(function(global) {
'use strict';
var browser = OT.$.browserVersion();
- if(browser.browser === 'IE' && browser.version < 10) {
+ if(browser && browser.browser === 'IE' && browser.version < 10) {
return; // IE 8 doesn't do websockets. No websockets, no encoding.
}
if ( (global.TextEncoder !== void 0) && (global.TextDecoder !== void 0)) {
// defer to the native ones
// @todo is this a good idea?
return;
}
@@ -13533,18 +13371,20 @@ waitForDomReady();
if (session.archives.has(dict.id)) return;
var archive = parseArchive(dict);
session.archives.add(archive);
return archive;
}
- var sessionRead;
- var sessionReadQueue = [];
+ var sessionRead,
+ sessionReadQueue = [],
+ // streams for which corresponding connectionCreated events have not been dispatched:
+ unconnectedStreams = {};
function sessionReadQueuePush(type, args) {
var triggerArgs = ['signal'];
triggerArgs.push.apply(triggerArgs, args);
sessionReadQueue.push(triggerArgs);
}
window.OT.SessionDispatcher = function(session) {
@@ -13604,25 +13444,39 @@ waitForDomReady();
});
dispatcher.on('connection#created', function(connection) {
connection = OT.Connection.fromHash(connection);
if (session.connection && connection.id !== session.connection.id) {
session.connections.add( connection );
}
+
+ OT.$.forEach(OT.$.keys(unconnectedStreams), function(streamId) {
+ var stream = unconnectedStreams[streamId];
+ if (stream && connection.id === stream.connection.id) {
+ // dispatch streamCreated event now that the connectionCreated has been dispatched
+ parseAndAddStreamToSession(stream, session);
+ delete unconnectedStreams[stream.id];
+ }
+ });
});
dispatcher.on('connection#deleted', function(connection, reason) {
connection = session.connections.get(connection);
connection.destroy(reason);
});
dispatcher.on('stream#created', function(stream, transactionId) {
- stream = parseAndAddStreamToSession(stream, session);
+ var connectionId = stream.connectionId ? stream.connectionId : stream.connection.id;
+ if (session.connections.has(connectionId)) {
+ stream = parseAndAddStreamToSession(stream, session);
+ } else {
+ unconnectedStreams[stream.id] = stream;
+ }
if (stream.publisher) {
stream.publisher.setStream(stream);
}
dispatcher.triggerCallback(transactionId, null, stream);
});
@@ -14767,16 +14621,32 @@ waitForDomReady();
this.destroy = function() {};
};
})(window);
!(function() {
+ /**
+ * Lazy instantiates an audio context and always return the same instance on following calls
+ *
+ * @returns {AudioContext}
+ */
+ OT.audioContext = function() {
+ var context = new window.AudioContext();
+ OT.audioContext = function() {
+ return context;
+ };
+ return context;
+ };
+
+})();
+!(function() {
+
/*
* A <code>RTCPeerConnection.getStats</code> based audio level sampler.
*
* It uses the the <code>getStats</code> method to get the <code>audioOutputLevel</code>.
* This implementation expects the single parameter version of the <code>getStats</code> method.
*
* Currently the <code>audioOutputLevel</code> stats is only supported in Chrome.
@@ -14929,17 +14799,150 @@ waitForDomReady();
this.stop = function() {
window.clearInterval(_intervalId);
_intervalId = null;
};
};
})(window);
+// tb_require('../../helpers/helpers.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+ trailing: true, browser: true, smarttabs:true */
+/* global OT */
+/* exported SDPHelpers */
+
+var findIndex = function(array, iter, ctx) {
+ if (!OT.$.isFunction(iter)) {
+ throw new TypeError('iter must be a function');
+ }
+
+ for (var i = 0, count = array.length || 0; i < count; ++i) {
+ if (i in array && iter.call(ctx, array[i], i, array)) {
+ return i;
+ }
+ }
+
+ return -1;
+};
+
+// Here are the structure of the rtpmap attribute and the media line, most of the
+// complex Regular Expressions in this code are matching against one of these two
+// formats:
+// * a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
+// * m=<media> <port>/<number of ports> <proto> <fmts>
+//
+// References:
+// * https://tools.ietf.org/html/rfc4566
+// * http://en.wikipedia.org/wiki/Session_Description_Protocol
+//
+var SDPHelpers = {
+ // Search through sdpLines to find the Media Line of type +mediaType+.
+ getMLineIndex: function getMLineIndex(sdpLines, mediaType) {
+ var targetMLine = 'm=' + mediaType;
+
+ // Find the index of the media line for +type+
+ return findIndex(sdpLines, function(line) {
+ if (line.indexOf(targetMLine) !== -1) {
+ return true;
+ }
+
+ return false;
+ });
+ },
+
+ // Extract the payload types for a give Media Line.
+ //
+ getMLinePayloadTypes: function getMLinePayloadTypes (mediaLine, mediaType) {
+ var mLineSelector = new RegExp('^m=' + mediaType +
+ ' \\d+(/\\d+)? [a-zA-Z0-9/]+(( [a-zA-Z0-9/]+)+)$', 'i');
+
+ // Get all payload types that the line supports
+ var payloadTypes = mediaLine.match(mLineSelector);
+ if (!payloadTypes || payloadTypes.length < 2) {
+ // Error, invalid M line?
+ return [];
+ }
+
+ return OT.$.trim(payloadTypes[2]).split(' ');
+ },
+
+ removeTypesFromMLine: function removeTypesFromMLine (mediaLine, payloadTypes) {
+ return mediaLine.replace(new RegExp(' ' + payloadTypes.join(' |'), 'ig') , '')
+ .replace(/\s+/g, ' ');
+ },
+
+
+ // Remove all references to a particular encodingName from a particular media type
+ //
+ removeMediaEncoding: function removeMediaEncoding (sdp, mediaType, encodingName) {
+ var sdpLines = sdp.split('\r\n'),
+ mLineIndex = SDPHelpers.getMLineIndex(sdpLines, mediaType),
+ mLine = mLineIndex > -1 ? sdpLines[mLineIndex] : void 0,
+ typesToRemove = [],
+ payloadTypes,
+ match;
+
+ if (mLineIndex === -1) {
+ // Error, missing M line
+ return sdpLines.join('\r\n');
+ }
+
+ // Get all payload types that the line supports
+ payloadTypes = SDPHelpers.getMLinePayloadTypes(mLine, mediaType);
+ if (payloadTypes.length === 0) {
+ // Error, invalid M line?
+ return sdpLines.join('\r\n');
+ }
+
+ // Find the location of all the rtpmap lines that relate to +encodingName+
+ // and any of the supported payload types
+ var matcher = new RegExp('a=rtpmap:(' + payloadTypes.join('|') + ') ' +
+ encodingName + '\\/\\d+', 'i');
+
+ sdpLines = OT.$.filter(sdpLines, function(line, index) {
+ match = line.match(matcher);
+ if (match === null) return true;
+
+ typesToRemove.push(match[1]);
+
+ if (index < mLineIndex) {
+ // This removal changed the index of the mline, track it
+ mLineIndex--;
+ }
+
+ // remove this one
+ return false;
+ });
+
+ if (typesToRemove.length > 0 && mLineIndex > -1) {
+ // Remove all the payload types and we've removed from the media line
+ sdpLines[mLineIndex] = SDPHelpers.removeTypesFromMLine(mLine, typesToRemove);
+ }
+
+ return sdpLines.join('\r\n');
+ },
+
+ // Removes all Confort Noise from +sdp+.
+ //
+ // See https://jira.tokbox.com/browse/OPENTOK-7176
+ //
+ removeComfortNoise: function removeComfortNoise (sdp) {
+ return SDPHelpers.removeMediaEncoding(sdp, 'audio', 'CN');
+ },
+
+ removeVideoCodec: function removeVideoCodec (sdp, codec) {
+ return SDPHelpers.removeMediaEncoding(sdp, 'video', codec);
+ }
+};
+
+
!(function(window) {
+ /* global SDPHelpers */
// Normalise these
var NativeRTCSessionDescription,
NativeRTCIceCandidate;
if (!TBPlugin.isInstalled()) {
// order is very important: 'RTCSessionDescription' defined in Firefox Nighly but useless
NativeRTCSessionDescription = (window.mozRTCSessionDescription ||
@@ -14998,84 +15001,16 @@ waitForDomReady();
this.processPending = function() {
while(_pendingIceCandidates.length) {
_peerConnection.addIceCandidate(_pendingIceCandidates.shift());
}
};
};
- // Removes all Confort Noise from +sdp+.
- //
- // See https://jira.tokbox.com/browse/OPENTOK-7176
- //
- var removeComfortNoise = function removeComfortNoise (sdp) {
- // a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
- var matcher = /a=rtpmap:(\d+) CN\/\d+/i,
- payloadTypes = [],
- audioMediaLineIndex,
- sdpLines,
- match;
-
- // Icky code. This filter operation has two side effects in addition
- // to doing the actual filtering:
- // 1. extract all the payload types from the rtpmap CN lines
- // 2. find the index of the audio media line
- //
- sdpLines = OT.$.filter(sdp.split('\r\n'), function(line, index) {
- if (line.indexOf('m=audio') !== -1) audioMediaLineIndex = index;
-
- match = line.match(matcher);
- if (match !== null) {
- payloadTypes.push(match[1]);
-
- // remove this line as it contains CN
- return false;
- }
-
- return true;
- });
-
- if (payloadTypes.length && audioMediaLineIndex) {
- // Remove all CN payload types from the audio media line.
- sdpLines[audioMediaLineIndex] = sdpLines[audioMediaLineIndex].replace(
- new RegExp(payloadTypes.join('|'), 'ig') , '').replace(/\s+/g, ' ');
- }
-
- return sdpLines.join('\r\n');
- };
-
- var removeVideoCodec = function removeVideoCodec (sdp, codec) {
- var matcher = new RegExp('a=rtpmap:(\\d+) ' + codec + '\\/\\d+', 'i'),
- payloadTypes = [],
- videoMediaLineIndex,
- sdpLines,
- match;
-
- sdpLines = OT.$.filter(sdp.split('\r\n'), function(line, index) {
- if (line.indexOf('m=video') !== -1) videoMediaLineIndex = index;
-
- match = line.match(matcher);
- if (match !== null) {
- payloadTypes.push(match[1]);
-
- // remove this line as it contains the codec
- return false;
- }
-
- return true;
- });
-
- if (payloadTypes.length && videoMediaLineIndex) {
- sdpLines[videoMediaLineIndex] = sdpLines[videoMediaLineIndex].replace(
- new RegExp(payloadTypes.join('|'), 'ig') , '').replace(/\s+/g, ' ');
- }
-
- return sdpLines.join('\r\n');
- };
// Attempt to completely process +offer+. This will:
// * set the offer as the remote description
// * create an answer and
// * set the new answer as the location description
//
// If there are no issues, the +success+ callback will be executed on completion.
// Errors during any step will result in the +failure+ callback being executed.
@@ -15090,19 +15025,19 @@ waitForDomReady();
OT.error(message);
OT.error(errorReason);
if (failure) failure(message, errorReason, prefix);
};
};
setLocalDescription = function(answer) {
- answer.sdp = removeComfortNoise(answer.sdp);
- answer.sdp = removeVideoCodec(answer.sdp, 'ulpfec');
- answer.sdp = removeVideoCodec(answer.sdp, 'red');
+ answer.sdp = SDPHelpers.removeComfortNoise(answer.sdp);
+ answer.sdp = SDPHelpers.removeVideoCodec(answer.sdp, 'ulpfec');
+ answer.sdp = SDPHelpers.removeVideoCodec(answer.sdp, 'red');
peerConnection.setLocalDescription(
answer,
// Success
function() {
success(answer);
},
@@ -15176,19 +15111,20 @@ waitForDomReady();
OT.error(message);
OT.error(errorReason);
if (failure) failure(message, errorReason, prefix);
};
};
setLocalDescription = function(offer) {
- offer.sdp = removeComfortNoise(offer.sdp);
- offer.sdp = removeVideoCodec(offer.sdp, 'ulpfec');
- offer.sdp = removeVideoCodec(offer.sdp, 'red');
+ offer.sdp = SDPHelpers.removeComfortNoise(offer.sdp);
+ offer.sdp = SDPHelpers.removeVideoCodec(offer.sdp, 'ulpfec');
+ offer.sdp = SDPHelpers.removeVideoCodec(offer.sdp, 'red');
+
peerConnection.setLocalDescription(
offer,
// Success
function() {
success(offer);
},
@@ -16581,52 +16517,39 @@ waitForDomReady();
// Whether to display the name. Possible values are: "auto" (the name is displayed
// when the stream is first displayed and when the user mouses over the display),
// "off" (the name is not displayed), and "on" (the name is displayed).
//
// displays a name
// can be shown/hidden
// can be destroyed
OT.Chrome.NamePanel = function(options) {
- var _name = options.name,
- _bugMode = options.bugMode;
+ var _name = options.name;
if (!_name || OT.$.trim(_name).length === '') {
_name = null;
// THere's no name, just flip the mode off
options.mode = 'off';
}
this.setName = OT.$.bind(function(name) {
if (!_name) this.setDisplayMode('auto');
_name = name;
this.domElement.innerHTML = _name;
});
- this.setBugMode = OT.$.bind(function(bugMode) {
- _bugMode = bugMode;
- if(bugMode === 'off') {
- OT.$.addClass(this.domElement, 'OT_name-no-bug');
- } else {
- OT.$.removeClass(this.domElement, 'OT_name-no-bug');
- }
- }, this);
-
// Mixin common widget behaviour
OT.Chrome.Behaviour.Widget(this, {
mode: options.mode,
nodeName: 'h1',
htmlContent: _name,
htmlAttributes: {
className: 'OT_name OT_edge-bar-item'
- },
- onCreate: OT.$.bind(function() {
- this.setBugMode(_bugMode);
- }, this)
+ }
});
};
})(window);
!(function() {
OT.Chrome.MuteButton = function(options) {
@@ -16693,33 +16616,16 @@ waitForDomReady();
onDestroy: OT.$.bind(detachEvents, this)
});
};
})(window);
!(function() {
- OT.Chrome.OpenTokButton = function(options) {
-
- // Mixin common widget behaviour
- OT.Chrome.Behaviour.Widget(this, {
- mode: options ? options.mode : null,
- nodeName: 'span',
- htmlContent: 'OpenTok',
- htmlAttributes: {
- className: 'OT_opentok OT_edge-bar-item'
- }
- });
-
- };
-
-})(window);
-!(function() {
-
// Archving Chrome Widget
//
// mode (String)
// Whether to display the archving widget. Possible values are: "on" (the status is displayed
// when archiving and briefly when archving ends) and "off" (the status is not displayed)
// Whether to display the archving widget. Possible values are: "auto" (the name is displayed
// when the status is first displayed and when the user mouses over the display),
@@ -16831,45 +16737,60 @@ waitForDomReady();
var widget = this,
_meterBarElement,
_voiceOnlyIconElement,
_meterValueElement,
_value,
_maxValue = options.maxValue || 1,
_minValue = options.minValue || 0;
+ function onCreate() {
+ _meterBarElement = OT.$.createElement('div', {
+ className: 'OT_audio-level-meter__bar'
+ }, '');
+ _meterValueElement = OT.$.createElement('div', {
+ className: 'OT_audio-level-meter__value'
+ }, '');
+ _voiceOnlyIconElement = OT.$.createElement('div', {
+ className: 'OT_audio-level-meter__audio-only-img'
+ }, '');
+
+ widget.domElement.appendChild(_meterBarElement);
+ widget.domElement.appendChild(_voiceOnlyIconElement);
+ widget.domElement.appendChild(_meterValueElement);
+ }
+
+ function updateView() {
+ var percentSize = _value * 100 / (_maxValue - _minValue);
+ _meterValueElement.style.width = _meterValueElement.style.height = 2 * percentSize + '%';
+ _meterValueElement.style.top = _meterValueElement.style.right = -percentSize + '%';
+ }
+
// Mixin common widget behaviour
- OT.Chrome.Behaviour.Widget(this, {
+ var widgetOptions = {
mode: options ? options.mode : 'auto',
nodeName: 'div',
htmlAttributes: {
className: 'OT_audio-level-meter'
},
- onCreate: function() {
- _meterBarElement = OT.$.createElement('div', {
- className: 'OT_audio-level-meter__bar'
- }, '');
- _meterValueElement = OT.$.createElement('div', {
- className: 'OT_audio-level-meter__value'
- }, '');
- _voiceOnlyIconElement = OT.$.createElement('div', {
- className: 'OT_audio-level-meter__audio-only-img'
- }, '');
-
- widget.domElement.appendChild(_meterBarElement);
- widget.domElement.appendChild(_voiceOnlyIconElement);
- widget.domElement.appendChild(_meterValueElement);
- }
- });
-
- function updateView() {
- var percentSize = _value * 100 / (_maxValue - _minValue);
- _meterValueElement.style.width = _meterValueElement.style.height = 2 * percentSize + '%';
- _meterValueElement.style.top = _meterValueElement.style.right = -percentSize + '%';
- }
+ onCreate: onCreate
+ };
+
+ OT.Chrome.Behaviour.Widget(this, widgetOptions);
+
+ // override
+ var _setDisplayMode = OT.$.bind(widget.setDisplayMode, widget);
+ widget.setDisplayMode = function(mode) {
+ _setDisplayMode(mode);
+ if (mode === 'off') {
+ if (options.onPassivate) options.onPassivate();
+ } else {
+ if (options.onActivate) options.onActivate();
+ }
+ };
widget.setValue = function(value) {
_value = value;
updateView();
};
};
})(window);
@@ -17177,24 +17098,22 @@ waitForDomReady();
isValidStyle,
castValue;
_COMPONENT_STYLES = [
'showMicButton',
'showSpeakerButton',
'nameDisplayMode',
'buttonDisplayMode',
- 'backgroundImageURI',
- 'bugDisplayMode'
+ 'backgroundImageURI'
];
_validStyleValues = {
buttonDisplayMode: ['auto', 'mini', 'mini-auto', 'off', 'on'],
nameDisplayMode: ['auto', 'off', 'on'],
- bugDisplayMode: ['auto', 'off', 'on'],
audioLevelDisplayMode: ['auto', 'off', 'on'],
showSettingsButton: [true, false],
showMicButton: [true, false],
backgroundImageURI: null,
showControlBar: [true, false],
showArchiveStatus: [true, false],
videoDisabledDisplayMode: ['auto', 'off', 'on']
};
@@ -17688,38 +17607,38 @@ waitForDomReady();
_audioLevelMeter,
_analytics = new OT.Analytics(),
_validResolutions,
_validFrameRates = [ 1, 7, 15, 30 ],
_prevStats,
_state,
_iceServers,
_audioLevelCapable = OT.$.hasCapabilities('webAudio'),
- _audioLevelSampler;
+ _audioLevelSampler,
+ _publisher = this;
_validResolutions = {
'320x240': {width: 320, height: 240},
'640x480': {width: 640, height: 480},
'1280x720': {width: 1280, height: 720}
};
_prevStats = {
'timeStamp' : OT.$.now()
};
OT.$.eventing(this);
if(_audioLevelCapable) {
- _audioLevelSampler = new OT.AnalyserAudioLevelSampler(new window.AudioContext());
-
- var publisher = this;
+ _audioLevelSampler = new OT.AnalyserAudioLevelSampler(OT.audioContext());
+
var audioLevelRunner = new OT.IntervalRunner(function() {
_audioLevelSampler.sample(function(audioInputLevel) {
OT.$.requestAnimationFrame(function() {
- publisher.dispatchEvent(
+ _publisher.dispatchEvent(
new OT.AudioLevelUpdatedEvent(audioInputLevel));
});
});
}, 60);
this.on({
'audioLevelUpdated:added': function(count) {
if (count === 1) {
@@ -17733,17 +17652,16 @@ waitForDomReady();
}
});
}
OT.StylableComponent(this, {
showArchiveStatus: true,
nameDisplayMode: 'auto',
buttonDisplayMode: 'auto',
- bugDisplayMode: 'auto',
audioLevelDisplayMode: 'auto',
backgroundImageURI: null
});
/// Private Methods
var logAnalyticsEvent = function(action, variation, payloadType, payload) {
_analytics.logEvent({
action: action,
@@ -17838,19 +17756,17 @@ waitForDomReady();
cleanupLocalStream();
_webRTCStream = webOTStream;
_microphone = new OT.Microphone(_webRTCStream, !_publishProperties.publishAudio);
this.publishVideo(_publishProperties.publishVideo &&
_webRTCStream.getVideoTracks().length > 0);
this.accessAllowed = true;
- this.dispatchEvent(
- new OT.Event(OT.Event.names.ACCESS_ALLOWED, false)
- );
+ this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_ALLOWED, false));
var videoContainerOptions = {
muted: true,
error: OT.$.bind(onVideoError, this)
};
_targetElement = _container.bindVideo(_webRTCStream,
videoContainerOptions,
@@ -17858,17 +17774,17 @@ waitForDomReady();
if (err) {
onLoadFailure.call(this, err);
return;
}
onLoaded.call(this);
}, this));
- if(_audioLevelSampler) {
+ if(_audioLevelSampler && webOTStream.getAudioTracks().length > 0) {
_audioLevelSampler.webOTStream = webOTStream;
}
},
onStreamAvailableError = function(error) {
OT.error('OT.Publisher.onStreamAvailableError ' + error.name + ': ' + error.message);
@@ -17896,115 +17812,34 @@ waitForDomReady();
_state.set('Failed');
this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH,
'Publisher Access Denied: Permission Denied' +
(error.message ? ': ' + error.message : '')));
logAnalyticsEvent('publish', 'Failure', 'reason',
'GetUserMedia:Publisher Access Denied: Permission Denied');
- var browser = OT.$.browserVersion();
-
- var event = new OT.Event(OT.Event.names.ACCESS_DENIED),
- defaultAction = function() {
- if(!event.isDefaultPrevented()) {
- if(browser.browser === 'Chrome') {
- if (_container) {
- _container.addError('', null, 'OT_publisher-denied-chrome');
- }
- if(!accessDialogWasOpened) {
- OT.Dialogs.AllowDeny.Chrome.previouslyDenied(window.location.hostname);
- } else {
- OT.Dialogs.AllowDeny.Chrome.deniedNow();
- }
- } else if(browser.browser === 'Firefox') {
- if(_container) {
- _container.addError('', 'Click the reload button in the URL bar to change ' +
- 'camera & mic settings.', 'OT_publisher-denied-firefox');
- }
- OT.Dialogs.AllowDeny.Firefox.denied().on({
- refresh: function() {
- window.location.reload();
- }
- });
- }
- }
- };
-
- this.dispatchEvent(event, defaultAction);
- },
-
- accessDialogPrompt,
- accessDialogChromeTimeout,
- accessDialogFirefoxTimeout,
+ this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_DENIED));
+ },
+
accessDialogWasOpened = false,
onAccessDialogOpened = function() {
accessDialogWasOpened = true;
logAnalyticsEvent('accessDialog', 'Opened', '', '');
- var browser = OT.$.browserVersion();
-
- this.dispatchEvent(
- new OT.Event(OT.Event.names.ACCESS_DIALOG_OPENED, true),
- function(event) {
- if(!event.isDefaultPrevented()) {
- if(browser.browser === 'Chrome') {
- accessDialogChromeTimeout = setTimeout(function() {
- accessDialogChromeTimeout = null;
- logAnalyticsEvent('allowDenyHelpers', 'show', 'version', 'Chrome');
- accessDialogPrompt = OT.Dialogs.AllowDeny.Chrome.initialPrompt();
- accessDialogPrompt.on('closeButtonClicked', function() {
- logAnalyticsEvent('allowDenyHelpers', 'dismissed', 'version', 'Chrome');
- });
- }, 5000);
- } else if(browser.browser === 'Firefox') {
- accessDialogFirefoxTimeout = setTimeout(function() {
- accessDialogFirefoxTimeout = null;
- logAnalyticsEvent('allowDenyHelpers', 'show', 'version', 'Firefox');
- accessDialogPrompt = OT.Dialogs.AllowDeny.Firefox.maybeDenied();
- accessDialogPrompt.on('closeButtonClicked', function() {
- logAnalyticsEvent('allowDenyHelpers', 'dismissed', 'version', 'Firefox');
- });
- }, 7000);
- }
- } else {
- logAnalyticsEvent('allowDenyHelpers', 'developerPrevented', '', '');
- }
- }
- );
+ this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_DIALOG_OPENED, true));
},
onAccessDialogClosed = function() {
logAnalyticsEvent('accessDialog', 'Closed', '', '');
- if(accessDialogChromeTimeout) {
- clearTimeout(accessDialogChromeTimeout);
- logAnalyticsEvent('allowDenyHelpers', 'notShown', 'version', 'Chrome');
- accessDialogChromeTimeout = null;
- }
-
- if(accessDialogFirefoxTimeout) {
- clearTimeout(accessDialogFirefoxTimeout);
- logAnalyticsEvent('allowDenyHelpers', 'notShown', 'version', 'Firefox');
- accessDialogFirefoxTimeout = null;
- }
-
- if(accessDialogPrompt) {
- accessDialogPrompt.close();
- var browser = OT.$.browserVersion();
- logAnalyticsEvent('allowDenyHelpers', 'closed', 'version', browser.browser);
- accessDialogPrompt = null;
- }
-
- this.dispatchEvent(
- new OT.Event(OT.Event.names.ACCESS_DIALOG_CLOSED, false)
- );
+ this.dispatchEvent( new OT.Event(OT.Event.names.ACCESS_DIALOG_CLOSED, false));
},
onVideoError = function(errorCode, errorReason) {
OT.error('OT.Publisher.onVideoError');
var message = errorReason + (errorCode ? ' (' + errorCode + ')' : '');
logAnalyticsEvent('stream', null, 'reason',
'Publisher while playing stream: ' + message);
@@ -18155,79 +17990,79 @@ waitForDomReady();
_chrome.muteButton.setDisplayMode(value);
_chrome.backingBar.setMuteMode(value);
break;
case 'audioLevelDisplayMode':
_chrome.audioLevel.setDisplayMode(value);
break;
- case 'bugDisplayMode':
- // bugDisplayMode can't be updated but is used by some partners
-
case 'backgroundImageURI':
_container.setBackgroundImageURI(value);
}
},
_createChrome = function() {
- if(this.getStyle('bugDisplayMode') === 'off') {
- logAnalyticsEvent('bugDisplayMode', 'createChrome', 'mode', 'off');
- }
if(!this.getStyle('showArchiveStatus')) {
logAnalyticsEvent('showArchiveStatus', 'createChrome', 'mode', 'off');
}
var widgets = {
backingBar: new OT.Chrome.BackingBar({
nameMode: !_publishProperties.name ? 'off' : this.getStyle('nameDisplayMode'),
muteMode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode'))
}),
name: new OT.Chrome.NamePanel({
name: _publishProperties.name,
- mode: this.getStyle('nameDisplayMode'),
- bugMode: this.getStyle('bugDisplayMode')
+ mode: this.getStyle('nameDisplayMode')
}),
muteButton: new OT.Chrome.MuteButton({
muted: _publishProperties.publishAudio === false,
mode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode'))
}),
- opentokButton: new OT.Chrome.OpenTokButton({
- mode: this.getStyle('bugDisplayMode')
- }),
-
archive: new OT.Chrome.Archiving({
show: this.getStyle('showArchiveStatus'),
archiving: false
})
};
- if(_audioLevelCapable) {
+ if (_audioLevelCapable) {
+ var audioLevelTransformer = new OT.AudioLevelTransformer();
+
+ var audioLevelUpdatedHandler = function(evt) {
+ _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+ };
+
_audioLevelMeter = new OT.Chrome.AudioLevelMeter({
- mode: this.getStyle('audioLevelDisplayMode')
- });
-
- var audioLevelTransformer = new OT.AudioLevelTransformer();
- this.on('audioLevelUpdated', function(evt) {
- _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+ mode: this.getStyle('audioLevelDisplayMode'),
+ onActivate: function() {
+ _publisher.on('audioLevelUpdated', audioLevelUpdatedHandler);
+ },
+ onPassivate: function() {
+ _publisher.off('audioLevelUpdated', audioLevelUpdatedHandler);
+ }
});
widgets.audioLevel = _audioLevelMeter;
}
_chrome = new OT.Chrome({
parent: _container.domElement
}).set(widgets).on({
muted: OT.$.bind(this.publishAudio, this, false),
unmuted: OT.$.bind(this.publishAudio, this, true)
});
+
+ if(_audioLevelMeter && this.getStyle('audioLevelDisplayMode') === 'auto') {
+ _audioLevelMeter[_container.audioOnly() ? 'show' : 'hide']();
+ }
},
reset = OT.$.bind(function() {
if (_chrome) {
_chrome.destroy();
_chrome = null;
}
@@ -18255,16 +18090,27 @@ waitForDomReady();
this.stream = _stream = null;
_loaded = false;
this.session = _session = null;
if (!_state.isDestroyed()) _state.set('NotPublishing');
}, this);
+ var setAudioOnly = function(audioOnly) {
+ if (_container) {
+ _container.audioOnly(audioOnly);
+ _container.showPoster(audioOnly);
+ }
+
+ if (_audioLevelMeter && _publisher.getStyle('audioLevelDisplayMode') === 'auto') {
+ _audioLevelMeter[audioOnly ? 'show' : 'hide']();
+ }
+ };
+
this.publish = function(targetElement, properties) {
OT.debug('OT.Publisher: publish');
if ( _state.isAttemptingToPublish() || _state.isPublishing() ) reset();
_state.set('GetUserMedia');
_publishProperties = OT.$.defaults(properties || {}, {
publishAudio : true,
@@ -18452,18 +18298,17 @@ waitForDomReady();
if (_session && _stream) {
_stream.setChannelActiveState('audio', value);
}
return this;
};
-
- /**
+ /**
* Starts publishing video (if it is currently not being published)
* when the <code>value</code> is <code>true</code>; stops publishing video
* (if it is currently being published) when the <code>value</code> is <code>false</code>.
*
* @param {Boolean} value Whether to start publishing video (<code>true</code>)
* or not (<code>false</code>).
*
* @see <a href="OT.html#initPublisher">OT.initPublisher()</a>
@@ -18485,20 +18330,17 @@ waitForDomReady();
// the value of publishVideo at this point. This will be tidied up shortly.
if (_webRTCStream) {
var videoTracks = _webRTCStream.getVideoTracks();
for (var i=0, num=videoTracks.length; i<num; ++i) {
videoTracks[i].setEnabled(value);
}
}
- if(_container) {
- _container.audioOnly(!value);
- _container.showPoster(!value);
- }
+ setAudioOnly(!value);
return this;
};
/**
* Deletes the Publisher object and removes it from the HTML DOM.
* <p>
@@ -18933,17 +18775,18 @@ waitForDomReady();
_audioVolume = 100,
_state,
_prevStats,
_lastSubscribeToVideoReason,
_audioLevelCapable = OT.$.hasCapabilities('audioOutputLevelStat') ||
OT.$.hasCapabilities('webAudioCapableRemoteStream'),
_audioLevelSampler,
_audioLevelRunner,
- _frameRateRestricted = false;
+ _frameRateRestricted = false,
+ _subscriber = this;
this.id = _domId;
this.widgetId = _widgetId;
this.session = _session;
_prevStats = {
timeStamp: OT.$.now()
};
@@ -18976,18 +18819,17 @@ waitForDomReady();
OT.StylableComponent(this, {
nameDisplayMode: 'auto',
buttonDisplayMode: 'auto',
audioLevelDisplayMode: 'auto',
videoDisabledIndicatorDisplayMode: 'auto',
backgroundImageURI: null,
showArchiveStatus: true,
- showMicButton: true,
- bugDisplayMode: 'auto'
+ showMicButton: true
});
var logAnalyticsEvent = function(action, variation, payloadType, payload) {
/* jshint camelcase:false*/
_analytics.logEvent({
action: action,
variation: variation,
payload_type: payloadType,
@@ -19156,17 +18998,18 @@ waitForDomReady();
width: _stream.videoDimensions.width,
height: _stream.videoDimensions.height,
videoOrientation: _stream.videoDimensions.orientation
});
onLoaded.call(this, null);
}, this));
- if (OT.$.hasCapabilities('webAudioCapableRemoteStream') && _audioLevelSampler) {
+ if (OT.$.hasCapabilities('webAudioCapableRemoteStream') && _audioLevelSampler &&
+ webOTStream.getAudioTracks().length > 0) {
_audioLevelSampler.webOTStream = webOTStream;
}
logAnalyticsEvent('createPeerConnection', 'StreamAdded', '', '');
this.trigger('streamAdded', this);
},
onRemoteStreamRemoved = function(webOTStream) {
@@ -19184,36 +19027,38 @@ waitForDomReady();
streamDestroyed = function () {
this.disconnect();
},
streamUpdated = function(event) {
switch(event.changedProperty) {
case 'videoDimensions':
+ if (!_streamContainer) {
+ // Ignore videoEmension updates before streamContainer is created OPENTOK-17253
+ break;
+ }
_streamContainer.orientation({
width: event.newValue.width,
height: event.newValue.height,
videoOrientation: event.newValue.orientation
});
break;
case 'videoDisableWarning':
_chrome.videoDisabledIndicator.setWarning(event.newValue);
this.dispatchEvent(new OT.VideoDisableWarningEvent(
event.newValue ? 'videoDisableWarning' : 'videoDisableWarningLifted'
));
break;
case 'hasVideo':
- if(_container) {
- var audioOnly = !(_stream.hasVideo && _properties.subscribeToVideo);
- _container.audioOnly(audioOnly);
- _container.showPoster(audioOnly);
- }
+
+ setAudioOnly(!(_stream.hasVideo && _properties.subscribeToVideo));
+
this.dispatchEvent(new OT.VideoEnabledChangedEvent(
_stream.hasVideo ? 'videoEnabled' : 'videoDisabled', {
reason: 'publishVideo'
}));
break;
case 'hasAudio':
// noop
@@ -19258,65 +19103,60 @@ waitForDomReady();
_chrome.muteButton.setDisplayMode(value);
_chrome.backingBar.setMuteMode(value);
break;
case 'audioLevelDisplayMode':
_chrome.audioLevel.setDisplayMode(value);
break;
- case 'bugDisplayMode':
- // bugDisplayMode can't be updated but is used by some partners
-
case 'backgroundImageURI':
_container.setBackgroundImageURI(value);
}
},
_createChrome = function() {
-
- if(this.getStyle('bugDisplayMode') === 'off') {
- logAnalyticsEvent('bugDisplayMode', 'createChrome', 'mode', 'off');
- }
var widgets = {
backingBar: new OT.Chrome.BackingBar({
nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'),
muteMode: chromeButtonMode.call(this, this.getStyle('showMuteButton'))
}),
name: new OT.Chrome.NamePanel({
name: _properties.name,
- mode: this.getStyle('nameDisplayMode'),
- bugMode: this.getStyle('bugDisplayMode')
+ mode: this.getStyle('nameDisplayMode')
}),
muteButton: new OT.Chrome.MuteButton({
muted: _properties.muted,
mode: chromeButtonMode.call(this, this.getStyle('showMuteButton'))
}),
- opentokButton: new OT.Chrome.OpenTokButton({
- mode: this.getStyle('bugDisplayMode')
- }),
-
archive: new OT.Chrome.Archiving({
show: this.getStyle('showArchiveStatus'),
archiving: false
})
};
- if(_audioLevelCapable) {
+ if (_audioLevelCapable) {
+ var audioLevelTransformer = new OT.AudioLevelTransformer();
+
+ var audioLevelUpdatedHandler = function(evt) {
+ _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+ };
+
_audioLevelMeter = new OT.Chrome.AudioLevelMeter({
- mode: this.getStyle('audioLevelDisplayMode')
- });
-
- var audioLevelTransformer = new OT.AudioLevelTransformer();
- this.on('audioLevelUpdated', function(evt) {
- _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+ mode: this.getStyle('audioLevelDisplayMode'),
+ onActivate: function() {
+ _subscriber.on('audioLevelUpdated', audioLevelUpdatedHandler);
+ },
+ onPassivate: function() {
+ _subscriber.off('audioLevelUpdated', audioLevelUpdatedHandler);
+ }
});
widgets.audioLevel = _audioLevelMeter;
}
widgets.videoDisabledIndicator = new OT.Chrome.VideoDisabledIndicator({
mode: this.getStyle('videoDisabledDisplayMode')
});
@@ -19327,29 +19167,43 @@ waitForDomReady();
muted: function() {
muteAudio.call(this, true);
},
unmuted: function() {
muteAudio.call(this, false);
}
}, this);
+
+ if(_audioLevelMeter && this.getStyle('audioLevelDisplayMode') === 'auto') {
+ _audioLevelMeter[_container.audioOnly() ? 'show' : 'hide']();
+ }
},
_showError = function() {
// Display the error message inside the container, assuming it's
// been created by now.
if (_container) {
_container.addError(
'The stream was unable to connect due to a network error.',
'Make sure your connection isn\'t blocked by a firewall.'
);
}
};
+ var setAudioOnly = function(audioOnly) {
+ if(_container) {
+ _container.audioOnly(audioOnly);
+ _container.showPoster(audioOnly);
+ }
+
+ if (_audioLevelMeter && _subscriber.getStyle('audioLevelDisplayMode') === 'auto') {
+ _audioLevelMeter[audioOnly ? 'show' : 'hide']();
+ }
+ };
this.subscribe = function(stream) {
OT.debug('OT.Subscriber: subscribe to ' + stream.id);
if (_state.isSubscribing()) {
// @todo error
OT.error('OT.Subscriber.Subscribe: Cannot subscribe, already subscribing.');
return false;
@@ -19413,28 +19267,27 @@ waitForDomReady();
}, this);
// initialize the peer connection AFTER we've added the event listeners
_peerConnection.init();
if (OT.$.hasCapabilities('audioOutputLevelStat')) {
_audioLevelSampler = new OT.GetStatsAudioLevelSampler(_peerConnection, 'out');
} else if (OT.$.hasCapabilities('webAudioCapableRemoteStream')) {
- _audioLevelSampler = new OT.AnalyserAudioLevelSampler(new window.AudioContext());
+ _audioLevelSampler = new OT.AnalyserAudioLevelSampler(OT.audioContext());
}
if(_audioLevelSampler) {
- var subscriber = this;
// sample with interval to minimise disturbance on animation loop but dispatch the
// event with RAF since the main purpose is animation of a meter
_audioLevelRunner = new OT.IntervalRunner(function() {
_audioLevelSampler.sample(function(audioOutputLevel) {
if (audioOutputLevel !== null) {
OT.$.requestAnimationFrame(function() {
- subscriber.dispatchEvent(
+ _subscriber.dispatchEvent(
new OT.AudioLevelUpdatedEvent(audioOutputLevel));
});
}
});
}, 60);
}
} else {
logAnalyticsEvent('createPeerConnection', 'Attempt', '', '');
@@ -19744,26 +19597,23 @@ waitForDomReady();
* @see <a href="StreamPropertyChangedEvent.html">StreamPropertyChangedEvent</a>
*
* @method #subscribeToVideo
* @memberOf Subscriber
*/
this.subscribeToVideo = function(pValue, reason) {
var value = OT.$.castToBoolean(pValue, true);
- if(_container) {
- var audioOnly = !(value && _stream.hasVideo);
- _container.audioOnly(audioOnly);
- _container.showPoster(audioOnly);
- if(value && _container.video()) {
- _container.loading(value);
- _container.video().whenTimeIncrements(function(){
- _container.loading(false);
- }, this);
- }
+ setAudioOnly(!(value && _stream.hasVideo));
+
+ if ( value && _container && _container.video()) {
+ _container.loading(value);
+ _container.video().whenTimeIncrements(function() {
+ _container.loading(false);
+ }, this);
}
if (_chrome && _chrome.videoDisabledIndicator) {
_chrome.videoDisabledIndicator.disableVideo(false);
}
if (_peerConnection) {
_peerConnection.subscribeToVideo(value);