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 39KB

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