/*! adapterjs - custom version from - 2015-08-12 */ // Adapter's interface. var AdapterJS = AdapterJS || {}; // Browserify compatibility if(typeof exports !== 'undefined') { module.exports = AdapterJS; } AdapterJS.options = AdapterJS.options || {}; // uncomment to get virtual webcams // AdapterJS.options.getAllCams = true; // uncomment to prevent the install prompt when the plugin in not yet installed // AdapterJS.options.hidePluginInstallPrompt = true; // AdapterJS version AdapterJS.VERSION = '0.11.1'; // 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 }; // 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 (callback) { if (typeof callback !== 'function') { throw new Error('Callback provided is not a function'); } if (true === AdapterJS.onwebrtcreadyDone) { // All WebRTC interfaces are ready, just call the callback callback(null !== AdapterJS.WebRTCPlugin.plugin); } else { // will be triggered automatically when your browser/plugin is ready. AdapterJS.onwebrtcready = callback; } }; // Plugin namespace AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {}; // The object to store plugin information AdapterJS.WebRTCPlugin.pluginInfo = { prefix : 'Tem', plugName : 'TemWebRTCPlugin', pluginId : 'plugin0', type : 'application/x-temwebrtcplugin', onload : '__TemWebRTCReady0', portalLink : 'http://skylink.io/plugin/', downloadLink : null, //set below companyName: 'Temasys' }; if(!!navigator.platform.match(/^Mac/i)) { AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1n77hco'; } else if(!!navigator.platform.match(/^Win/i)) { AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN'; } // 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; // !!!! 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. __TemWebRTCReady0 = function () { if (document.readyState === 'complete') { AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY; AdapterJS.maybeThroughWebRTCReady(); } else { AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () { if (document.readyState === 'complete') { // TODO: update comments, we wait for the document to be ready clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval); AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY; AdapterJS.maybeThroughWebRTCReady(); } }, 100); } }; AdapterJS.maybeThroughWebRTCReady = function() { if (!AdapterJS.onwebrtcreadyDone) { AdapterJS.onwebrtcreadyDone = true; 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.', 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; // This function helps to retrieve the webrtc detected browser information. // This sets: // - webrtcDetectedBrowser: The browser agent name. // - webrtcDetectedVersion: The browser version. // - 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, checkMatch = navigator.userAgent.match( /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; if (/trident/i.test(checkMatch[1])) { hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || []; webrtcDetectedBrowser = 'IE'; webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); } else if (checkMatch[1] === 'Chrome') { hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/); if (hasMatch !== null) { webrtcDetectedBrowser = 'opera'; webrtcDetectedVersion = parseInt(hasMatch[1], 10); } } if (navigator.userAgent.indexOf('Safari')) { if (typeof InstallTrigger !== 'undefined') { webrtcDetectedBrowser = 'firefox'; } else if (/*@cc_on!@*/ false || !!document.documentMode) { webrtcDetectedBrowser = 'IE'; } else if ( Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) { webrtcDetectedBrowser = 'safari'; } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) { webrtcDetectedBrowser = 'opera'; } else if (!!window.chrome) { webrtcDetectedBrowser = 'chrome'; } } if (!webrtcDetectedBrowser) { webrtcDetectedVersion = checkMatch[1]; } if (!webrtcDetectedVersion) { try { checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] : [navigator.appName, navigator.appVersion, '-?']; if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) { checkMatch.splice(1, 1, hasMatch[1]); } webrtcDetectedVersion = parseInt(checkMatch[1], 10); } catch (error) { } } }; // To fix configuration as some browsers does not support // the 'urls' attribute. AdapterJS.maybeFixConfiguration = function (pcConfig) { if (pcConfig === null) { return; } for (var i = 0; i < pcConfig.iceServers.length; i++) { if (pcConfig.iceServers[i].hasOwnProperty('urls')) { pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls; delete pcConfig.iceServers[i].urls; } } }; 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 (text, buttonText, buttonLink, openNewTab, displayRefreshBar) { // only inject once the page is ready if (document.readyState !== 'complete') { return; } var w = window; var i = document.createElement('iframe'); 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); c = (i.contentWindow) ? i.contentWindow : (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument; c.document.open(); c.document.write('' + text + ''); if(buttonText && buttonLink) { c.document.write(''); c.document.close(); AdapterJS.addEvent(c.document.getElementById('okay'), 'click', function(e) { if (!!displayRefreshBar) { AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION ? AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH : AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH, AdapterJS.TEXT.REFRESH.BUTTON, 'javascript:location.reload()'); } window.open(buttonLink, !!openNewTab ? '_blank' : '_top'); e.preventDefault(); try { event.cancelBubble = true; } catch(error) { } var pluginInstallInterval = setInterval(function(){ if(! isIE) { navigator.plugins.refresh(false); } AdapterJS.WebRTCPlugin.isPluginInstalled( AdapterJS.WebRTCPlugin.pluginInfo.prefix, AdapterJS.WebRTCPlugin.pluginInfo.plugName, function() { clearInterval(pluginInstallInterval); AdapterJS.WebRTCPlugin.defineWebRTCInterface() }, function() { //Does nothing because not used here }); } , 500); }); }else { c.document.close(); } AdapterJS.addEvent(c.document, 'click', function() { w.document.body.removeChild(i); }); 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); }; // ----------------------------------------------------------- // Detected webrtc implementation. Types are: // - 'moz': Mozilla implementation of webRTC. // - 'webkit': WebKit implementation of webRTC. // - 'plugin': Using the plugin implementation. webrtcDetectedType = null; // Detected webrtc datachannel support. Types are: // - 'SCTP': SCTP datachannel support. // - 'RTP': RTP datachannel support. webrtcDetectedDCSupport = null; // Set the settings for creating DataChannels, MediaStream for // Cross-browser compability. // - This is only for SCTP based support browsers. // the 'urls' attribute. checkMediaDataChannelSettings = function (peerBrowserAgent, peerBrowserVersion, callback, constraints) { if (typeof callback !== 'function') { return; } var beOfferer = true; var isLocalFirefox = webrtcDetectedBrowser === 'firefox'; // Nightly version does not require MozDontOfferDataChannel for interop var isLocalFirefoxInterop = webrtcDetectedType === 'moz' && webrtcDetectedVersion > 30; var isPeerFirefox = peerBrowserAgent === 'firefox'; var isPeerFirefoxInterop = peerBrowserAgent === 'firefox' && ((peerBrowserVersion) ? (peerBrowserVersion > 30) : false); // Resends an updated version of constraints for MozDataChannel to work // If other userAgent is firefox and user is firefox, remove MozDataChannel if ((isLocalFirefox && isPeerFirefox) || (isLocalFirefoxInterop)) { try { delete constraints.mandatory.MozDontOfferDataChannel; } catch (error) { console.error('Failed deleting MozDontOfferDataChannel'); console.error(error); } } else if ((isLocalFirefox && !isPeerFirefox)) { constraints.mandatory.MozDontOfferDataChannel = true; } if (!isLocalFirefox) { // temporary measure to remove Moz* constraints in non Firefox browsers for (var prop in constraints.mandatory) { if (constraints.mandatory.hasOwnProperty(prop)) { if (prop.indexOf('Moz') !== -1) { delete constraints.mandatory[prop]; } } } } // Firefox (not interopable) cannot offer DataChannel as it will cause problems to the // interopability of the media stream if (isLocalFirefox && !isPeerFirefox && !isLocalFirefoxInterop) { beOfferer = false; } callback(beOfferer, constraints); }; // Handles the differences for all browsers ice connection state output. // - Tested outcomes are: // - Chrome (offerer) : 'checking' > 'completed' > 'completed' // - Chrome (answerer) : 'checking' > 'connected' // - Firefox (offerer) : 'checking' > 'connected' // - Firefox (answerer): 'checking' > 'connected' checkIceConnectionState = function (peerId, iceConnectionState, callback) { if (typeof callback !== 'function') { console.warn('No callback specified in checkIceConnectionState. Aborted.'); return; } peerId = (peerId) ? peerId : 'peer'; if (!AdapterJS._iceConnectionFiredStates[peerId] || iceConnectionState === AdapterJS._iceConnectionStates.disconnected || iceConnectionState === AdapterJS._iceConnectionStates.failed || iceConnectionState === AdapterJS._iceConnectionStates.closed) { AdapterJS._iceConnectionFiredStates[peerId] = []; } iceConnectionState = AdapterJS._iceConnectionStates[iceConnectionState]; if (AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState) < 0) { AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState); if (iceConnectionState === AdapterJS._iceConnectionStates.connected) { setTimeout(function () { AdapterJS._iceConnectionFiredStates[peerId] .push(AdapterJS._iceConnectionStates.done); callback(AdapterJS._iceConnectionStates.done); }, 1000); } callback(iceConnectionState); } return; }; // Firefox: // - Creates iceServer from the url for Firefox. // - Create iceServer with stun url. // - Create iceServer with turn url. // - Ignore the transport parameter from TURN url for FF version <=27. // - Return null for createIceServer if transport=tcp. // - FF 27 and above supports transport parameters in TURN url, // - So passing in the full url to create iceServer. // Chrome: // - Creates iceServer from the url for Chrome M33 and earlier. // - Create iceServer with stun url. // - Chrome M28 & above uses below TURN format. // Plugin: // - Creates Ice Server for Plugin Browsers // - If Stun - Create iceServer with stun url. // - Else - Create iceServer with turn url // - This is a WebRTC Function createIceServer = null; // Firefox: // - Creates IceServers for Firefox // - Use .url for FireFox. // - Multiple Urls support // Chrome: // - Creates iceServers from the urls for Chrome M34 and above. // - .urls is supported since Chrome M34. // - Multiple Urls support // Plugin: // - Creates Ice Servers for Plugin Browsers // - Multiple Urls support // - This is a WebRTC Function createIceServers = null; //------------------------------------------------------------ //The RTCPeerConnection object. RTCPeerConnection = null; // Creates RTCSessionDescription object for Plugin Browsers RTCSessionDescription = (typeof RTCSessionDescription === 'function') ? RTCSessionDescription : null; // Creates RTCIceCandidate object for Plugin Browsers RTCIceCandidate = (typeof RTCIceCandidate === 'function') ? RTCIceCandidate : null; // Get UserMedia (only difference is the prefix). // Code from Adam Barth. getUserMedia = null; // Attach a media stream to an element. attachMediaStream = null; // Re-attach a media stream to an element. reattachMediaStream = null; // Detected browser agent name. Types are: // - 'firefox': Firefox browser. // - 'chrome': Chrome browser. // - 'opera': Opera browser. // - 'safari': Safari browser. // - 'IE' - Internet Explorer browser. webrtcDetectedBrowser = null; // Detected browser version. webrtcDetectedVersion = null; // Check for browser types and react accordingly if (navigator.mozGetUserMedia) { webrtcDetectedBrowser = 'firefox'; webrtcDetectedVersion = parseInt(navigator .userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); webrtcDetectedType = 'moz'; webrtcDetectedDCSupport = 'SCTP'; RTCPeerConnection = function (pcConfig, pcConstraints) { AdapterJS.maybeFixConfiguration(pcConfig); return new mozRTCPeerConnection(pcConfig, pcConstraints); }; // The RTCSessionDescription object. RTCSessionDescription = mozRTCSessionDescription; window.RTCSessionDescription = RTCSessionDescription; // The RTCIceCandidate object. RTCIceCandidate = mozRTCIceCandidate; window.RTCIceCandidate = RTCIceCandidate; window.getUserMedia = navigator.mozGetUserMedia.bind(navigator); navigator.getUserMedia = window.getUserMedia; // 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); }; createIceServer = function (url, username, password) { var iceServer = null; var url_parts = url.split(':'); if (url_parts[0].indexOf('stun') === 0) { iceServer = { url : url }; } else if (url_parts[0].indexOf('turn') === 0) { if (webrtcDetectedVersion < 27) { var turn_url_parts = url.split('?'); if (turn_url_parts.length === 1 || turn_url_parts[1].indexOf('transport=udp') === 0) { iceServer = { url : turn_url_parts[0], credential : password, username : username }; } } else { iceServer = { url : url, credential : password, username : username }; } } return iceServer; }; createIceServers = function (urls, username, password) { var iceServers = []; for (i = 0; i < urls.length; i++) { var iceServer = createIceServer(urls[i], username, password); if (iceServer !== null) { iceServers.push(iceServer); } } return iceServers; }; attachMediaStream = function (element, stream) { element.mozSrcObject = stream; if (stream !== null) element.play(); return element; }; reattachMediaStream = function (to, from) { to.mozSrcObject = from.mozSrcObject; to.play(); return to; }; MediaStreamTrack.getSources = MediaStreamTrack.getSources || function (callback) { if (!callback) { throw new TypeError('Failed to execute \'getSources\' on \'MediaStreamTrack\'' + ': 1 argument required, but only 0 present.'); } return callback([]); }; // Fake get{Video,Audio}Tracks if (!MediaStream.prototype.getVideoTracks) { MediaStream.prototype.getVideoTracks = function () { return []; }; } if (!MediaStream.prototype.getAudioTracks) { MediaStream.prototype.getAudioTracks = function () { return []; }; } AdapterJS.maybeThroughWebRTCReady(); } else if (navigator.webkitGetUserMedia) { webrtcDetectedBrowser = 'chrome'; webrtcDetectedType = 'webkit'; webrtcDetectedVersion = parseInt(navigator .userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); // check if browser is opera 20+ var checkIfOpera = navigator.userAgent.match(/\bOPR\/(\d+)/); if (checkIfOpera !== null) { webrtcDetectedBrowser = 'opera'; webrtcDetectedVersion = parseInt(checkIfOpera[1], 10); } // check browser datachannel support if ((webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion >= 31) || (webrtcDetectedBrowser === 'opera' && webrtcDetectedVersion >= 20)) { webrtcDetectedDCSupport = 'SCTP'; } else if (webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion < 30 && webrtcDetectedVersion > 24) { webrtcDetectedDCSupport = 'RTP'; } else { webrtcDetectedDCSupport = ''; } createIceServer = function (url, username, password) { var iceServer = null; var url_parts = url.split(':'); if (url_parts[0].indexOf('stun') === 0) { iceServer = { 'url' : url }; } else if (url_parts[0].indexOf('turn') === 0) { iceServer = { 'url' : url, 'credential' : password, 'username' : username }; } return iceServer; }; createIceServers = function (urls, username, password) { var iceServers = []; if (webrtcDetectedVersion >= 34) { iceServers = { 'urls' : urls, 'credential' : password, 'username' : username }; } else { for (i = 0; i < urls.length; i++) { var iceServer = createIceServer(urls[i], username, password); if (iceServer !== null) { iceServers.push(iceServer); } } } return iceServers; }; RTCPeerConnection = function (pcConfig, pcConstraints) { if (webrtcDetectedVersion < 34) { AdapterJS.maybeFixConfiguration(pcConfig); } return new webkitRTCPeerConnection(pcConfig, pcConstraints); }; window.getUserMedia = navigator.webkitGetUserMedia.bind(navigator); navigator.getUserMedia = window.getUserMedia; attachMediaStream = function (element, stream) { if (typeof element.srcObject !== 'undefined') { element.srcObject = stream; } else if (typeof element.mozSrcObject !== 'undefined') { element.mozSrcObject = stream; } else if (typeof element.src !== 'undefined') { element.src = (stream === null ? '' : URL.createObjectURL(stream)); } else { console.log('Error attaching stream to element.'); } return element; }; reattachMediaStream = function (to, from) { to.src = from.src; return to; }; AdapterJS.maybeThroughWebRTCReady(); } else { // TRY TO USE PLUGIN // 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 */ } webrtcDetectedType = 'plugin'; webrtcDetectedDCSupport = 'plugin'; AdapterJS.parseWebrtcDetectedBrowser(); isIE = webrtcDetectedBrowser === 'IE'; /* 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 (document.readyState !== 'complete') { return; } // Prevent multiple injections if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) { return; } AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING; if (webrtcDetectedBrowser === 'IE' && 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 (isIE) { 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, installedCb, notInstalledCb) { if (!isIE) { var pluginArray = navigator.plugins; for (var i = 0; i < pluginArray.length; i++) { if (pluginArray[i].name.indexOf(plugName) >= 0) { installedCb(); return; } } notInstalledCb(); } 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("WebRTC interface has been defined already"); return; } AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING; AdapterJS.isDefined = function (variable) { return variable !== null && variable !== undefined; }; createIceServer = function (url, username, password) { var iceServer = null; var url_parts = url.split(':'); if (url_parts[0].indexOf('stun') === 0) { iceServer = { 'url' : url, 'hasCredentials' : false }; } else if (url_parts[0].indexOf('turn') === 0) { iceServer = { 'url' : url, 'hasCredentials' : true, 'credential' : password, 'username' : username }; } return iceServer; }; createIceServers = function (urls, username, password) { var iceServers = []; for (var i = 0; i < urls.length; ++i) { iceServers.push(createIceServer(urls[i], username, password)); } return iceServers; }; RTCSessionDescription = function (info) { AdapterJS.WebRTCPlugin.WaitForPluginReady(); return AdapterJS.WebRTCPlugin.plugin. ConstructSessionDescription(info.type, info.sdp); }; RTCPeerConnection = function (servers, constraints) { var iceServers = null; if (servers) { iceServers = servers.iceServers; for (var i = 0; i < iceServers.length; i++) { 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); } } var mandatory = (constraints && constraints.mandatory) ? constraints.mandatory : null; var optional = (constraints && constraints.optional) ? constraints.optional : null; AdapterJS.WebRTCPlugin.WaitForPluginReady(); return AdapterJS.WebRTCPlugin.plugin. PeerConnection(AdapterJS.WebRTCPlugin.pageId, iceServers, mandatory, optional); }; MediaStreamTrack = {}; MediaStreamTrack.getSources = function (callback) { AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { AdapterJS.WebRTCPlugin.plugin.GetSources(callback); }); }; window.getUserMedia = function (constraints, successCallback, failureCallback) { constraints.audio = constraints.audio || false; constraints.video = constraints.video || false; AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { AdapterJS.WebRTCPlugin.plugin. getUserMedia(constraints, successCallback, failureCallback); }); }; window.navigator.getUserMedia = window.getUserMedia; attachMediaStream = function (element, stream) { if (!element || !element.parentNode) { return; } var streamId if (stream === null) { streamId = ''; } else { stream.enableSoundTracks(true); streamId = stream.id; } if (element.nodeName.toLowerCase() !== 'audio') { var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id; if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) { 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.getBoundingClientRect) { var rectObject = element.getBoundingClientRect(); width = rectObject.width + 'px'; height = rectObject.height + 'px'; } else if (element.width) { width = element.width; height = element.height; } else { // TODO: What scenario could bring us here? } element.parentNode.insertBefore(frag, element); frag = document.getElementById(elementId); frag.width = width; frag.height = height; element.parentNode.removeChild(element); } else { 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); newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {}; if (isIE) { // on IE the event needs to be plugged manually newElement.attachEvent('onplaying', newElement.onplaying); newElement.onclick = (element.onclick) ? element.onclick : function (arg) {}; newElement._TemOnClick = function (id) { var arg = { srcElement : document.getElementById(id) }; newElement.onclick(arg); }; } return newElement; } else { return element; } }; 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'); } }; 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.options.hidePluginInstallPrompt) { return; } var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink; if(downloadLink) { // if download link var popupString; if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { // is portal link popupString = 'This website requires you to install the ' + ' ' + AdapterJS.WebRTCPlugin.pluginInfo.companyName + ' WebRTC Plugin' + ' 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, downloadLink); } 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.defineWebRTCInterface, AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb); } (function () { 'use strict'; var baseGetUserMedia = null; AdapterJS.TEXT.EXTENSION = { REQUIRE_INSTALLATION_FF: 'To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.', REQUIRE_INSTALLATION_CHROME: 'To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.', REQUIRE_REFRESH: 'Please refresh this page after the Skylink WebRTC tools extension has been installed.', BUTTON_FF: 'Install Now', BUTTON_CHROME: 'Go to Chrome Web Store' }; var clone = function(obj) { if (null == obj || "object" != typeof obj) return obj; var copy = obj.constructor(); for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; } return copy; }; if (window.navigator.mozGetUserMedia) { baseGetUserMedia = window.navigator.getUserMedia; navigator.getUserMedia = function (constraints, successCb, failureCb) { if (constraints && constraints.video && !!constraints.video.mediaSource) { // intercepting screensharing requests if (constraints.video.mediaSource !== 'screen' && constraints.video.mediaSource !== 'window') { throw new Error('Only "screen" and "window" option is available as mediaSource'); } var updatedConstraints = clone(constraints); //constraints.video.mediaSource = constraints.video.mediaSource; updatedConstraints.video.mozMediaSource = updatedConstraints.video.mediaSource; // so generally, it requires for document.readyState to be completed before the getUserMedia could be invoked. // strange but this works anyway var checkIfReady = setInterval(function () { if (document.readyState === 'complete') { clearInterval(checkIfReady); baseGetUserMedia(updatedConstraints, successCb, function (error) { if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') { AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF, AdapterJS.TEXT.EXTENSION.BUTTON_FF, 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true); //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname; } else { failureCb(error); } }); } }, 1); } else { // regular GetUserMediaRequest baseGetUserMedia(constraints, successCb, failureCb); } }; getUserMedia = navigator.getUserMedia; } else if (window.navigator.webkitGetUserMedia) { baseGetUserMedia = window.navigator.getUserMedia; navigator.getUserMedia = function (constraints, successCb, failureCb) { if (constraints && constraints.video && !!constraints.video.mediaSource) { if (window.webrtcDetectedBrowser !== 'chrome') { throw new Error('Current browser does not support screensharing'); } // would be fine since no methods var updatedConstraints = clone(constraints); var chromeCallback = function(error, sourceId) { if(!error) { updatedConstraints.video.mandatory = updatedConstraints.video.mandatory || {}; updatedConstraints.video.mandatory.chromeMediaSource = 'desktop'; updatedConstraints.video.mandatory.maxWidth = window.screen.width > 1920 ? window.screen.width : 1920; updatedConstraints.video.mandatory.maxHeight = window.screen.height > 1080 ? window.screen.height : 1080; if (sourceId) { updatedConstraints.video.mandatory.chromeMediaSourceId = sourceId; } delete updatedConstraints.video.mediaSource; baseGetUserMedia(updatedConstraints, successCb, failureCb); } else { if (error === 'permission-denied') { throw new Error('Permission denied for screen retrieval'); } else { throw new Error('Failed retrieving selected screen'); } } }; var onIFrameCallback = function (event) { if (!event.data) { return; } if (event.data.chromeMediaSourceId) { if (event.data.chromeMediaSourceId === 'PermissionDeniedError') { chromeCallback('permission-denied'); } else { chromeCallback(null, event.data.chromeMediaSourceId); } } if (event.data.chromeExtensionStatus) { if (event.data.chromeExtensionStatus === 'not-installed') { AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME, AdapterJS.TEXT.EXTENSION.BUTTON_CHROME, event.data.data, true, true); } else { chromeCallback(event.data.chromeExtensionStatus, null); } } // this event listener is no more needed window.removeEventListener('message', onIFrameCallback); }; window.addEventListener('message', onIFrameCallback); postFrameMessage({ captureSourceId: true }); } else { baseGetUserMedia(constraints, successCb, failureCb); } }; getUserMedia = navigator.getUserMedia; } else { baseGetUserMedia = window.navigator.getUserMedia; navigator.getUserMedia = function (constraints, successCb, failureCb) { if (constraints && constraints.video && !!constraints.video.mediaSource) { // would be fine since no methods var updatedConstraints = clone(constraints); // wait for plugin to be ready AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { // check if screensharing feature is available if (!!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature && !!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) { // set the constraints updatedConstraints.video.optional = updatedConstraints.video.optional || []; updatedConstraints.video.optional.push({ sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey || 'Screensharing' }); delete updatedConstraints.video.mediaSource; } else { throw new Error('Your WebRTC plugin does not support screensharing'); } baseGetUserMedia(updatedConstraints, successCb, failureCb); }); } else { baseGetUserMedia(constraints, successCb, failureCb); } }; getUserMedia = window.navigator.getUserMedia; } if (window.webrtcDetectedBrowser === 'chrome') { var iframe = document.createElement('iframe'); iframe.onload = function() { iframe.isLoaded = true; }; iframe.src = 'https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html'; //'https://temasys-cdn.s3.amazonaws.com/skylink/extensions/detection-script-dev/detectRTC.html'; iframe.style.display = 'none'; (document.body || document.documentElement).appendChild(iframe); var postFrameMessage = function (object) { object = object || {}; if (!iframe.isLoaded) { setTimeout(function () { iframe.contentWindow.postMessage(object, '*'); }, 100); return; } iframe.contentWindow.postMessage(object, '*'); }; } })();