You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

adapter.screenshare.js 46KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. /*! adapterjs - custom version from - 2015-08-12 */
  2. // Adapter's interface.
  3. var AdapterJS = AdapterJS || {};
  4. // Browserify compatibility
  5. if(typeof exports !== 'undefined') {
  6. module.exports = AdapterJS;
  7. }
  8. AdapterJS.options = AdapterJS.options || {};
  9. // uncomment to get virtual webcams
  10. // AdapterJS.options.getAllCams = true;
  11. // uncomment to prevent the install prompt when the plugin in not yet installed
  12. // AdapterJS.options.hidePluginInstallPrompt = true;
  13. // AdapterJS version
  14. AdapterJS.VERSION = '0.11.1';
  15. // This function will be called when the WebRTC API is ready to be used
  16. // Whether it is the native implementation (Chrome, Firefox, Opera) or
  17. // the plugin
  18. // You may Override this function to synchronise the start of your application
  19. // with the WebRTC API being ready.
  20. // If you decide not to override use this synchronisation, it may result in
  21. // an extensive CPU usage on the plugin start (once per tab loaded)
  22. // Params:
  23. // - isUsingPlugin: true is the WebRTC plugin is being used, false otherwise
  24. //
  25. AdapterJS.onwebrtcready = AdapterJS.onwebrtcready || function(isUsingPlugin) {
  26. // The WebRTC API is ready.
  27. // Override me and do whatever you want here
  28. };
  29. // Sets a callback function to be called when the WebRTC interface is ready.
  30. // The first argument is the function to callback.\
  31. // Throws an error if the first argument is not a function
  32. AdapterJS.webRTCReady = function (callback) {
  33. if (typeof callback !== 'function') {
  34. throw new Error('Callback provided is not a function');
  35. }
  36. if (true === AdapterJS.onwebrtcreadyDone) {
  37. // All WebRTC interfaces are ready, just call the callback
  38. callback(null !== AdapterJS.WebRTCPlugin.plugin);
  39. } else {
  40. // will be triggered automatically when your browser/plugin is ready.
  41. AdapterJS.onwebrtcready = callback;
  42. }
  43. };
  44. // Plugin namespace
  45. AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {};
  46. // The object to store plugin information
  47. AdapterJS.WebRTCPlugin.pluginInfo = {
  48. prefix : 'Tem',
  49. plugName : 'TemWebRTCPlugin',
  50. pluginId : 'plugin0',
  51. type : 'application/x-temwebrtcplugin',
  52. onload : '__TemWebRTCReady0',
  53. portalLink : 'http://skylink.io/plugin/',
  54. downloadLink : null, //set below
  55. companyName: 'Temasys'
  56. };
  57. if(!!navigator.platform.match(/^Mac/i)) {
  58. AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1n77hco';
  59. }
  60. else if(!!navigator.platform.match(/^Win/i)) {
  61. AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN';
  62. }
  63. // Unique identifier of each opened page
  64. AdapterJS.WebRTCPlugin.pageId = Math.random().toString(36).slice(2);
  65. // Use this whenever you want to call the plugin.
  66. AdapterJS.WebRTCPlugin.plugin = null;
  67. // Set log level for the plugin once it is ready.
  68. // The different values are
  69. // This is an asynchronous function that will run when the plugin is ready
  70. AdapterJS.WebRTCPlugin.setLogLevel = null;
  71. // Defines webrtc's JS interface according to the plugin's implementation.
  72. // Define plugin Browsers as WebRTC Interface.
  73. AdapterJS.WebRTCPlugin.defineWebRTCInterface = null;
  74. // This function detects whether or not a plugin is installed.
  75. // Checks if Not IE (firefox, for example), else if it's IE,
  76. // we're running IE and do something. If not it is not supported.
  77. AdapterJS.WebRTCPlugin.isPluginInstalled = null;
  78. // Lets adapter.js wait until the the document is ready before injecting the plugin
  79. AdapterJS.WebRTCPlugin.pluginInjectionInterval = null;
  80. // Inject the HTML DOM object element into the page.
  81. AdapterJS.WebRTCPlugin.injectPlugin = null;
  82. // States of readiness that the plugin goes through when
  83. // being injected and stated
  84. AdapterJS.WebRTCPlugin.PLUGIN_STATES = {
  85. NONE : 0, // no plugin use
  86. INITIALIZING : 1, // Detected need for plugin
  87. INJECTING : 2, // Injecting plugin
  88. INJECTED: 3, // Plugin element injected but not usable yet
  89. READY: 4 // Plugin ready to be used
  90. };
  91. // Current state of the plugin. You cannot use the plugin before this is
  92. // equal to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY
  93. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE;
  94. // True is AdapterJS.onwebrtcready was already called, false otherwise
  95. // Used to make sure AdapterJS.onwebrtcready is only called once
  96. AdapterJS.onwebrtcreadyDone = false;
  97. // Log levels for the plugin.
  98. // To be set by calling AdapterJS.WebRTCPlugin.setLogLevel
  99. /*
  100. Log outputs are prefixed in some cases.
  101. INFO: Information reported by the plugin.
  102. ERROR: Errors originating from within the plugin.
  103. WEBRTC: Error originating from within the libWebRTC library
  104. */
  105. // From the least verbose to the most verbose
  106. AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS = {
  107. NONE : 'NONE',
  108. ERROR : 'ERROR',
  109. WARNING : 'WARNING',
  110. INFO: 'INFO',
  111. VERBOSE: 'VERBOSE',
  112. SENSITIVE: 'SENSITIVE'
  113. };
  114. // Does a waiting check before proceeding to load the plugin.
  115. AdapterJS.WebRTCPlugin.WaitForPluginReady = null;
  116. // This methid will use an interval to wait for the plugin to be ready.
  117. AdapterJS.WebRTCPlugin.callWhenPluginReady = null;
  118. // !!!! WARNING: DO NOT OVERRIDE THIS FUNCTION. !!!
  119. // This function will be called when plugin is ready. It sends necessary
  120. // details to the plugin.
  121. // The function will wait for the document to be ready and the set the
  122. // plugin state to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,
  123. // indicating that it can start being requested.
  124. // This function is not in the IE/Safari condition brackets so that
  125. // TemPluginLoaded function might be called on Chrome/Firefox.
  126. // This function is the only private function that is not encapsulated to
  127. // allow the plugin method to be called.
  128. __TemWebRTCReady0 = function () {
  129. if (document.readyState === 'complete') {
  130. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
  131. AdapterJS.maybeThroughWebRTCReady();
  132. } else {
  133. AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () {
  134. if (document.readyState === 'complete') {
  135. // TODO: update comments, we wait for the document to be ready
  136. clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval);
  137. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
  138. AdapterJS.maybeThroughWebRTCReady();
  139. }
  140. }, 100);
  141. }
  142. };
  143. AdapterJS.maybeThroughWebRTCReady = function() {
  144. if (!AdapterJS.onwebrtcreadyDone) {
  145. AdapterJS.onwebrtcreadyDone = true;
  146. if (typeof(AdapterJS.onwebrtcready) === 'function') {
  147. AdapterJS.onwebrtcready(AdapterJS.WebRTCPlugin.plugin !== null);
  148. }
  149. }
  150. };
  151. // Text namespace
  152. AdapterJS.TEXT = {
  153. PLUGIN: {
  154. REQUIRE_INSTALLATION: 'This website requires you to install a WebRTC-enabling plugin ' +
  155. 'to work on this browser.',
  156. NOT_SUPPORTED: 'Your browser does not support WebRTC.',
  157. BUTTON: 'Install Now'
  158. },
  159. REFRESH: {
  160. REQUIRE_REFRESH: 'Please refresh page',
  161. BUTTON: 'Refresh Page'
  162. }
  163. };
  164. // The result of ice connection states.
  165. // - starting: Ice connection is starting.
  166. // - checking: Ice connection is checking.
  167. // - connected Ice connection is connected.
  168. // - completed Ice connection is connected.
  169. // - done Ice connection has been completed.
  170. // - disconnected Ice connection has been disconnected.
  171. // - failed Ice connection has failed.
  172. // - closed Ice connection is closed.
  173. AdapterJS._iceConnectionStates = {
  174. starting : 'starting',
  175. checking : 'checking',
  176. connected : 'connected',
  177. completed : 'connected',
  178. done : 'completed',
  179. disconnected : 'disconnected',
  180. failed : 'failed',
  181. closed : 'closed'
  182. };
  183. //The IceConnection states that has been fired for each peer.
  184. AdapterJS._iceConnectionFiredStates = [];
  185. // Check if WebRTC Interface is defined.
  186. AdapterJS.isDefined = null;
  187. // This function helps to retrieve the webrtc detected browser information.
  188. // This sets:
  189. // - webrtcDetectedBrowser: The browser agent name.
  190. // - webrtcDetectedVersion: The browser version.
  191. // - webrtcDetectedType: The types of webRTC support.
  192. // - 'moz': Mozilla implementation of webRTC.
  193. // - 'webkit': WebKit implementation of webRTC.
  194. // - 'plugin': Using the plugin implementation.
  195. AdapterJS.parseWebrtcDetectedBrowser = function () {
  196. var hasMatch, checkMatch = navigator.userAgent.match(
  197. /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
  198. if (/trident/i.test(checkMatch[1])) {
  199. hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
  200. webrtcDetectedBrowser = 'IE';
  201. webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
  202. } else if (checkMatch[1] === 'Chrome') {
  203. hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/);
  204. if (hasMatch !== null) {
  205. webrtcDetectedBrowser = 'opera';
  206. webrtcDetectedVersion = parseInt(hasMatch[1], 10);
  207. }
  208. }
  209. if (navigator.userAgent.indexOf('Safari')) {
  210. if (typeof InstallTrigger !== 'undefined') {
  211. webrtcDetectedBrowser = 'firefox';
  212. } else if (/*@cc_on!@*/ false || !!document.documentMode) {
  213. webrtcDetectedBrowser = 'IE';
  214. } else if (
  215. Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
  216. webrtcDetectedBrowser = 'safari';
  217. } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
  218. webrtcDetectedBrowser = 'opera';
  219. } else if (!!window.chrome) {
  220. webrtcDetectedBrowser = 'chrome';
  221. }
  222. }
  223. if (!webrtcDetectedBrowser) {
  224. webrtcDetectedVersion = checkMatch[1];
  225. }
  226. if (!webrtcDetectedVersion) {
  227. try {
  228. checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] :
  229. [navigator.appName, navigator.appVersion, '-?'];
  230. if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) {
  231. checkMatch.splice(1, 1, hasMatch[1]);
  232. }
  233. webrtcDetectedVersion = parseInt(checkMatch[1], 10);
  234. } catch (error) { }
  235. }
  236. };
  237. // To fix configuration as some browsers does not support
  238. // the 'urls' attribute.
  239. AdapterJS.maybeFixConfiguration = function (pcConfig) {
  240. if (pcConfig === null) {
  241. return;
  242. }
  243. for (var i = 0; i < pcConfig.iceServers.length; i++) {
  244. if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
  245. pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
  246. delete pcConfig.iceServers[i].urls;
  247. }
  248. }
  249. };
  250. AdapterJS.addEvent = function(elem, evnt, func) {
  251. if (elem.addEventListener) { // W3C DOM
  252. elem.addEventListener(evnt, func, false);
  253. } else if (elem.attachEvent) {// OLD IE DOM
  254. elem.attachEvent('on'+evnt, func);
  255. } else { // No much to do
  256. elem[evnt] = func;
  257. }
  258. };
  259. AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNewTab, displayRefreshBar) {
  260. // only inject once the page is ready
  261. if (document.readyState !== 'complete') {
  262. return;
  263. }
  264. var w = window;
  265. var i = document.createElement('iframe');
  266. i.style.position = 'fixed';
  267. i.style.top = '-41px';
  268. i.style.left = 0;
  269. i.style.right = 0;
  270. i.style.width = '100%';
  271. i.style.height = '40px';
  272. i.style.backgroundColor = '#ffffe1';
  273. i.style.border = 'none';
  274. i.style.borderBottom = '1px solid #888888';
  275. i.style.zIndex = '9999999';
  276. if(typeof i.style.webkitTransition === 'string') {
  277. i.style.webkitTransition = 'all .5s ease-out';
  278. } else if(typeof i.style.transition === 'string') {
  279. i.style.transition = 'all .5s ease-out';
  280. }
  281. document.body.appendChild(i);
  282. c = (i.contentWindow) ? i.contentWindow :
  283. (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
  284. c.document.open();
  285. c.document.write('<span style="display: inline-block; font-family: Helvetica, Arial,' +
  286. 'sans-serif; font-size: .9rem; padding: 4px; vertical-align: ' +
  287. 'middle; cursor: default;">' + text + '</span>');
  288. if(buttonText && buttonLink) {
  289. c.document.write('<button id="okay">' + buttonText + '</button><button>Cancel</button>');
  290. c.document.close();
  291. AdapterJS.addEvent(c.document.getElementById('okay'), 'click', function(e) {
  292. if (!!displayRefreshBar) {
  293. AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION ?
  294. AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH : AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH,
  295. AdapterJS.TEXT.REFRESH.BUTTON, 'javascript:location.reload()');
  296. }
  297. window.open(buttonLink, !!openNewTab ? '_blank' : '_top');
  298. e.preventDefault();
  299. try {
  300. event.cancelBubble = true;
  301. } catch(error) { }
  302. var pluginInstallInterval = setInterval(function(){
  303. if(! isIE) {
  304. navigator.plugins.refresh(false);
  305. }
  306. AdapterJS.WebRTCPlugin.isPluginInstalled(
  307. AdapterJS.WebRTCPlugin.pluginInfo.prefix,
  308. AdapterJS.WebRTCPlugin.pluginInfo.plugName,
  309. function() {
  310. clearInterval(pluginInstallInterval);
  311. AdapterJS.WebRTCPlugin.defineWebRTCInterface()
  312. },
  313. function() { //Does nothing because not used here
  314. });
  315. } , 500);
  316. });
  317. }else {
  318. c.document.close();
  319. }
  320. AdapterJS.addEvent(c.document, 'click', function() {
  321. w.document.body.removeChild(i);
  322. });
  323. setTimeout(function() {
  324. if(typeof i.style.webkitTransform === 'string') {
  325. i.style.webkitTransform = 'translateY(40px)';
  326. } else if(typeof i.style.transform === 'string') {
  327. i.style.transform = 'translateY(40px)';
  328. } else {
  329. i.style.top = '0px';
  330. }
  331. }, 300);
  332. };
  333. // -----------------------------------------------------------
  334. // Detected webrtc implementation. Types are:
  335. // - 'moz': Mozilla implementation of webRTC.
  336. // - 'webkit': WebKit implementation of webRTC.
  337. // - 'plugin': Using the plugin implementation.
  338. webrtcDetectedType = null;
  339. // Detected webrtc datachannel support. Types are:
  340. // - 'SCTP': SCTP datachannel support.
  341. // - 'RTP': RTP datachannel support.
  342. webrtcDetectedDCSupport = null;
  343. // Set the settings for creating DataChannels, MediaStream for
  344. // Cross-browser compability.
  345. // - This is only for SCTP based support browsers.
  346. // the 'urls' attribute.
  347. checkMediaDataChannelSettings =
  348. function (peerBrowserAgent, peerBrowserVersion, callback, constraints) {
  349. if (typeof callback !== 'function') {
  350. return;
  351. }
  352. var beOfferer = true;
  353. var isLocalFirefox = webrtcDetectedBrowser === 'firefox';
  354. // Nightly version does not require MozDontOfferDataChannel for interop
  355. var isLocalFirefoxInterop = webrtcDetectedType === 'moz' && webrtcDetectedVersion > 30;
  356. var isPeerFirefox = peerBrowserAgent === 'firefox';
  357. var isPeerFirefoxInterop = peerBrowserAgent === 'firefox' &&
  358. ((peerBrowserVersion) ? (peerBrowserVersion > 30) : false);
  359. // Resends an updated version of constraints for MozDataChannel to work
  360. // If other userAgent is firefox and user is firefox, remove MozDataChannel
  361. if ((isLocalFirefox && isPeerFirefox) || (isLocalFirefoxInterop)) {
  362. try {
  363. delete constraints.mandatory.MozDontOfferDataChannel;
  364. } catch (error) {
  365. console.error('Failed deleting MozDontOfferDataChannel');
  366. console.error(error);
  367. }
  368. } else if ((isLocalFirefox && !isPeerFirefox)) {
  369. constraints.mandatory.MozDontOfferDataChannel = true;
  370. }
  371. if (!isLocalFirefox) {
  372. // temporary measure to remove Moz* constraints in non Firefox browsers
  373. for (var prop in constraints.mandatory) {
  374. if (constraints.mandatory.hasOwnProperty(prop)) {
  375. if (prop.indexOf('Moz') !== -1) {
  376. delete constraints.mandatory[prop];
  377. }
  378. }
  379. }
  380. }
  381. // Firefox (not interopable) cannot offer DataChannel as it will cause problems to the
  382. // interopability of the media stream
  383. if (isLocalFirefox && !isPeerFirefox && !isLocalFirefoxInterop) {
  384. beOfferer = false;
  385. }
  386. callback(beOfferer, constraints);
  387. };
  388. // Handles the differences for all browsers ice connection state output.
  389. // - Tested outcomes are:
  390. // - Chrome (offerer) : 'checking' > 'completed' > 'completed'
  391. // - Chrome (answerer) : 'checking' > 'connected'
  392. // - Firefox (offerer) : 'checking' > 'connected'
  393. // - Firefox (answerer): 'checking' > 'connected'
  394. checkIceConnectionState = function (peerId, iceConnectionState, callback) {
  395. if (typeof callback !== 'function') {
  396. console.warn('No callback specified in checkIceConnectionState. Aborted.');
  397. return;
  398. }
  399. peerId = (peerId) ? peerId : 'peer';
  400. if (!AdapterJS._iceConnectionFiredStates[peerId] ||
  401. iceConnectionState === AdapterJS._iceConnectionStates.disconnected ||
  402. iceConnectionState === AdapterJS._iceConnectionStates.failed ||
  403. iceConnectionState === AdapterJS._iceConnectionStates.closed) {
  404. AdapterJS._iceConnectionFiredStates[peerId] = [];
  405. }
  406. iceConnectionState = AdapterJS._iceConnectionStates[iceConnectionState];
  407. if (AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState) < 0) {
  408. AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState);
  409. if (iceConnectionState === AdapterJS._iceConnectionStates.connected) {
  410. setTimeout(function () {
  411. AdapterJS._iceConnectionFiredStates[peerId]
  412. .push(AdapterJS._iceConnectionStates.done);
  413. callback(AdapterJS._iceConnectionStates.done);
  414. }, 1000);
  415. }
  416. callback(iceConnectionState);
  417. }
  418. return;
  419. };
  420. // Firefox:
  421. // - Creates iceServer from the url for Firefox.
  422. // - Create iceServer with stun url.
  423. // - Create iceServer with turn url.
  424. // - Ignore the transport parameter from TURN url for FF version <=27.
  425. // - Return null for createIceServer if transport=tcp.
  426. // - FF 27 and above supports transport parameters in TURN url,
  427. // - So passing in the full url to create iceServer.
  428. // Chrome:
  429. // - Creates iceServer from the url for Chrome M33 and earlier.
  430. // - Create iceServer with stun url.
  431. // - Chrome M28 & above uses below TURN format.
  432. // Plugin:
  433. // - Creates Ice Server for Plugin Browsers
  434. // - If Stun - Create iceServer with stun url.
  435. // - Else - Create iceServer with turn url
  436. // - This is a WebRTC Function
  437. createIceServer = null;
  438. // Firefox:
  439. // - Creates IceServers for Firefox
  440. // - Use .url for FireFox.
  441. // - Multiple Urls support
  442. // Chrome:
  443. // - Creates iceServers from the urls for Chrome M34 and above.
  444. // - .urls is supported since Chrome M34.
  445. // - Multiple Urls support
  446. // Plugin:
  447. // - Creates Ice Servers for Plugin Browsers
  448. // - Multiple Urls support
  449. // - This is a WebRTC Function
  450. createIceServers = null;
  451. //------------------------------------------------------------
  452. //The RTCPeerConnection object.
  453. RTCPeerConnection = null;
  454. // Creates RTCSessionDescription object for Plugin Browsers
  455. RTCSessionDescription = (typeof RTCSessionDescription === 'function') ?
  456. RTCSessionDescription : null;
  457. // Creates RTCIceCandidate object for Plugin Browsers
  458. RTCIceCandidate = (typeof RTCIceCandidate === 'function') ?
  459. RTCIceCandidate : null;
  460. // Get UserMedia (only difference is the prefix).
  461. // Code from Adam Barth.
  462. getUserMedia = null;
  463. // Attach a media stream to an element.
  464. attachMediaStream = null;
  465. // Re-attach a media stream to an element.
  466. reattachMediaStream = null;
  467. // Detected browser agent name. Types are:
  468. // - 'firefox': Firefox browser.
  469. // - 'chrome': Chrome browser.
  470. // - 'opera': Opera browser.
  471. // - 'safari': Safari browser.
  472. // - 'IE' - Internet Explorer browser.
  473. webrtcDetectedBrowser = null;
  474. // Detected browser version.
  475. webrtcDetectedVersion = null;
  476. // Check for browser types and react accordingly
  477. if (navigator.mozGetUserMedia) {
  478. webrtcDetectedBrowser = 'firefox';
  479. webrtcDetectedVersion = parseInt(navigator
  480. .userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
  481. webrtcDetectedType = 'moz';
  482. webrtcDetectedDCSupport = 'SCTP';
  483. RTCPeerConnection = function (pcConfig, pcConstraints) {
  484. AdapterJS.maybeFixConfiguration(pcConfig);
  485. return new mozRTCPeerConnection(pcConfig, pcConstraints);
  486. };
  487. // The RTCSessionDescription object.
  488. RTCSessionDescription = mozRTCSessionDescription;
  489. window.RTCSessionDescription = RTCSessionDescription;
  490. // The RTCIceCandidate object.
  491. RTCIceCandidate = mozRTCIceCandidate;
  492. window.RTCIceCandidate = RTCIceCandidate;
  493. window.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
  494. navigator.getUserMedia = window.getUserMedia;
  495. // Shim for MediaStreamTrack.getSources.
  496. MediaStreamTrack.getSources = function(successCb) {
  497. setTimeout(function() {
  498. var infos = [
  499. { kind: 'audio', id: 'default', label:'', facing:'' },
  500. { kind: 'video', id: 'default', label:'', facing:'' }
  501. ];
  502. successCb(infos);
  503. }, 0);
  504. };
  505. createIceServer = function (url, username, password) {
  506. var iceServer = null;
  507. var url_parts = url.split(':');
  508. if (url_parts[0].indexOf('stun') === 0) {
  509. iceServer = { url : url };
  510. } else if (url_parts[0].indexOf('turn') === 0) {
  511. if (webrtcDetectedVersion < 27) {
  512. var turn_url_parts = url.split('?');
  513. if (turn_url_parts.length === 1 ||
  514. turn_url_parts[1].indexOf('transport=udp') === 0) {
  515. iceServer = {
  516. url : turn_url_parts[0],
  517. credential : password,
  518. username : username
  519. };
  520. }
  521. } else {
  522. iceServer = {
  523. url : url,
  524. credential : password,
  525. username : username
  526. };
  527. }
  528. }
  529. return iceServer;
  530. };
  531. createIceServers = function (urls, username, password) {
  532. var iceServers = [];
  533. for (i = 0; i < urls.length; i++) {
  534. var iceServer = createIceServer(urls[i], username, password);
  535. if (iceServer !== null) {
  536. iceServers.push(iceServer);
  537. }
  538. }
  539. return iceServers;
  540. };
  541. attachMediaStream = function (element, stream) {
  542. element.mozSrcObject = stream;
  543. if (stream !== null)
  544. element.play();
  545. return element;
  546. };
  547. reattachMediaStream = function (to, from) {
  548. to.mozSrcObject = from.mozSrcObject;
  549. to.play();
  550. return to;
  551. };
  552. MediaStreamTrack.getSources = MediaStreamTrack.getSources || function (callback) {
  553. if (!callback) {
  554. throw new TypeError('Failed to execute \'getSources\' on \'MediaStreamTrack\'' +
  555. ': 1 argument required, but only 0 present.');
  556. }
  557. return callback([]);
  558. };
  559. // Fake get{Video,Audio}Tracks
  560. if (!MediaStream.prototype.getVideoTracks) {
  561. MediaStream.prototype.getVideoTracks = function () {
  562. return [];
  563. };
  564. }
  565. if (!MediaStream.prototype.getAudioTracks) {
  566. MediaStream.prototype.getAudioTracks = function () {
  567. return [];
  568. };
  569. }
  570. AdapterJS.maybeThroughWebRTCReady();
  571. } else if (navigator.webkitGetUserMedia) {
  572. webrtcDetectedBrowser = 'chrome';
  573. webrtcDetectedType = 'webkit';
  574. webrtcDetectedVersion = parseInt(navigator
  575. .userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
  576. // check if browser is opera 20+
  577. var checkIfOpera = navigator.userAgent.match(/\bOPR\/(\d+)/);
  578. if (checkIfOpera !== null) {
  579. webrtcDetectedBrowser = 'opera';
  580. webrtcDetectedVersion = parseInt(checkIfOpera[1], 10);
  581. }
  582. // check browser datachannel support
  583. if ((webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion >= 31) ||
  584. (webrtcDetectedBrowser === 'opera' && webrtcDetectedVersion >= 20)) {
  585. webrtcDetectedDCSupport = 'SCTP';
  586. } else if (webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion < 30 &&
  587. webrtcDetectedVersion > 24) {
  588. webrtcDetectedDCSupport = 'RTP';
  589. } else {
  590. webrtcDetectedDCSupport = '';
  591. }
  592. createIceServer = function (url, username, password) {
  593. var iceServer = null;
  594. var url_parts = url.split(':');
  595. if (url_parts[0].indexOf('stun') === 0) {
  596. iceServer = { 'url' : url };
  597. } else if (url_parts[0].indexOf('turn') === 0) {
  598. iceServer = {
  599. 'url' : url,
  600. 'credential' : password,
  601. 'username' : username
  602. };
  603. }
  604. return iceServer;
  605. };
  606. createIceServers = function (urls, username, password) {
  607. var iceServers = [];
  608. if (webrtcDetectedVersion >= 34) {
  609. iceServers = {
  610. 'urls' : urls,
  611. 'credential' : password,
  612. 'username' : username
  613. };
  614. } else {
  615. for (i = 0; i < urls.length; i++) {
  616. var iceServer = createIceServer(urls[i], username, password);
  617. if (iceServer !== null) {
  618. iceServers.push(iceServer);
  619. }
  620. }
  621. }
  622. return iceServers;
  623. };
  624. RTCPeerConnection = function (pcConfig, pcConstraints) {
  625. if (webrtcDetectedVersion < 34) {
  626. AdapterJS.maybeFixConfiguration(pcConfig);
  627. }
  628. return new webkitRTCPeerConnection(pcConfig, pcConstraints);
  629. };
  630. window.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
  631. navigator.getUserMedia = window.getUserMedia;
  632. attachMediaStream = function (element, stream) {
  633. if (typeof element.srcObject !== 'undefined') {
  634. element.srcObject = stream;
  635. } else if (typeof element.mozSrcObject !== 'undefined') {
  636. element.mozSrcObject = stream;
  637. } else if (typeof element.src !== 'undefined') {
  638. element.src = (stream === null ? '' : URL.createObjectURL(stream));
  639. } else {
  640. console.log('Error attaching stream to element.');
  641. }
  642. return element;
  643. };
  644. reattachMediaStream = function (to, from) {
  645. to.src = from.src;
  646. return to;
  647. };
  648. AdapterJS.maybeThroughWebRTCReady();
  649. } else { // TRY TO USE PLUGIN
  650. // IE 9 is not offering an implementation of console.log until you open a console
  651. if (typeof console !== 'object' || typeof console.log !== 'function') {
  652. /* jshint -W020 */
  653. console = {} || console;
  654. // Implemented based on console specs from MDN
  655. // You may override these functions
  656. console.log = function (arg) {};
  657. console.info = function (arg) {};
  658. console.error = function (arg) {};
  659. console.dir = function (arg) {};
  660. console.exception = function (arg) {};
  661. console.trace = function (arg) {};
  662. console.warn = function (arg) {};
  663. console.count = function (arg) {};
  664. console.debug = function (arg) {};
  665. console.count = function (arg) {};
  666. console.time = function (arg) {};
  667. console.timeEnd = function (arg) {};
  668. console.group = function (arg) {};
  669. console.groupCollapsed = function (arg) {};
  670. console.groupEnd = function (arg) {};
  671. /* jshint +W020 */
  672. }
  673. webrtcDetectedType = 'plugin';
  674. webrtcDetectedDCSupport = 'plugin';
  675. AdapterJS.parseWebrtcDetectedBrowser();
  676. isIE = webrtcDetectedBrowser === 'IE';
  677. /* jshint -W035 */
  678. AdapterJS.WebRTCPlugin.WaitForPluginReady = function() {
  679. while (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  680. /* empty because it needs to prevent the function from running. */
  681. }
  682. };
  683. /* jshint +W035 */
  684. AdapterJS.WebRTCPlugin.callWhenPluginReady = function (callback) {
  685. if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  686. // Call immediately if possible
  687. // Once the plugin is set, the code will always take this path
  688. callback();
  689. } else {
  690. // otherwise start a 100ms interval
  691. var checkPluginReadyState = setInterval(function () {
  692. if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  693. clearInterval(checkPluginReadyState);
  694. callback();
  695. }
  696. }, 100);
  697. }
  698. };
  699. AdapterJS.WebRTCPlugin.setLogLevel = function(logLevel) {
  700. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  701. AdapterJS.WebRTCPlugin.plugin.setLogLevel(logLevel);
  702. });
  703. };
  704. AdapterJS.WebRTCPlugin.injectPlugin = function () {
  705. // only inject once the page is ready
  706. if (document.readyState !== 'complete') {
  707. return;
  708. }
  709. // Prevent multiple injections
  710. if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) {
  711. return;
  712. }
  713. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING;
  714. if (webrtcDetectedBrowser === 'IE' && webrtcDetectedVersion <= 10) {
  715. var frag = document.createDocumentFragment();
  716. AdapterJS.WebRTCPlugin.plugin = document.createElement('div');
  717. AdapterJS.WebRTCPlugin.plugin.innerHTML = '<object id="' +
  718. AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '" type="' +
  719. AdapterJS.WebRTCPlugin.pluginInfo.type + '" ' + 'width="1" height="1">' +
  720. '<param name="pluginId" value="' +
  721. AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '" /> ' +
  722. '<param name="windowless" value="false" /> ' +
  723. '<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
  724. '<param name="onload" value="' + AdapterJS.WebRTCPlugin.pluginInfo.onload +
  725. '" />' +
  726. // uncomment to be able to use virtual cams
  727. (AdapterJS.options.getAllCams ? '<param name="forceGetAllCams" value="True" />':'') +
  728. '</object>';
  729. while (AdapterJS.WebRTCPlugin.plugin.firstChild) {
  730. frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);
  731. }
  732. document.body.appendChild(frag);
  733. // Need to re-fetch the plugin
  734. AdapterJS.WebRTCPlugin.plugin =
  735. document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId);
  736. } else {
  737. // Load Plugin
  738. AdapterJS.WebRTCPlugin.plugin = document.createElement('object');
  739. AdapterJS.WebRTCPlugin.plugin.id =
  740. AdapterJS.WebRTCPlugin.pluginInfo.pluginId;
  741. // IE will only start the plugin if it's ACTUALLY visible
  742. if (isIE) {
  743. AdapterJS.WebRTCPlugin.plugin.width = '1px';
  744. AdapterJS.WebRTCPlugin.plugin.height = '1px';
  745. } else { // The size of the plugin on Safari should be 0x0px
  746. // so that the autorisation prompt is at the top
  747. AdapterJS.WebRTCPlugin.plugin.width = '0px';
  748. AdapterJS.WebRTCPlugin.plugin.height = '0px';
  749. }
  750. AdapterJS.WebRTCPlugin.plugin.type = AdapterJS.WebRTCPlugin.pluginInfo.type;
  751. AdapterJS.WebRTCPlugin.plugin.innerHTML = '<param name="onload" value="' +
  752. AdapterJS.WebRTCPlugin.pluginInfo.onload + '">' +
  753. '<param name="pluginId" value="' +
  754. AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '">' +
  755. '<param name="windowless" value="false" /> ' +
  756. (AdapterJS.options.getAllCams ? '<param name="forceGetAllCams" value="True" />':'') +
  757. '<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '">';
  758. document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);
  759. }
  760. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED;
  761. };
  762. AdapterJS.WebRTCPlugin.isPluginInstalled =
  763. function (comName, plugName, installedCb, notInstalledCb) {
  764. if (!isIE) {
  765. var pluginArray = navigator.plugins;
  766. for (var i = 0; i < pluginArray.length; i++) {
  767. if (pluginArray[i].name.indexOf(plugName) >= 0) {
  768. installedCb();
  769. return;
  770. }
  771. }
  772. notInstalledCb();
  773. } else {
  774. try {
  775. var axo = new ActiveXObject(comName + '.' + plugName);
  776. } catch (e) {
  777. notInstalledCb();
  778. return;
  779. }
  780. installedCb();
  781. }
  782. };
  783. AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
  784. if (AdapterJS.WebRTCPlugin.pluginState ===
  785. AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  786. console.error("WebRTC interface has been defined already");
  787. return;
  788. }
  789. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING;
  790. AdapterJS.isDefined = function (variable) {
  791. return variable !== null && variable !== undefined;
  792. };
  793. createIceServer = function (url, username, password) {
  794. var iceServer = null;
  795. var url_parts = url.split(':');
  796. if (url_parts[0].indexOf('stun') === 0) {
  797. iceServer = {
  798. 'url' : url,
  799. 'hasCredentials' : false
  800. };
  801. } else if (url_parts[0].indexOf('turn') === 0) {
  802. iceServer = {
  803. 'url' : url,
  804. 'hasCredentials' : true,
  805. 'credential' : password,
  806. 'username' : username
  807. };
  808. }
  809. return iceServer;
  810. };
  811. createIceServers = function (urls, username, password) {
  812. var iceServers = [];
  813. for (var i = 0; i < urls.length; ++i) {
  814. iceServers.push(createIceServer(urls[i], username, password));
  815. }
  816. return iceServers;
  817. };
  818. RTCSessionDescription = function (info) {
  819. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  820. return AdapterJS.WebRTCPlugin.plugin.
  821. ConstructSessionDescription(info.type, info.sdp);
  822. };
  823. RTCPeerConnection = function (servers, constraints) {
  824. var iceServers = null;
  825. if (servers) {
  826. iceServers = servers.iceServers;
  827. for (var i = 0; i < iceServers.length; i++) {
  828. if (iceServers[i].urls && !iceServers[i].url) {
  829. iceServers[i].url = iceServers[i].urls;
  830. }
  831. iceServers[i].hasCredentials = AdapterJS.
  832. isDefined(iceServers[i].username) &&
  833. AdapterJS.isDefined(iceServers[i].credential);
  834. }
  835. }
  836. var mandatory = (constraints && constraints.mandatory) ?
  837. constraints.mandatory : null;
  838. var optional = (constraints && constraints.optional) ?
  839. constraints.optional : null;
  840. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  841. return AdapterJS.WebRTCPlugin.plugin.
  842. PeerConnection(AdapterJS.WebRTCPlugin.pageId,
  843. iceServers, mandatory, optional);
  844. };
  845. MediaStreamTrack = {};
  846. MediaStreamTrack.getSources = function (callback) {
  847. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  848. AdapterJS.WebRTCPlugin.plugin.GetSources(callback);
  849. });
  850. };
  851. window.getUserMedia = function (constraints, successCallback, failureCallback) {
  852. constraints.audio = constraints.audio || false;
  853. constraints.video = constraints.video || false;
  854. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  855. AdapterJS.WebRTCPlugin.plugin.
  856. getUserMedia(constraints, successCallback, failureCallback);
  857. });
  858. };
  859. window.navigator.getUserMedia = window.getUserMedia;
  860. attachMediaStream = function (element, stream) {
  861. if (!element || !element.parentNode) {
  862. return;
  863. }
  864. var streamId
  865. if (stream === null) {
  866. streamId = '';
  867. }
  868. else {
  869. stream.enableSoundTracks(true);
  870. streamId = stream.id;
  871. }
  872. if (element.nodeName.toLowerCase() !== 'audio') {
  873. var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id;
  874. if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) {
  875. var frag = document.createDocumentFragment();
  876. var temp = document.createElement('div');
  877. var classHTML = '';
  878. if (element.className) {
  879. classHTML = 'class="' + element.className + '" ';
  880. } else if (element.attributes && element.attributes['class']) {
  881. classHTML = 'class="' + element.attributes['class'].value + '" ';
  882. }
  883. temp.innerHTML = '<object id="' + elementId + '" ' + classHTML +
  884. 'type="' + AdapterJS.WebRTCPlugin.pluginInfo.type + '">' +
  885. '<param name="pluginId" value="' + elementId + '" /> ' +
  886. '<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
  887. '<param name="windowless" value="true" /> ' +
  888. '<param name="streamId" value="' + streamId + '" /> ' +
  889. '</object>';
  890. while (temp.firstChild) {
  891. frag.appendChild(temp.firstChild);
  892. }
  893. var height = '';
  894. var width = '';
  895. if (element.getBoundingClientRect) {
  896. var rectObject = element.getBoundingClientRect();
  897. width = rectObject.width + 'px';
  898. height = rectObject.height + 'px';
  899. }
  900. else if (element.width) {
  901. width = element.width;
  902. height = element.height;
  903. } else {
  904. // TODO: What scenario could bring us here?
  905. }
  906. element.parentNode.insertBefore(frag, element);
  907. frag = document.getElementById(elementId);
  908. frag.width = width;
  909. frag.height = height;
  910. element.parentNode.removeChild(element);
  911. } else {
  912. var children = element.children;
  913. for (var i = 0; i !== children.length; ++i) {
  914. if (children[i].name === 'streamId') {
  915. children[i].value = streamId;
  916. break;
  917. }
  918. }
  919. element.setStreamId(streamId);
  920. }
  921. var newElement = document.getElementById(elementId);
  922. newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
  923. if (isIE) { // on IE the event needs to be plugged manually
  924. newElement.attachEvent('onplaying', newElement.onplaying);
  925. newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
  926. newElement._TemOnClick = function (id) {
  927. var arg = {
  928. srcElement : document.getElementById(id)
  929. };
  930. newElement.onclick(arg);
  931. };
  932. }
  933. return newElement;
  934. } else {
  935. return element;
  936. }
  937. };
  938. reattachMediaStream = function (to, from) {
  939. var stream = null;
  940. var children = from.children;
  941. for (var i = 0; i !== children.length; ++i) {
  942. if (children[i].name === 'streamId') {
  943. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  944. stream = AdapterJS.WebRTCPlugin.plugin
  945. .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, children[i].value);
  946. break;
  947. }
  948. }
  949. if (stream !== null) {
  950. return attachMediaStream(to, stream);
  951. } else {
  952. console.log('Could not find the stream associated with this element');
  953. }
  954. };
  955. RTCIceCandidate = function (candidate) {
  956. if (!candidate.sdpMid) {
  957. candidate.sdpMid = '';
  958. }
  959. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  960. return AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(
  961. candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate
  962. );
  963. };
  964. // inject plugin
  965. AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.injectPlugin);
  966. AdapterJS.WebRTCPlugin.injectPlugin();
  967. };
  968. // This function will be called if the plugin is needed (browser different
  969. // from Chrome or Firefox), but the plugin is not installed.
  970. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb ||
  971. function() {
  972. AdapterJS.addEvent(document,
  973. 'readystatechange',
  974. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv);
  975. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv();
  976. };
  977. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv = function () {
  978. if (AdapterJS.options.hidePluginInstallPrompt) {
  979. return;
  980. }
  981. var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;
  982. if(downloadLink) { // if download link
  983. var popupString;
  984. if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { // is portal link
  985. popupString = 'This website requires you to install the ' +
  986. ' <a href="' + AdapterJS.WebRTCPlugin.pluginInfo.portalLink +
  987. '" target="_blank">' + AdapterJS.WebRTCPlugin.pluginInfo.companyName +
  988. ' WebRTC Plugin</a>' +
  989. ' to work on this browser.';
  990. } else { // no portal link, just print a generic explanation
  991. popupString = AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION;
  992. }
  993. AdapterJS.renderNotificationBar(popupString, AdapterJS.TEXT.PLUGIN.BUTTON, downloadLink);
  994. } else { // no download link, just print a generic explanation
  995. AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED);
  996. }
  997. };
  998. // Try to detect the plugin and act accordingly
  999. AdapterJS.WebRTCPlugin.isPluginInstalled(
  1000. AdapterJS.WebRTCPlugin.pluginInfo.prefix,
  1001. AdapterJS.WebRTCPlugin.pluginInfo.plugName,
  1002. AdapterJS.WebRTCPlugin.defineWebRTCInterface,
  1003. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
  1004. }
  1005. (function () {
  1006. 'use strict';
  1007. var baseGetUserMedia = null;
  1008. AdapterJS.TEXT.EXTENSION = {
  1009. REQUIRE_INSTALLATION_FF: 'To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.',
  1010. REQUIRE_INSTALLATION_CHROME: 'To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.',
  1011. REQUIRE_REFRESH: 'Please refresh this page after the Skylink WebRTC tools extension has been installed.',
  1012. BUTTON_FF: 'Install Now',
  1013. BUTTON_CHROME: 'Go to Chrome Web Store'
  1014. };
  1015. var clone = function(obj) {
  1016. if (null == obj || "object" != typeof obj) return obj;
  1017. var copy = obj.constructor();
  1018. for (var attr in obj) {
  1019. if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
  1020. }
  1021. return copy;
  1022. };
  1023. if (window.navigator.mozGetUserMedia) {
  1024. baseGetUserMedia = window.navigator.getUserMedia;
  1025. navigator.getUserMedia = function (constraints, successCb, failureCb) {
  1026. if (constraints && constraints.video && !!constraints.video.mediaSource) {
  1027. // intercepting screensharing requests
  1028. if (constraints.video.mediaSource !== 'screen' && constraints.video.mediaSource !== 'window') {
  1029. throw new Error('Only "screen" and "window" option is available as mediaSource');
  1030. }
  1031. var updatedConstraints = clone(constraints);
  1032. //constraints.video.mediaSource = constraints.video.mediaSource;
  1033. updatedConstraints.video.mozMediaSource = updatedConstraints.video.mediaSource;
  1034. // so generally, it requires for document.readyState to be completed before the getUserMedia could be invoked.
  1035. // strange but this works anyway
  1036. var checkIfReady = setInterval(function () {
  1037. if (document.readyState === 'complete') {
  1038. clearInterval(checkIfReady);
  1039. baseGetUserMedia(updatedConstraints, successCb, function (error) {
  1040. if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') {
  1041. AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,
  1042. AdapterJS.TEXT.EXTENSION.BUTTON_FF,
  1043. 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true);
  1044. //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname;
  1045. } else {
  1046. failureCb(error);
  1047. }
  1048. });
  1049. }
  1050. }, 1);
  1051. } else { // regular GetUserMediaRequest
  1052. baseGetUserMedia(constraints, successCb, failureCb);
  1053. }
  1054. };
  1055. getUserMedia = navigator.getUserMedia;
  1056. } else if (window.navigator.webkitGetUserMedia) {
  1057. baseGetUserMedia = window.navigator.getUserMedia;
  1058. navigator.getUserMedia = function (constraints, successCb, failureCb) {
  1059. if (constraints && constraints.video && !!constraints.video.mediaSource) {
  1060. if (window.webrtcDetectedBrowser !== 'chrome') {
  1061. throw new Error('Current browser does not support screensharing');
  1062. }
  1063. // would be fine since no methods
  1064. var updatedConstraints = clone(constraints);
  1065. var chromeCallback = function(error, sourceId) {
  1066. if(!error) {
  1067. updatedConstraints.video.mandatory = updatedConstraints.video.mandatory || {};
  1068. updatedConstraints.video.mandatory.chromeMediaSource = 'desktop';
  1069. updatedConstraints.video.mandatory.maxWidth = window.screen.width > 1920 ? window.screen.width : 1920;
  1070. updatedConstraints.video.mandatory.maxHeight = window.screen.height > 1080 ? window.screen.height : 1080;
  1071. if (sourceId) {
  1072. updatedConstraints.video.mandatory.chromeMediaSourceId = sourceId;
  1073. }
  1074. delete updatedConstraints.video.mediaSource;
  1075. baseGetUserMedia(updatedConstraints, successCb, failureCb);
  1076. } else {
  1077. if (error === 'permission-denied') {
  1078. throw new Error('Permission denied for screen retrieval');
  1079. } else {
  1080. throw new Error('Failed retrieving selected screen');
  1081. }
  1082. }
  1083. };
  1084. var onIFrameCallback = function (event) {
  1085. if (!event.data) {
  1086. return;
  1087. }
  1088. if (event.data.chromeMediaSourceId) {
  1089. if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
  1090. chromeCallback('permission-denied');
  1091. } else {
  1092. chromeCallback(null, event.data.chromeMediaSourceId);
  1093. }
  1094. }
  1095. if (event.data.chromeExtensionStatus) {
  1096. if (event.data.chromeExtensionStatus === 'not-installed') {
  1097. AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,
  1098. AdapterJS.TEXT.EXTENSION.BUTTON_CHROME,
  1099. event.data.data, true, true);
  1100. } else {
  1101. chromeCallback(event.data.chromeExtensionStatus, null);
  1102. }
  1103. }
  1104. // this event listener is no more needed
  1105. window.removeEventListener('message', onIFrameCallback);
  1106. };
  1107. window.addEventListener('message', onIFrameCallback);
  1108. postFrameMessage({
  1109. captureSourceId: true
  1110. });
  1111. } else {
  1112. baseGetUserMedia(constraints, successCb, failureCb);
  1113. }
  1114. };
  1115. getUserMedia = navigator.getUserMedia;
  1116. } else {
  1117. baseGetUserMedia = window.navigator.getUserMedia;
  1118. navigator.getUserMedia = function (constraints, successCb, failureCb) {
  1119. if (constraints && constraints.video && !!constraints.video.mediaSource) {
  1120. // would be fine since no methods
  1121. var updatedConstraints = clone(constraints);
  1122. // wait for plugin to be ready
  1123. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  1124. // check if screensharing feature is available
  1125. if (!!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature &&
  1126. !!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) {
  1127. // set the constraints
  1128. updatedConstraints.video.optional = updatedConstraints.video.optional || [];
  1129. updatedConstraints.video.optional.push({
  1130. sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey || 'Screensharing'
  1131. });
  1132. delete updatedConstraints.video.mediaSource;
  1133. } else {
  1134. throw new Error('Your WebRTC plugin does not support screensharing');
  1135. }
  1136. baseGetUserMedia(updatedConstraints, successCb, failureCb);
  1137. });
  1138. } else {
  1139. baseGetUserMedia(constraints, successCb, failureCb);
  1140. }
  1141. };
  1142. getUserMedia = window.navigator.getUserMedia;
  1143. }
  1144. if (window.webrtcDetectedBrowser === 'chrome') {
  1145. var iframe = document.createElement('iframe');
  1146. iframe.onload = function() {
  1147. iframe.isLoaded = true;
  1148. };
  1149. iframe.src = 'https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html';
  1150. //'https://temasys-cdn.s3.amazonaws.com/skylink/extensions/detection-script-dev/detectRTC.html';
  1151. iframe.style.display = 'none';
  1152. (document.body || document.documentElement).appendChild(iframe);
  1153. var postFrameMessage = function (object) {
  1154. object = object || {};
  1155. if (!iframe.isLoaded) {
  1156. setTimeout(function () {
  1157. iframe.contentWindow.postMessage(object, '*');
  1158. }, 100);
  1159. return;
  1160. }
  1161. iframe.contentWindow.postMessage(object, '*');
  1162. };
  1163. }
  1164. })();