'use strict' // Adapter's interface. var AdapterJS = AdapterJS || window.AdapterJS || {}; AdapterJS.options = AdapterJS.options || {}; // uncomment to get virtual webcams // AdapterJS.options.getAllCams = true; AdapterJS.options.getAllCams = !!AdapterJS.options.getAllCams; // uncomment to prevent the install prompt when the plugin in not yet installed // AdapterJS.options.hidePluginInstallPrompt = true; AdapterJS.options.hidePluginInstallPrompt = !!AdapterJS.options.hidePluginInstallPrompt; // uncomment to force the use of the plugin on Safari // AdapterJS.options.forceSafariPlugin = true; AdapterJS.options.forceSafariPlugin = !!AdapterJS.options.forceSafariPlugin; // AdapterJS version AdapterJS.VERSION = '@@version'; // This function will be called when the WebRTC API is ready to be used // Whether it is the native implementation (Chrome, Firefox, Opera) or // the plugin // You may Override this function to synchronise the start of your application // with the WebRTC API being ready. // If you decide not to override use this synchronisation, it may result in // an extensive CPU usage on the plugin start (once per tab loaded) // Params: // - isUsingPlugin: true is the WebRTC plugin is being used, false otherwise // AdapterJS.onwebrtcready = AdapterJS.onwebrtcready || function(isUsingPlugin) { // The WebRTC API is ready. // Override me and do whatever you want here }; // New interface to store multiple callbacks, private AdapterJS._onwebrtcreadies = []; // Sets a callback function to be called when the WebRTC interface is ready. // The first argument is the function to callback.\ // Throws an error if the first argument is not a function AdapterJS.webRTCReady = function (baseCallback) { if (typeof baseCallback !== 'function') { throw new Error('Callback provided is not a function'); } var callback = function () { // Make users having requirejs to use the webRTCReady function to define first // When you set a setTimeout(definePolyfill, 0), it overrides the WebRTC function // This is be more than 0s if (typeof window.require === 'function' && typeof AdapterJS._defineMediaSourcePolyfill === 'function') { AdapterJS._defineMediaSourcePolyfill(); } // All WebRTC interfaces are ready, just call the callback baseCallback(null !== AdapterJS.WebRTCPlugin.plugin); }; if (true === AdapterJS.onwebrtcreadyDone) { callback(); } else { // will be triggered automatically when your browser/plugin is ready. AdapterJS._onwebrtcreadies.push(callback); } }; // Plugin namespace AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {}; // The object to store plugin information /* jshint ignore:start */ @Tem@include('pluginInfo.js', {}) /* jshint ignore:end */ AdapterJS.WebRTCPlugin.TAGS = { NONE : 'none', AUDIO : 'audio', VIDEO : 'video' }; // Unique identifier of each opened page AdapterJS.WebRTCPlugin.pageId = Math.random().toString(36).slice(2); // Use this whenever you want to call the plugin. AdapterJS.WebRTCPlugin.plugin = null; // Set log level for the plugin once it is ready. // The different values are // This is an asynchronous function that will run when the plugin is ready AdapterJS.WebRTCPlugin.setLogLevel = null; // Defines webrtc's JS interface according to the plugin's implementation. // Define plugin Browsers as WebRTC Interface. AdapterJS.WebRTCPlugin.defineWebRTCInterface = null; // This function detects whether or not a plugin is installed. // Checks if Not IE (firefox, for example), else if it's IE, // we're running IE and do something. If not it is not supported. AdapterJS.WebRTCPlugin.isPluginInstalled = null; // Lets adapter.js wait until the the document is ready before injecting the plugin AdapterJS.WebRTCPlugin.pluginInjectionInterval = null; // Inject the HTML DOM object element into the page. AdapterJS.WebRTCPlugin.injectPlugin = null; // States of readiness that the plugin goes through when // being injected and stated AdapterJS.WebRTCPlugin.PLUGIN_STATES = { NONE : 0, // no plugin use INITIALIZING : 1, // Detected need for plugin INJECTING : 2, // Injecting plugin INJECTED: 3, // Plugin element injected but not usable yet READY: 4 // Plugin ready to be used }; // Current state of the plugin. You cannot use the plugin before this is // equal to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE; // True is AdapterJS.onwebrtcready was already called, false otherwise // Used to make sure AdapterJS.onwebrtcready is only called once AdapterJS.onwebrtcreadyDone = false; // Log levels for the plugin. // To be set by calling AdapterJS.WebRTCPlugin.setLogLevel /* Log outputs are prefixed in some cases. INFO: Information reported by the plugin. ERROR: Errors originating from within the plugin. WEBRTC: Error originating from within the libWebRTC library */ // From the least verbose to the most verbose AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS = { NONE : 'NONE', ERROR : 'ERROR', WARNING : 'WARNING', INFO: 'INFO', VERBOSE: 'VERBOSE', SENSITIVE: 'SENSITIVE' }; // Does a waiting check before proceeding to load the plugin. AdapterJS.WebRTCPlugin.WaitForPluginReady = null; // This methid will use an interval to wait for the plugin to be ready. AdapterJS.WebRTCPlugin.callWhenPluginReady = null; AdapterJS.documentReady = function () { return (document.readyState === 'interactive' && !!document.body) || document.readyState === 'complete'; } // !!!! WARNING: DO NOT OVERRIDE THIS FUNCTION. !!! // This function will be called when plugin is ready. It sends necessary // details to the plugin. // The function will wait for the document to be ready and the set the // plugin state to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY, // indicating that it can start being requested. // This function is not in the IE/Safari condition brackets so that // TemPluginLoaded function might be called on Chrome/Firefox. // This function is the only private function that is not encapsulated to // allow the plugin method to be called. window.__TemWebRTCReady0 = function () { if (AdapterJS.documentReady()) { AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY; AdapterJS.maybeThroughWebRTCReady(); } else { // Try again in 100ms setTimeout(__TemWebRTCReady0, 100); } }; AdapterJS.maybeThroughWebRTCReady = function() { if (!AdapterJS.onwebrtcreadyDone) { AdapterJS.onwebrtcreadyDone = true; // If new interface for multiple callbacks used if (AdapterJS._onwebrtcreadies.length) { AdapterJS._onwebrtcreadies.forEach(function (callback) { if (typeof(callback) === 'function') { callback(AdapterJS.WebRTCPlugin.plugin !== null); } }); // Else if no callbacks on new interface assuming user used old(deprecated) way to set callback through AdapterJS.onwebrtcready = ... } else if (typeof(AdapterJS.onwebrtcready) === 'function') { AdapterJS.onwebrtcready(AdapterJS.WebRTCPlugin.plugin !== null); } } }; // Text namespace AdapterJS.TEXT = { PLUGIN: { REQUIRE_INSTALLATION: 'This website requires you to install a WebRTC-enabling plugin ' + 'to work on this browser.', REQUIRE_RESTART: 'Your plugin is being downloaded. Please run the installer, and restart your browser to begin using it.', NOT_SUPPORTED: 'Your browser does not support WebRTC.', BUTTON: 'Install Now' }, REFRESH: { REQUIRE_REFRESH: 'Please refresh page', BUTTON: 'Refresh Page' } }; // The result of ice connection states. // - starting: Ice connection is starting. // - checking: Ice connection is checking. // - connected Ice connection is connected. // - completed Ice connection is connected. // - done Ice connection has been completed. // - disconnected Ice connection has been disconnected. // - failed Ice connection has failed. // - closed Ice connection is closed. AdapterJS._iceConnectionStates = { starting : 'starting', checking : 'checking', connected : 'connected', completed : 'connected', done : 'completed', disconnected : 'disconnected', failed : 'failed', closed : 'closed' }; //The IceConnection states that has been fired for each peer. AdapterJS._iceConnectionFiredStates = []; // Check if WebRTC Interface is defined. AdapterJS.isDefined = null; // ----------------------------------------------------------- // Detected webrtc implementation. Types are: // - 'moz': Mozilla implementation of webRTC. // - 'webkit': WebKit implementation of webRTC. // - 'plugin': Using the plugin implementation. window.webrtcDetectedType = null; //Creates MediaStream object. window.MediaStream = (typeof MediaStream === 'function') ? MediaStream : null; //Creates MediaStreamTrack object. window.MediaStreamTrack = (typeof MediaStreamTrack === 'function') ? MediaStreamTrack : null; //The RTCPeerConnection object. window.RTCPeerConnection = (typeof RTCPeerConnection === 'function') ? RTCPeerConnection : null; // Creates RTCSessionDescription object for Plugin Browsers window.RTCSessionDescription = (typeof RTCSessionDescription === 'function') ? RTCSessionDescription : null; // Creates RTCIceCandidate object for Plugin Browsers window.RTCIceCandidate = (typeof RTCIceCandidate === 'function') ? RTCIceCandidate : null; // Get UserMedia (only difference is the prefix). // Code from Adam Barth. window.getUserMedia = (typeof getUserMedia === 'function') ? getUserMedia : null; // Attach a media stream to an element. window.attachMediaStream = null; // Re-attach a media stream to an element. window.reattachMediaStream = null; // Detected browser agent name. Types are: // - 'firefox': Firefox browser. // - 'chrome': Chrome browser. // - 'opera': Opera browser. // - 'safari': Safari browser. // - 'IE' - Internet Explorer browser. window.webrtcDetectedBrowser = null; // Detected browser version. window.webrtcDetectedVersion = null; // The minimum browser version still supported by AJS. window.webrtcMinimumVersion = null; // The type of DC supported by the browser window.webrtcDetectedDCSupport = null; // This function helps to retrieve the webrtc detected browser information. // This sets: // - webrtcDetectedBrowser: The browser agent name. // - webrtcDetectedVersion: The browser version. // - webrtcMinimumVersion: The minimum browser version still supported by AJS. // - webrtcDetectedType: The types of webRTC support. // - 'moz': Mozilla implementation of webRTC. // - 'webkit': WebKit implementation of webRTC. // - 'plugin': Using the plugin implementation. AdapterJS.parseWebrtcDetectedBrowser = function () { var hasMatch = null; // Detect Opera (8.0+) if ((!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) { hasMatch = navigator.userAgent.match(/OPR\/(\d+)/i) || []; window.webrtcDetectedBrowser = 'opera'; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); window.webrtcMinimumVersion = 26; window.webrtcDetectedType = 'webkit'; window.webrtcDetectedDCSupport = 'SCTP'; // Opera 20+ uses Chrome 33 // Detect Bowser on iOS } else if (navigator.userAgent.match(/Bowser\/[0-9.]*/g)) { hasMatch = navigator.userAgent.match(/Bowser\/[0-9.]*/g) || []; var chromiumVersion = parseInt((navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./i) || [])[2] || '0', 10); window.webrtcDetectedBrowser = 'bowser'; window.webrtcDetectedVersion = parseFloat((hasMatch[0] || '0/0').split('/')[1], 10); window.webrtcMinimumVersion = 0; window.webrtcDetectedType = 'webkit'; window.webrtcDetectedDCSupport = chromiumVersion > 30 ? 'SCTP' : 'RTP'; // Detect Opera on iOS (does not support WebRTC yet) } else if (navigator.userAgent.indexOf('OPiOS') > 0) { hasMatch = navigator.userAgent.match(/OPiOS\/([0-9]+)\./); // Browser which do not support webrtc yet window.webrtcDetectedBrowser = 'opera'; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); window.webrtcMinimumVersion = 0; window.webrtcDetectedType = null; window.webrtcDetectedDCSupport = null; // Detect Chrome on iOS (does not support WebRTC yet) } else if (navigator.userAgent.indexOf('CriOS') > 0) { hasMatch = navigator.userAgent.match(/CriOS\/([0-9]+)\./) || []; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); window.webrtcMinimumVersion = 0; window.webrtcDetectedType = null; window.webrtcDetectedBrowser = 'chrome'; window.webrtcDetectedDCSupport = null; // Detect Firefox on iOS (does not support WebRTC yet) } else if (navigator.userAgent.indexOf('FxiOS') > 0) { hasMatch = navigator.userAgent.match(/FxiOS\/([0-9]+)\./) || []; // Browser which do not support webrtc yet window.webrtcDetectedBrowser = 'firefox'; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); window.webrtcMinimumVersion = 0; window.webrtcDetectedType = null; window.webrtcDetectedDCSupport = null; // Detect IE (6-11) } else if (/*@cc_on!@*/false || !!document.documentMode) { hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || []; window.webrtcDetectedBrowser = 'IE'; window.webrtcDetectedVersion = parseInt(hasMatch[1], 10); window.webrtcMinimumVersion = 9; window.webrtcDetectedType = 'plugin'; window.webrtcDetectedDCSupport = 'SCTP'; if (!webrtcDetectedVersion) { hasMatch = /\bMSIE[ :]+(\d+)/g.exec(navigator.userAgent) || []; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); } // Detect Edge (20+) } else if (!!window.StyleMedia || navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { hasMatch = navigator.userAgent.match(/Edge\/(\d+).(\d+)$/) || []; // Previous webrtc/adapter uses minimum version as 10547 but checking in the Edge release history, // It's close to 13.10547 and ObjectRTC API is fully supported in that version window.webrtcDetectedBrowser = 'edge'; window.webrtcDetectedVersion = parseFloat((hasMatch[0] || '0/0').split('/')[1], 10); window.webrtcMinimumVersion = 13.10547; window.webrtcDetectedType = 'ms'; window.webrtcDetectedDCSupport = null; // Detect Firefox (1.0+) // Placed before Safari check to ensure Firefox on Android is detected } else if (typeof InstallTrigger !== 'undefined' || navigator.userAgent.indexOf('irefox') > 0) { hasMatch = navigator.userAgent.match(/Firefox\/([0-9]+)\./) || []; window.webrtcDetectedBrowser = 'firefox'; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); window.webrtcMinimumVersion = 33; window.webrtcDetectedType = 'moz'; window.webrtcDetectedDCSupport = 'SCTP'; // Detect Chrome (1+ and mobile) // Placed before Safari check to ensure Chrome on Android is detected } else if ((!!window.chrome && !!window.chrome.webstore) || navigator.userAgent.indexOf('Chrom') > 0) { hasMatch = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./i) || []; window.webrtcDetectedBrowser = 'chrome'; window.webrtcDetectedVersion = parseInt(hasMatch[2] || '0', 10); window.webrtcMinimumVersion = 38; window.webrtcDetectedType = 'webkit'; window.webrtcDetectedDCSupport = window.webrtcDetectedVersion > 30 ? 'SCTP' : 'RTP'; // Chrome 31+ supports SCTP without flags // Detect Safari } else if (/constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || safari.pushNotification) || navigator.userAgent.match(/AppleWebKit\/(\d+)\./) || navigator.userAgent.match(/Version\/(\d+).(\d+)/)) { hasMatch = navigator.userAgent.match(/version\/(\d+)\.(\d+)/i) || []; var AppleWebKitBuild = navigator.userAgent.match(/AppleWebKit\/(\d+)/i) || []; var isMobile = navigator.userAgent.match(/(iPhone|iPad)/gi); var hasNativeImpl = AppleWebKitBuild.length >= 1 && AppleWebKitBuild[1] >= 604; window.webrtcDetectedBrowser = 'safari'; window.webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); window.webrtcMinimumVersion = 7; if (isMobile) { window.webrtcDetectedType = hasNativeImpl ? 'AppleWebKit' : null; } else { // desktop var majorVersion = window.webrtcDetectedVersion; var minorVersion = parseInt(hasMatch[2] || '0', 10); var nativeImplIsOverridable = majorVersion == 11 && minorVersion < 2; window.webrtcDetectedType = hasNativeImpl && !(AdapterJS.options.forceSafariPlugin && nativeImplIsOverridable) ? 'AppleWebKit' : 'plugin'; } window.webrtcDetectedDCSupport = 'SCTP'; } // Scope it to AdapterJS and window for better consistency AdapterJS.webrtcDetectedBrowser = window.webrtcDetectedBrowser; AdapterJS.webrtcDetectedVersion = window.webrtcDetectedVersion; AdapterJS.webrtcMinimumVersion = window.webrtcMinimumVersion; AdapterJS.webrtcDetectedType = window.webrtcDetectedType; AdapterJS.webrtcDetectedDCSupport = window.webrtcDetectedDCSupport; }; AdapterJS.addEvent = function(elem, evnt, func) { if (elem.addEventListener) { // W3C DOM elem.addEventListener(evnt, func, false); } else if (elem.attachEvent) {// OLD IE DOM elem.attachEvent('on'+evnt, func); } else { // No much to do elem[evnt] = func; } }; AdapterJS.renderNotificationBar = function (message, buttonText, buttonCallback) { // only inject once the page is ready if (!AdapterJS.documentReady()) { return; } var w = window; var i = document.createElement('iframe'); i.name = 'adapterjs-alert'; i.style.position = 'fixed'; i.style.top = '-41px'; i.style.left = 0; i.style.right = 0; i.style.width = '100%'; i.style.height = '40px'; i.style.backgroundColor = '#ffffe1'; i.style.border = 'none'; i.style.borderBottom = '1px solid #888888'; i.style.zIndex = '9999999'; if(typeof i.style.webkitTransition === 'string') { i.style.webkitTransition = 'all .5s ease-out'; } else if(typeof i.style.transition === 'string') { i.style.transition = 'all .5s ease-out'; } document.body.appendChild(i); var c = (i.contentWindow) ? i.contentWindow : (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument; c.document.open(); c.document.write('' + message + ''); if(buttonText && typeof buttonCallback === 'function') { c.document.write(''); c.document.close(); // On click on okay AdapterJS.addEvent(c.document.getElementById('okay'), 'click', function (e) { e.preventDefault(); try { e.cancelBubble = true; } catch(error) { } buttonCallback(e); }); // On click on Cancel - all bars has same logic so keeping it that way for now AdapterJS.addEvent(c.document.getElementById('cancel'), 'click', function(e) { w.document.body.removeChild(i); }); } else { c.document.close(); } setTimeout(function() { if(typeof i.style.webkitTransform === 'string') { i.style.webkitTransform = 'translateY(40px)'; } else if(typeof i.style.transform === 'string') { i.style.transform = 'translateY(40px)'; } else { i.style.top = '0px'; } }, 300); }; // The requestUserMedia used by plugin gUM window.requestUserMedia = (typeof requestUserMedia === 'function') ? requestUserMedia : null; // Check for browser types and react accordingly AdapterJS.parseWebrtcDetectedBrowser(); if (['webkit', 'moz', 'ms', 'AppleWebKit'].indexOf(AdapterJS.webrtcDetectedType) > -1) { /////////////////////////////////////////////////////////////////// // INJECTION OF GOOGLE'S ADAPTER.JS CONTENT // Store the original native RTCPC in msRTCPeerConnection object if (navigator.userAgent.match(/Edge\/(\d+).(\d+)$/) && window.RTCPeerConnection) { window.msRTCPeerConnection = window.RTCPeerConnection; } /* jshint ignore:start */ @Goo@include('node_modules/webrtc-adapter/out/adapter.js', {}) /* jshint ignore:end */ // END OF INJECTION OF GOOGLE'S ADAPTER.JS CONTENT /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // EXTENSION FOR CHROME, FIREFOX AND EDGE // Includes legacy functions // -- MediaStreamTrack.getSources // // and additional shims // -- attachMediaStream // -- reattachMediaStream // -- requestUserMedia // -- a call to AdapterJS.maybeThroughWebRTCReady (notifies WebRTC is ready) // Add support for legacy functions if ( navigator.mozGetUserMedia ) { // Shim for MediaStreamTrack.getSources. MediaStreamTrack.getSources = function(successCb) { setTimeout(function() { var infos = [ { kind: 'audio', id: 'default', label:'', facing:'' }, { kind: 'video', id: 'default', label:'', facing:'' } ]; successCb(infos); }, 0); }; // Attach a media stream to an element. attachMediaStream = function(element, stream) { element.srcObject = stream; return element; }; reattachMediaStream = function(to, from) { to.srcObject = from.srcObject; return to; }; } else if ( navigator.webkitGetUserMedia ) { // Attach a media stream to an element. attachMediaStream = function(element, stream) { if (AdapterJS.webrtcDetectedVersion >= 43) { element.srcObject = stream; } else if (typeof element.src !== 'undefined') { element.src = URL.createObjectURL(stream); } else { console.error('Error attaching stream to element.'); // logging('Error attaching stream to element.'); } return element; }; reattachMediaStream = function(to, from) { if (AdapterJS.webrtcDetectedVersion >= 43) { to.srcObject = from.srcObject; } else { to.src = from.src; } return to; }; } else if (AdapterJS.webrtcDetectedType === 'AppleWebKit') { attachMediaStream = function(element, stream) { element.srcObject = stream; return element; }; reattachMediaStream = function(to, from) { to.srcObject = from.srcObject; return to; }; // Polyfill getUserMedia() if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { navigator.getUserMedia = getUserMedia = function (constraints, successCb, errorCb) { navigator.mediaDevices.getUserMedia(constraints).then(successCb).catch(errorCb); }; } } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Attach a media stream to an element. attachMediaStream = function(element, stream) { element.srcObject = stream; return element; }; reattachMediaStream = function(to, from) { to.srcObject = from.srcObject; return to; }; } // Need to override attachMediaStream and reattachMediaStream // to support the plugin's logic var attachMediaStream_base = attachMediaStream; if (AdapterJS.webrtcDetectedBrowser === 'opera') { attachMediaStream_base = function (element, stream) { if (AdapterJS.webrtcDetectedVersion > 38) { element.srcObject = stream; } else if (typeof element.src !== 'undefined') { element.src = URL.createObjectURL(stream); } // Else it doesn't work }; } attachMediaStream = function (element, stream) { if ((AdapterJS.webrtcDetectedBrowser === 'chrome' || AdapterJS.webrtcDetectedBrowser === 'opera') && !stream) { // Chrome does not support "src = null" element.src = ''; } else { attachMediaStream_base(element, stream); } return element; }; var reattachMediaStream_base = reattachMediaStream; reattachMediaStream = function (to, from) { reattachMediaStream_base(to, from); return to; }; // Propagate attachMediaStream and gUM in window and AdapterJS window.attachMediaStream = attachMediaStream; window.reattachMediaStream = reattachMediaStream; window.getUserMedia = function(constraints, onSuccess, onFailure) { navigator.getUserMedia(constraints, onSuccess, onFailure); }; AdapterJS.attachMediaStream = attachMediaStream; AdapterJS.reattachMediaStream = reattachMediaStream; AdapterJS.getUserMedia = getUserMedia; // Removed Google defined promises when promise is not defined if (typeof Promise === 'undefined') { requestUserMedia = null; } AdapterJS.maybeThroughWebRTCReady(); // END OF EXTENSION OF CHROME, FIREFOX AND EDGE /////////////////////////////////////////////////////////////////// } else { // TRY TO USE PLUGIN /////////////////////////////////////////////////////////////////// // WEBRTC PLUGIN SHIM // Will automatically check if the plugin is available and inject it // into the DOM if it is. // When the plugin is not available, will prompt a banner to suggest installing it // Use AdapterJS.options.hidePluginInstallPrompt to prevent this banner from popping // // Shims the follwing: // -- getUserMedia // -- MediaStream // -- MediaStreamTrack // -- MediaStreamTrack.getSources // -- RTCPeerConnection // -- RTCSessionDescription // -- RTCIceCandidate // -- attachMediaStream // -- reattachMediaStream // -- webrtcDetectedBrowser // -- webrtcDetectedVersion // IE 9 is not offering an implementation of console.log until you open a console if (typeof console !== 'object' || typeof console.log !== 'function') { /* jshint -W020 */ console = {} || console; // Implemented based on console specs from MDN // You may override these functions console.log = function (arg) {}; console.info = function (arg) {}; console.error = function (arg) {}; console.dir = function (arg) {}; console.exception = function (arg) {}; console.trace = function (arg) {}; console.warn = function (arg) {}; console.count = function (arg) {}; console.debug = function (arg) {}; console.count = function (arg) {}; console.time = function (arg) {}; console.timeEnd = function (arg) {}; console.group = function (arg) {}; console.groupCollapsed = function (arg) {}; console.groupEnd = function (arg) {}; /* jshint +W020 */ } /* jshint -W035 */ AdapterJS.WebRTCPlugin.WaitForPluginReady = function() { while (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { /* empty because it needs to prevent the function from running. */ } }; /* jshint +W035 */ AdapterJS.WebRTCPlugin.callWhenPluginReady = function (callback) { if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { // Call immediately if possible // Once the plugin is set, the code will always take this path callback(); } else { // otherwise start a 100ms interval var checkPluginReadyState = setInterval(function () { if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { clearInterval(checkPluginReadyState); callback(); } }, 100); } }; AdapterJS.WebRTCPlugin.setLogLevel = function(logLevel) { AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { AdapterJS.WebRTCPlugin.plugin.setLogLevel(logLevel); }); }; AdapterJS.WebRTCPlugin.injectPlugin = function () { // only inject once the page is ready if (!AdapterJS.documentReady()) { return; } // Prevent multiple injections if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) { return; } AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING; var existing = document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId); if (!!existing) { // There is already a plugin injected in the DOM. // Probably from multiple calls to node's require(AJS); // Take the existing one, and make it this AJS's plugin AdapterJS.WebRTCPlugin.plugin = existing; AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED; if (AdapterJS.WebRTCPlugin.plugin.valid) { window[AdapterJS.WebRTCPlugin.pluginInfo.onload](); // call onload function to unlock AJS } else { // wait for plugin.valid with an interval var pluginValidInterval = setInterval(function () { if (AdapterJS.WebRTCPlugin.plugin.valid) { clearInterval(pluginValidInterval); window[AdapterJS.WebRTCPlugin.pluginInfo.onload](); // call onload function to unlock AJS } }, 100); } return; } if (AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion <= 10) { var frag = document.createDocumentFragment(); AdapterJS.WebRTCPlugin.plugin = document.createElement('div'); AdapterJS.WebRTCPlugin.plugin.innerHTML = '' + ' ' + ' ' + ' ' + '' + '' + // uncomment to be able to use virtual cams (AdapterJS.options.getAllCams ? '':'') + ''; while (AdapterJS.WebRTCPlugin.plugin.firstChild) { frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild); } document.body.appendChild(frag); // Need to re-fetch the plugin AdapterJS.WebRTCPlugin.plugin = document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId); } else { // Load Plugin AdapterJS.WebRTCPlugin.plugin = document.createElement('object'); AdapterJS.WebRTCPlugin.plugin.id = AdapterJS.WebRTCPlugin.pluginInfo.pluginId; // IE will only start the plugin if it's ACTUALLY visible if (AdapterJS.webrtcDetectedBrowser === 'IE') { AdapterJS.WebRTCPlugin.plugin.width = '1px'; AdapterJS.WebRTCPlugin.plugin.height = '1px'; } else { // The size of the plugin on Safari should be 0x0px // so that the autorisation prompt is at the top AdapterJS.WebRTCPlugin.plugin.width = '0px'; AdapterJS.WebRTCPlugin.plugin.height = '0px'; } AdapterJS.WebRTCPlugin.plugin.type = AdapterJS.WebRTCPlugin.pluginInfo.type; AdapterJS.WebRTCPlugin.plugin.innerHTML = '' + '' + ' ' + (AdapterJS.options.getAllCams ? '':'') + '' + ''; document.body.appendChild(AdapterJS.WebRTCPlugin.plugin); } AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED; }; AdapterJS.WebRTCPlugin.isPluginInstalled = function (comName, plugName, plugType, installedCb, notInstalledCb) { if (AdapterJS.webrtcDetectedBrowser !== 'IE') { var pluginArray = navigator.mimeTypes; if (typeof pluginArray !== 'undefined') { for (var i = 0; i < pluginArray.length; i++) { if (pluginArray[i].type.indexOf(plugType) >= 0) { installedCb(); return; } } notInstalledCb(); } else { AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED); } } else { try { var axo = new ActiveXObject(comName + '.' + plugName); } catch (e) { notInstalledCb(); return; } installedCb(); } }; AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () { if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { console.error('AdapterJS - WebRTC interface has already been defined'); return; } AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING; AdapterJS.isDefined = function (variable) { return variable !== null && variable !== undefined; }; //////////////////////////////////////////////////////////////////////////// /// RTCSessionDescription //////////////////////////////////////////////////////////////////////////// RTCSessionDescription = function (info) { AdapterJS.WebRTCPlugin.WaitForPluginReady(); return AdapterJS.WebRTCPlugin.plugin. ConstructSessionDescription(info.type, info.sdp); }; //////////////////////////////////////////////////////////////////////////// /// MediaStream //////////////////////////////////////////////////////////////////////////// MediaStream = function (mediaStreamOrTracks) { AdapterJS.WebRTCPlugin.WaitForPluginReady(); return AdapterJS.WebRTCPlugin.plugin.MediaStream(mediaStreamOrTracks); } //////////////////////////////////////////////////////////////////////////// /// RTCPeerConnection //////////////////////////////////////////////////////////////////////////// RTCPeerConnection = function (servers, constraints) { // Validate server argumenr if (!(servers === undefined || servers === null || Array.isArray(servers.iceServers))) { throw new Error('Failed to construct \'RTCPeerConnection\': Malformed RTCConfiguration'); } // Validate constraints argument if (typeof constraints !== 'undefined' && constraints !== null) { var invalidConstraits = false; invalidConstraits |= typeof constraints !== 'object'; invalidConstraits |= constraints.hasOwnProperty('mandatory') && constraints.mandatory !== undefined && constraints.mandatory !== null && constraints.mandatory.constructor !== Object; invalidConstraits |= constraints.hasOwnProperty('optional') && constraints.optional !== undefined && constraints.optional !== null && !Array.isArray(constraints.optional); if (invalidConstraits) { throw new Error('Failed to construct \'RTCPeerConnection\': Malformed constraints object'); } } // Call relevant PeerConnection constructor according to plugin version AdapterJS.WebRTCPlugin.WaitForPluginReady(); // RTCPeerConnection prototype from the old spec var iceServers = null; if (servers && Array.isArray(servers.iceServers)) { iceServers = servers.iceServers; for (var i = 0; i < iceServers.length; i++) { // Legacy plugin versions compatibility if (iceServers[i].urls && !iceServers[i].url) { iceServers[i].url = iceServers[i].urls; } iceServers[i].hasCredentials = AdapterJS. isDefined(iceServers[i].username) && AdapterJS.isDefined(iceServers[i].credential); } } if (AdapterJS.WebRTCPlugin.plugin.PEER_CONNECTION_VERSION && AdapterJS.WebRTCPlugin.plugin.PEER_CONNECTION_VERSION > 1) { // RTCPeerConnection prototype from the new spec if (iceServers) { servers.iceServers = iceServers; } return AdapterJS.WebRTCPlugin.plugin.PeerConnection(servers); } else { var mandatory = (constraints && constraints.mandatory) ? constraints.mandatory : null; var optional = (constraints && constraints.optional) ? constraints.optional : null; return AdapterJS.WebRTCPlugin.plugin. PeerConnection(AdapterJS.WebRTCPlugin.pageId, iceServers, mandatory, optional); } }; //////////////////////////////////////////////////////////////////////////// /// MediaStreamTrack //////////////////////////////////////////////////////////////////////////// MediaStreamTrack = function(){}; MediaStreamTrack.getSources = function (callback) { AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { AdapterJS.WebRTCPlugin.plugin.GetSources(callback); }); }; // getUserMedia constraints shim. // Copied from Chrome var constraintsToPlugin = function(c) { if (typeof c !== 'object' || c.mandatory || c.optional) { return c; } var cc = {}; Object.keys(c).forEach(function(key) { if (key === 'require' || key === 'advanced') { return; } if (typeof c[key] === 'string') { cc[key] = c[key]; return; } var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]}; if (r.exact !== undefined && typeof r.exact === 'number') { r.min = r.max = r.exact; } var oldname = function(prefix, name) { if (prefix) { return prefix + name.charAt(0).toUpperCase() + name.slice(1); } return (name === 'deviceId') ? 'sourceId' : name; }; // HACK : Specially handling: if deviceId is an object with exact property, // change it such that deviceId value is not in exact property // Reason : AJS-286 (deviceId in WebRTC samples not in the format specified as specifications) if ( oldname('', key) === 'sourceId' && r.exact !== undefined ) { r.ideal = r.exact; r.exact = undefined; } if (r.ideal !== undefined) { cc.optional = cc.optional || []; var oc = {}; if (typeof r.ideal === 'number') { oc[oldname('min', key)] = r.ideal; cc.optional.push(oc); oc = {}; oc[oldname('max', key)] = r.ideal; cc.optional.push(oc); } else { oc[oldname('', key)] = r.ideal; cc.optional.push(oc); } } if (r.exact !== undefined && typeof r.exact !== 'number') { cc.mandatory = cc.mandatory || {}; cc.mandatory[oldname('', key)] = r.exact; } else { ['min', 'max'].forEach(function(mix) { if (r[mix] !== undefined) { cc.mandatory = cc.mandatory || {}; cc.mandatory[oldname(mix, key)] = r[mix]; } }); } }); if (c.advanced) { cc.optional = (cc.optional || []).concat(c.advanced); } return cc; }; //////////////////////////////////////////////////////////////////////////// /// getUserMedia //////////////////////////////////////////////////////////////////////////// getUserMedia = function (constraints, successCallback, failureCallback) { var cc = {}; cc.audio = constraints.audio ? constraintsToPlugin(constraints.audio) : false; cc.video = constraints.video ? constraintsToPlugin(constraints.video) : false; AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { AdapterJS.WebRTCPlugin.plugin. getUserMedia(cc, successCallback, failureCallback); }); }; window.navigator.getUserMedia = getUserMedia; //////////////////////////////////////////////////////////////////////////// /// mediaDevices //////////////////////////////////////////////////////////////////////////// if (typeof Promise !== 'undefined') { requestUserMedia = function(constraints) { return new Promise(function(resolve, reject) { try { getUserMedia(constraints, resolve, reject); } catch (error) { reject(error); } }); }; if (typeof(navigator.mediaDevices) === 'undefined') navigator.mediaDevices = {}; navigator.mediaDevices.getUserMedia = requestUserMedia; navigator.mediaDevices.enumerateDevices = function() { return new Promise(function(resolve) { var kinds = {audio: 'audioinput', video: 'videoinput'}; return MediaStreamTrack.getSources(function(devices) { resolve(devices.map(function(device) { return {label: device.label, kind: kinds[device.kind], id: device.id, deviceId: device.id, groupId: ''}; })); }); }); }; } //////////////////////////////////////////////////////////////////////////// /// attachMediaStream //////////////////////////////////////////////////////////////////////////// attachMediaStream = function (element, stream) { if (!element || !element.parentNode) { return; } var streamId; if (stream === null) { streamId = ''; } else { if (typeof stream.enableSoundTracks !== 'undefined') { stream.enableSoundTracks(true); } streamId = stream.id; } var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id; var nodeName = element.nodeName.toLowerCase(); if (nodeName !== 'object') { // not a plugin tag yet var tag; switch(nodeName) { case 'audio': tag = AdapterJS.WebRTCPlugin.TAGS.AUDIO; break; case 'video': tag = AdapterJS.WebRTCPlugin.TAGS.VIDEO; break; default: tag = AdapterJS.WebRTCPlugin.TAGS.NONE; } var frag = document.createDocumentFragment(); var temp = document.createElement('div'); var classHTML = ''; if (element.className) { classHTML = 'class="' + element.className + '" '; } else if (element.attributes && element.attributes['class']) { classHTML = 'class="' + element.attributes['class'].value + '" '; } temp.innerHTML = '' + ' ' + ' ' + ' ' + ' ' + ' ' + ''; while (temp.firstChild) { frag.appendChild(temp.firstChild); } var height = ''; var width = ''; if (element.clientWidth || element.clientHeight) { width = element.clientWidth; height = element.clientHeight; } else if (element.width || element.height) { width = element.width; height = element.height; } element.parentNode.insertBefore(frag, element); frag = document.getElementById(elementId); frag.width = width; frag.height = height; element.parentNode.removeChild(element); } else { // already an tag, just change the stream id var children = element.children; for (var i = 0; i !== children.length; ++i) { if (children[i].name === 'streamId') { children[i].value = streamId; break; } } element.setStreamId(streamId); } var newElement = document.getElementById(elementId); AdapterJS.forwardEventHandlers(newElement, element, Object.getPrototypeOf(element)); return newElement; }; //////////////////////////////////////////////////////////////////////////// /// reattachMediaStream //////////////////////////////////////////////////////////////////////////// reattachMediaStream = function (to, from) { var stream = null; var children = from.children; for (var i = 0; i !== children.length; ++i) { if (children[i].name === 'streamId') { AdapterJS.WebRTCPlugin.WaitForPluginReady(); stream = AdapterJS.WebRTCPlugin.plugin .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, children[i].value); break; } } if (stream !== null) { return attachMediaStream(to, stream); } else { console.log('Could not find the stream associated with this element'); } }; // Propagate attachMediaStream and gUM in window and AdapterJS window.attachMediaStream = attachMediaStream; window.reattachMediaStream = reattachMediaStream; window.getUserMedia = getUserMedia; AdapterJS.attachMediaStream = attachMediaStream; AdapterJS.reattachMediaStream = reattachMediaStream; AdapterJS.getUserMedia = getUserMedia; AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) { var properties = Object.getOwnPropertyNames( prototype ); for(var prop in properties) { if (prop) { var propName = properties[prop]; if (typeof propName.slice === 'function' && propName.slice(0,2) === 'on' && typeof srcElem[propName] === 'function') { AdapterJS.addEvent(destElem, propName.slice(2), srcElem[propName]); } } } var subPrototype = Object.getPrototypeOf(prototype); if(!!subPrototype) { AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype); } }; //////////////////////////////////////////////////////////////////////////// /// RTCIceCandidate //////////////////////////////////////////////////////////////////////////// RTCIceCandidate = function (candidate) { if (!candidate.sdpMid) { candidate.sdpMid = ''; } AdapterJS.WebRTCPlugin.WaitForPluginReady(); return AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate( candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate ); }; // inject plugin AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.injectPlugin); AdapterJS.WebRTCPlugin.injectPlugin(); }; // This function will be called if the plugin is needed (browser different // from Chrome or Firefox), but the plugin is not installed. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb || function() { AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv); AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv(); }; AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv = function () { if (!AdapterJS.documentReady()) { return; } document.removeEventListener('readystatechange', AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv); if (AdapterJS.options.hidePluginInstallPrompt) { return; } var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink; if(downloadLink) { // if download link var popupString; if (AdapterJS.WebRTCPlugin.pluginInfo.companyName) { // Show the company name and if possible a link to their portal popupString = 'This website requires you to install the '; if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { popupString += ' ' + AdapterJS.WebRTCPlugin.pluginInfo.companyName + ' WebRTC Plugin'; } else { popupString += AdapterJS.WebRTCPlugin.pluginInfo.companyName + ' WebRTC Plugin'; } popupString += ' to work on this browser.'; } else { // no portal link, just print a generic explanation popupString = AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION; } AdapterJS.renderNotificationBar(popupString, AdapterJS.TEXT.PLUGIN.BUTTON, function () { window.open(downloadLink, '_top'); if (webrtcDetectedBrowser === 'safari' && webrtcDetectedVersion == 11) { // Safari 11 doesn't have a way to reload the list of plugins AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.REQUIRE_RESTART); } else { // IE or Safari 10- // Try to reload the list of plugins, // start the plugin once available var pluginInstallInterval = setInterval(function(){ if(AdapterJS.webrtcDetectedBrowser !== 'IE') { // IE auto-refreshes navigator.plugins.refresh(false); } AdapterJS.WebRTCPlugin.isPluginInstalled( AdapterJS.WebRTCPlugin.pluginInfo.prefix, AdapterJS.WebRTCPlugin.pluginInfo.plugName, AdapterJS.WebRTCPlugin.pluginInfo.type, function() { // plugin now installed clearInterval(pluginInstallInterval); AdapterJS.WebRTCPlugin.defineWebRTCInterface(); }, function() { // still no plugin detected, nothing to do }); } , 500); } }); } else { // no download link, just print a generic explanation AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED); } }; // Try to detect the plugin and act accordingly AdapterJS.WebRTCPlugin.isPluginInstalled( AdapterJS.WebRTCPlugin.pluginInfo.prefix, AdapterJS.WebRTCPlugin.pluginInfo.plugName, AdapterJS.WebRTCPlugin.pluginInfo.type, AdapterJS.WebRTCPlugin.defineWebRTCInterface, AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb); // END OF WEBRTC PLUGIN SHIM /////////////////////////////////////////////////////////////////// } // Placed it here so that the module.exports from the browserified // adapterjs will not override our AdapterJS exports // Browserify compatibility if(typeof exports !== 'undefined') { module.exports = AdapterJS; }