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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322
  1. /*! adapterjs - v0.11.1 - 2015-07-28 */
  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. AdapterJS.WebRTCPlugin.defineWebRTCInterface,
  310. function() { //Does nothing because not used here
  311. });
  312. } , 500);
  313. });
  314. }else {
  315. c.document.close();
  316. }
  317. AdapterJS.addEvent(c.document, 'click', function() {
  318. w.document.body.removeChild(i);
  319. });
  320. setTimeout(function() {
  321. if(typeof i.style.webkitTransform === 'string') {
  322. i.style.webkitTransform = 'translateY(40px)';
  323. } else if(typeof i.style.transform === 'string') {
  324. i.style.transform = 'translateY(40px)';
  325. } else {
  326. i.style.top = '0px';
  327. }
  328. }, 300);
  329. };
  330. // -----------------------------------------------------------
  331. // Detected webrtc implementation. Types are:
  332. // - 'moz': Mozilla implementation of webRTC.
  333. // - 'webkit': WebKit implementation of webRTC.
  334. // - 'plugin': Using the plugin implementation.
  335. webrtcDetectedType = null;
  336. // Detected webrtc datachannel support. Types are:
  337. // - 'SCTP': SCTP datachannel support.
  338. // - 'RTP': RTP datachannel support.
  339. webrtcDetectedDCSupport = null;
  340. // Set the settings for creating DataChannels, MediaStream for
  341. // Cross-browser compability.
  342. // - This is only for SCTP based support browsers.
  343. // the 'urls' attribute.
  344. checkMediaDataChannelSettings =
  345. function (peerBrowserAgent, peerBrowserVersion, callback, constraints) {
  346. if (typeof callback !== 'function') {
  347. return;
  348. }
  349. var beOfferer = true;
  350. var isLocalFirefox = webrtcDetectedBrowser === 'firefox';
  351. // Nightly version does not require MozDontOfferDataChannel for interop
  352. var isLocalFirefoxInterop = webrtcDetectedType === 'moz' && webrtcDetectedVersion > 30;
  353. var isPeerFirefox = peerBrowserAgent === 'firefox';
  354. var isPeerFirefoxInterop = peerBrowserAgent === 'firefox' &&
  355. ((peerBrowserVersion) ? (peerBrowserVersion > 30) : false);
  356. // Resends an updated version of constraints for MozDataChannel to work
  357. // If other userAgent is firefox and user is firefox, remove MozDataChannel
  358. if ((isLocalFirefox && isPeerFirefox) || (isLocalFirefoxInterop)) {
  359. try {
  360. delete constraints.mandatory.MozDontOfferDataChannel;
  361. } catch (error) {
  362. console.error('Failed deleting MozDontOfferDataChannel');
  363. console.error(error);
  364. }
  365. } else if ((isLocalFirefox && !isPeerFirefox)) {
  366. constraints.mandatory.MozDontOfferDataChannel = true;
  367. }
  368. if (!isLocalFirefox) {
  369. // temporary measure to remove Moz* constraints in non Firefox browsers
  370. for (var prop in constraints.mandatory) {
  371. if (constraints.mandatory.hasOwnProperty(prop)) {
  372. if (prop.indexOf('Moz') !== -1) {
  373. delete constraints.mandatory[prop];
  374. }
  375. }
  376. }
  377. }
  378. // Firefox (not interopable) cannot offer DataChannel as it will cause problems to the
  379. // interopability of the media stream
  380. if (isLocalFirefox && !isPeerFirefox && !isLocalFirefoxInterop) {
  381. beOfferer = false;
  382. }
  383. callback(beOfferer, constraints);
  384. };
  385. // Handles the differences for all browsers ice connection state output.
  386. // - Tested outcomes are:
  387. // - Chrome (offerer) : 'checking' > 'completed' > 'completed'
  388. // - Chrome (answerer) : 'checking' > 'connected'
  389. // - Firefox (offerer) : 'checking' > 'connected'
  390. // - Firefox (answerer): 'checking' > 'connected'
  391. checkIceConnectionState = function (peerId, iceConnectionState, callback) {
  392. if (typeof callback !== 'function') {
  393. console.warn('No callback specified in checkIceConnectionState. Aborted.');
  394. return;
  395. }
  396. peerId = (peerId) ? peerId : 'peer';
  397. if (!AdapterJS._iceConnectionFiredStates[peerId] ||
  398. iceConnectionState === AdapterJS._iceConnectionStates.disconnected ||
  399. iceConnectionState === AdapterJS._iceConnectionStates.failed ||
  400. iceConnectionState === AdapterJS._iceConnectionStates.closed) {
  401. AdapterJS._iceConnectionFiredStates[peerId] = [];
  402. }
  403. iceConnectionState = AdapterJS._iceConnectionStates[iceConnectionState];
  404. if (AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState) < 0) {
  405. AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState);
  406. if (iceConnectionState === AdapterJS._iceConnectionStates.connected) {
  407. setTimeout(function () {
  408. AdapterJS._iceConnectionFiredStates[peerId]
  409. .push(AdapterJS._iceConnectionStates.done);
  410. callback(AdapterJS._iceConnectionStates.done);
  411. }, 1000);
  412. }
  413. callback(iceConnectionState);
  414. }
  415. return;
  416. };
  417. // Firefox:
  418. // - Creates iceServer from the url for Firefox.
  419. // - Create iceServer with stun url.
  420. // - Create iceServer with turn url.
  421. // - Ignore the transport parameter from TURN url for FF version <=27.
  422. // - Return null for createIceServer if transport=tcp.
  423. // - FF 27 and above supports transport parameters in TURN url,
  424. // - So passing in the full url to create iceServer.
  425. // Chrome:
  426. // - Creates iceServer from the url for Chrome M33 and earlier.
  427. // - Create iceServer with stun url.
  428. // - Chrome M28 & above uses below TURN format.
  429. // Plugin:
  430. // - Creates Ice Server for Plugin Browsers
  431. // - If Stun - Create iceServer with stun url.
  432. // - Else - Create iceServer with turn url
  433. // - This is a WebRTC Function
  434. createIceServer = null;
  435. // Firefox:
  436. // - Creates IceServers for Firefox
  437. // - Use .url for FireFox.
  438. // - Multiple Urls support
  439. // Chrome:
  440. // - Creates iceServers from the urls for Chrome M34 and above.
  441. // - .urls is supported since Chrome M34.
  442. // - Multiple Urls support
  443. // Plugin:
  444. // - Creates Ice Servers for Plugin Browsers
  445. // - Multiple Urls support
  446. // - This is a WebRTC Function
  447. createIceServers = null;
  448. //------------------------------------------------------------
  449. //The RTCPeerConnection object.
  450. RTCPeerConnection = null;
  451. // Creates RTCSessionDescription object for Plugin Browsers
  452. RTCSessionDescription = (typeof RTCSessionDescription === 'function') ?
  453. RTCSessionDescription : null;
  454. // Creates RTCIceCandidate object for Plugin Browsers
  455. RTCIceCandidate = (typeof RTCIceCandidate === 'function') ?
  456. RTCIceCandidate : null;
  457. // Get UserMedia (only difference is the prefix).
  458. // Code from Adam Barth.
  459. getUserMedia = null;
  460. // Attach a media stream to an element.
  461. attachMediaStream = null;
  462. // Re-attach a media stream to an element.
  463. reattachMediaStream = null;
  464. // Detected browser agent name. Types are:
  465. // - 'firefox': Firefox browser.
  466. // - 'chrome': Chrome browser.
  467. // - 'opera': Opera browser.
  468. // - 'safari': Safari browser.
  469. // - 'IE' - Internet Explorer browser.
  470. webrtcDetectedBrowser = null;
  471. // Detected browser version.
  472. webrtcDetectedVersion = null;
  473. // Check for browser types and react accordingly
  474. if (navigator.mozGetUserMedia) {
  475. webrtcDetectedBrowser = 'firefox';
  476. webrtcDetectedVersion = parseInt(navigator
  477. .userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
  478. webrtcDetectedType = 'moz';
  479. webrtcDetectedDCSupport = 'SCTP';
  480. RTCPeerConnection = function (pcConfig, pcConstraints) {
  481. AdapterJS.maybeFixConfiguration(pcConfig);
  482. return new mozRTCPeerConnection(pcConfig, pcConstraints);
  483. };
  484. // The RTCSessionDescription object.
  485. RTCSessionDescription = mozRTCSessionDescription;
  486. window.RTCSessionDescription = RTCSessionDescription;
  487. // The RTCIceCandidate object.
  488. RTCIceCandidate = mozRTCIceCandidate;
  489. window.RTCIceCandidate = RTCIceCandidate;
  490. window.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
  491. navigator.getUserMedia = window.getUserMedia;
  492. // Shim for MediaStreamTrack.getSources.
  493. MediaStreamTrack.getSources = function(successCb) {
  494. setTimeout(function() {
  495. var infos = [
  496. { kind: 'audio', id: 'default', label:'', facing:'' },
  497. { kind: 'video', id: 'default', label:'', facing:'' }
  498. ];
  499. successCb(infos);
  500. }, 0);
  501. };
  502. createIceServer = function (url, username, password) {
  503. var iceServer = null;
  504. var url_parts = url.split(':');
  505. if (url_parts[0].indexOf('stun') === 0) {
  506. iceServer = { url : url };
  507. } else if (url_parts[0].indexOf('turn') === 0) {
  508. if (webrtcDetectedVersion < 27) {
  509. var turn_url_parts = url.split('?');
  510. if (turn_url_parts.length === 1 ||
  511. turn_url_parts[1].indexOf('transport=udp') === 0) {
  512. iceServer = {
  513. url : turn_url_parts[0],
  514. credential : password,
  515. username : username
  516. };
  517. }
  518. } else {
  519. iceServer = {
  520. url : url,
  521. credential : password,
  522. username : username
  523. };
  524. }
  525. }
  526. return iceServer;
  527. };
  528. createIceServers = function (urls, username, password) {
  529. var iceServers = [];
  530. for (i = 0; i < urls.length; i++) {
  531. var iceServer = createIceServer(urls[i], username, password);
  532. if (iceServer !== null) {
  533. iceServers.push(iceServer);
  534. }
  535. }
  536. return iceServers;
  537. };
  538. attachMediaStream = function (element, stream) {
  539. element.mozSrcObject = stream;
  540. if (stream !== null)
  541. element.play();
  542. return element;
  543. };
  544. reattachMediaStream = function (to, from) {
  545. to.mozSrcObject = from.mozSrcObject;
  546. to.play();
  547. return to;
  548. };
  549. MediaStreamTrack.getSources = MediaStreamTrack.getSources || function (callback) {
  550. if (!callback) {
  551. throw new TypeError('Failed to execute \'getSources\' on \'MediaStreamTrack\'' +
  552. ': 1 argument required, but only 0 present.');
  553. }
  554. return callback([]);
  555. };
  556. // Fake get{Video,Audio}Tracks
  557. if (!MediaStream.prototype.getVideoTracks) {
  558. MediaStream.prototype.getVideoTracks = function () {
  559. return [];
  560. };
  561. }
  562. if (!MediaStream.prototype.getAudioTracks) {
  563. MediaStream.prototype.getAudioTracks = function () {
  564. return [];
  565. };
  566. }
  567. AdapterJS.maybeThroughWebRTCReady();
  568. } else if (navigator.webkitGetUserMedia) {
  569. webrtcDetectedBrowser = 'chrome';
  570. webrtcDetectedType = 'webkit';
  571. webrtcDetectedVersion = parseInt(navigator
  572. .userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
  573. // check if browser is opera 20+
  574. var checkIfOpera = navigator.userAgent.match(/\bOPR\/(\d+)/);
  575. if (checkIfOpera !== null) {
  576. webrtcDetectedBrowser = 'opera';
  577. webrtcDetectedVersion = parseInt(checkIfOpera[1], 10);
  578. }
  579. // check browser datachannel support
  580. if ((webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion >= 31) ||
  581. (webrtcDetectedBrowser === 'opera' && webrtcDetectedVersion >= 20)) {
  582. webrtcDetectedDCSupport = 'SCTP';
  583. } else if (webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion < 30 &&
  584. webrtcDetectedVersion > 24) {
  585. webrtcDetectedDCSupport = 'RTP';
  586. } else {
  587. webrtcDetectedDCSupport = '';
  588. }
  589. createIceServer = function (url, username, password) {
  590. var iceServer = null;
  591. var url_parts = url.split(':');
  592. if (url_parts[0].indexOf('stun') === 0) {
  593. iceServer = { 'url' : url };
  594. } else if (url_parts[0].indexOf('turn') === 0) {
  595. iceServer = {
  596. 'url' : url,
  597. 'credential' : password,
  598. 'username' : username
  599. };
  600. }
  601. return iceServer;
  602. };
  603. createIceServers = function (urls, username, password) {
  604. var iceServers = [];
  605. if (webrtcDetectedVersion >= 34) {
  606. iceServers = {
  607. 'urls' : urls,
  608. 'credential' : password,
  609. 'username' : username
  610. };
  611. } else {
  612. for (i = 0; i < urls.length; i++) {
  613. var iceServer = createIceServer(urls[i], username, password);
  614. if (iceServer !== null) {
  615. iceServers.push(iceServer);
  616. }
  617. }
  618. }
  619. return iceServers;
  620. };
  621. RTCPeerConnection = function (pcConfig, pcConstraints) {
  622. if (webrtcDetectedVersion < 34) {
  623. AdapterJS.maybeFixConfiguration(pcConfig);
  624. }
  625. return new webkitRTCPeerConnection(pcConfig, pcConstraints);
  626. };
  627. window.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
  628. navigator.getUserMedia = window.getUserMedia;
  629. attachMediaStream = function (element, stream) {
  630. if (typeof element.srcObject !== 'undefined') {
  631. element.srcObject = stream;
  632. } else if (typeof element.mozSrcObject !== 'undefined') {
  633. element.mozSrcObject = stream;
  634. } else if (typeof element.src !== 'undefined') {
  635. element.src = (stream === null ? '' : URL.createObjectURL(stream));
  636. } else {
  637. console.log('Error attaching stream to element.');
  638. }
  639. return element;
  640. };
  641. reattachMediaStream = function (to, from) {
  642. to.src = from.src;
  643. return to;
  644. };
  645. AdapterJS.maybeThroughWebRTCReady();
  646. } else { // TRY TO USE PLUGIN
  647. // IE 9 is not offering an implementation of console.log until you open a console
  648. if (typeof console !== 'object' || typeof console.log !== 'function') {
  649. /* jshint -W020 */
  650. console = {} || console;
  651. // Implemented based on console specs from MDN
  652. // You may override these functions
  653. console.log = function (arg) {};
  654. console.info = function (arg) {};
  655. console.error = function (arg) {};
  656. console.dir = function (arg) {};
  657. console.exception = function (arg) {};
  658. console.trace = function (arg) {};
  659. console.warn = function (arg) {};
  660. console.count = function (arg) {};
  661. console.debug = function (arg) {};
  662. console.count = function (arg) {};
  663. console.time = function (arg) {};
  664. console.timeEnd = function (arg) {};
  665. console.group = function (arg) {};
  666. console.groupCollapsed = function (arg) {};
  667. console.groupEnd = function (arg) {};
  668. /* jshint +W020 */
  669. }
  670. webrtcDetectedType = 'plugin';
  671. webrtcDetectedDCSupport = 'plugin';
  672. AdapterJS.parseWebrtcDetectedBrowser();
  673. isIE = webrtcDetectedBrowser === 'IE';
  674. /* jshint -W035 */
  675. AdapterJS.WebRTCPlugin.WaitForPluginReady = function() {
  676. while (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  677. /* empty because it needs to prevent the function from running. */
  678. }
  679. };
  680. /* jshint +W035 */
  681. AdapterJS.WebRTCPlugin.callWhenPluginReady = function (callback) {
  682. if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  683. // Call immediately if possible
  684. // Once the plugin is set, the code will always take this path
  685. callback();
  686. } else {
  687. // otherwise start a 100ms interval
  688. var checkPluginReadyState = setInterval(function () {
  689. if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
  690. clearInterval(checkPluginReadyState);
  691. callback();
  692. }
  693. }, 100);
  694. }
  695. };
  696. AdapterJS.WebRTCPlugin.setLogLevel = function(logLevel) {
  697. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  698. AdapterJS.WebRTCPlugin.plugin.setLogLevel(logLevel);
  699. });
  700. };
  701. AdapterJS.WebRTCPlugin.injectPlugin = function () {
  702. // only inject once the page is ready
  703. if (document.readyState !== 'complete') {
  704. return;
  705. }
  706. // Prevent multiple injections
  707. if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) {
  708. return;
  709. }
  710. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING;
  711. if (webrtcDetectedBrowser === 'IE' && webrtcDetectedVersion <= 10) {
  712. var frag = document.createDocumentFragment();
  713. AdapterJS.WebRTCPlugin.plugin = document.createElement('div');
  714. AdapterJS.WebRTCPlugin.plugin.innerHTML = '<object id="' +
  715. AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '" type="' +
  716. AdapterJS.WebRTCPlugin.pluginInfo.type + '" ' + 'width="1" height="1">' +
  717. '<param name="pluginId" value="' +
  718. AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '" /> ' +
  719. '<param name="windowless" value="false" /> ' +
  720. '<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
  721. '<param name="onload" value="' + AdapterJS.WebRTCPlugin.pluginInfo.onload +
  722. '" />' +
  723. // uncomment to be able to use virtual cams
  724. (AdapterJS.options.getAllCams ? '<param name="forceGetAllCams" value="True" />':'') +
  725. '</object>';
  726. while (AdapterJS.WebRTCPlugin.plugin.firstChild) {
  727. frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);
  728. }
  729. document.body.appendChild(frag);
  730. // Need to re-fetch the plugin
  731. AdapterJS.WebRTCPlugin.plugin =
  732. document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId);
  733. } else {
  734. // Load Plugin
  735. AdapterJS.WebRTCPlugin.plugin = document.createElement('object');
  736. AdapterJS.WebRTCPlugin.plugin.id =
  737. AdapterJS.WebRTCPlugin.pluginInfo.pluginId;
  738. // IE will only start the plugin if it's ACTUALLY visible
  739. if (isIE) {
  740. AdapterJS.WebRTCPlugin.plugin.width = '1px';
  741. AdapterJS.WebRTCPlugin.plugin.height = '1px';
  742. } else { // The size of the plugin on Safari should be 0x0px
  743. // so that the autorisation prompt is at the top
  744. AdapterJS.WebRTCPlugin.plugin.width = '0px';
  745. AdapterJS.WebRTCPlugin.plugin.height = '0px';
  746. }
  747. AdapterJS.WebRTCPlugin.plugin.type = AdapterJS.WebRTCPlugin.pluginInfo.type;
  748. AdapterJS.WebRTCPlugin.plugin.innerHTML = '<param name="onload" value="' +
  749. AdapterJS.WebRTCPlugin.pluginInfo.onload + '">' +
  750. '<param name="pluginId" value="' +
  751. AdapterJS.WebRTCPlugin.pluginInfo.pluginId + '">' +
  752. '<param name="windowless" value="false" /> ' +
  753. (AdapterJS.options.getAllCams ? '<param name="forceGetAllCams" value="True" />':'') +
  754. '<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '">';
  755. document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);
  756. }
  757. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED;
  758. };
  759. AdapterJS.WebRTCPlugin.isPluginInstalled =
  760. function (comName, plugName, installedCb, notInstalledCb) {
  761. if (!isIE) {
  762. var pluginArray = navigator.plugins;
  763. for (var i = 0; i < pluginArray.length; i++) {
  764. if (pluginArray[i].name.indexOf(plugName) >= 0) {
  765. installedCb();
  766. return;
  767. }
  768. }
  769. notInstalledCb();
  770. } else {
  771. try {
  772. var axo = new ActiveXObject(comName + '.' + plugName);
  773. } catch (e) {
  774. notInstalledCb();
  775. return;
  776. }
  777. installedCb();
  778. }
  779. };
  780. AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
  781. AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING;
  782. AdapterJS.isDefined = function (variable) {
  783. return variable !== null && variable !== undefined;
  784. };
  785. createIceServer = function (url, username, password) {
  786. var iceServer = null;
  787. var url_parts = url.split(':');
  788. if (url_parts[0].indexOf('stun') === 0) {
  789. iceServer = {
  790. 'url' : url,
  791. 'hasCredentials' : false
  792. };
  793. } else if (url_parts[0].indexOf('turn') === 0) {
  794. iceServer = {
  795. 'url' : url,
  796. 'hasCredentials' : true,
  797. 'credential' : password,
  798. 'username' : username
  799. };
  800. }
  801. return iceServer;
  802. };
  803. createIceServers = function (urls, username, password) {
  804. var iceServers = [];
  805. for (var i = 0; i < urls.length; ++i) {
  806. iceServers.push(createIceServer(urls[i], username, password));
  807. }
  808. return iceServers;
  809. };
  810. RTCSessionDescription = function (info) {
  811. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  812. return AdapterJS.WebRTCPlugin.plugin.
  813. ConstructSessionDescription(info.type, info.sdp);
  814. };
  815. RTCPeerConnection = function (servers, constraints) {
  816. var iceServers = null;
  817. if (servers) {
  818. iceServers = servers.iceServers;
  819. for (var i = 0; i < iceServers.length; i++) {
  820. if (iceServers[i].urls && !iceServers[i].url) {
  821. iceServers[i].url = iceServers[i].urls;
  822. }
  823. iceServers[i].hasCredentials = AdapterJS.
  824. isDefined(iceServers[i].username) &&
  825. AdapterJS.isDefined(iceServers[i].credential);
  826. }
  827. }
  828. var mandatory = (constraints && constraints.mandatory) ?
  829. constraints.mandatory : null;
  830. var optional = (constraints && constraints.optional) ?
  831. constraints.optional : null;
  832. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  833. return AdapterJS.WebRTCPlugin.plugin.
  834. PeerConnection(AdapterJS.WebRTCPlugin.pageId,
  835. iceServers, mandatory, optional);
  836. };
  837. MediaStreamTrack = {};
  838. MediaStreamTrack.getSources = function (callback) {
  839. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  840. AdapterJS.WebRTCPlugin.plugin.GetSources(callback);
  841. });
  842. };
  843. window.getUserMedia = function (constraints, successCallback, failureCallback) {
  844. constraints.audio = constraints.audio || false;
  845. constraints.video = constraints.video || false;
  846. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  847. AdapterJS.WebRTCPlugin.plugin.
  848. getUserMedia(constraints, successCallback, failureCallback);
  849. });
  850. };
  851. window.navigator.getUserMedia = window.getUserMedia;
  852. attachMediaStream = function (element, stream) {
  853. if (!element || !element.parentNode) {
  854. return;
  855. }
  856. var streamId
  857. if (stream === null) {
  858. streamId = '';
  859. }
  860. else {
  861. stream.enableSoundTracks(true);
  862. streamId = stream.id;
  863. }
  864. if (element.nodeName.toLowerCase() !== 'audio') {
  865. var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id;
  866. if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) {
  867. var frag = document.createDocumentFragment();
  868. var temp = document.createElement('div');
  869. var classHTML = '';
  870. if (element.className) {
  871. classHTML = 'class="' + element.className + '" ';
  872. } else if (element.attributes && element.attributes['class']) {
  873. classHTML = 'class="' + element.attributes['class'].value + '" ';
  874. }
  875. temp.innerHTML = '<object id="' + elementId + '" ' + classHTML +
  876. 'type="' + AdapterJS.WebRTCPlugin.pluginInfo.type + '">' +
  877. '<param name="pluginId" value="' + elementId + '" /> ' +
  878. '<param name="pageId" value="' + AdapterJS.WebRTCPlugin.pageId + '" /> ' +
  879. '<param name="windowless" value="true" /> ' +
  880. '<param name="streamId" value="' + streamId + '" /> ' +
  881. '</object>';
  882. while (temp.firstChild) {
  883. frag.appendChild(temp.firstChild);
  884. }
  885. var height = '';
  886. var width = '';
  887. if (element.getBoundingClientRect) {
  888. var rectObject = element.getBoundingClientRect();
  889. width = rectObject.width + 'px';
  890. height = rectObject.height + 'px';
  891. }
  892. else if (element.width) {
  893. width = element.width;
  894. height = element.height;
  895. } else {
  896. // TODO: What scenario could bring us here?
  897. }
  898. element.parentNode.insertBefore(frag, element);
  899. frag = document.getElementById(elementId);
  900. frag.width = width;
  901. frag.height = height;
  902. element.parentNode.removeChild(element);
  903. } else {
  904. var children = element.children;
  905. for (var i = 0; i !== children.length; ++i) {
  906. if (children[i].name === 'streamId') {
  907. children[i].value = streamId;
  908. break;
  909. }
  910. }
  911. element.setStreamId(streamId);
  912. }
  913. var newElement = document.getElementById(elementId);
  914. newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
  915. if (isIE) { // on IE the event needs to be plugged manually
  916. newElement.attachEvent('onplaying', newElement.onplaying);
  917. newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
  918. newElement._TemOnClick = function (id) {
  919. var arg = {
  920. srcElement : document.getElementById(id)
  921. };
  922. newElement.onclick(arg);
  923. };
  924. }
  925. return newElement;
  926. } else {
  927. return element;
  928. }
  929. };
  930. reattachMediaStream = function (to, from) {
  931. var stream = null;
  932. var children = from.children;
  933. for (var i = 0; i !== children.length; ++i) {
  934. if (children[i].name === 'streamId') {
  935. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  936. stream = AdapterJS.WebRTCPlugin.plugin
  937. .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, children[i].value);
  938. break;
  939. }
  940. }
  941. if (stream !== null) {
  942. return attachMediaStream(to, stream);
  943. } else {
  944. console.log('Could not find the stream associated with this element');
  945. }
  946. };
  947. RTCIceCandidate = function (candidate) {
  948. if (!candidate.sdpMid) {
  949. candidate.sdpMid = '';
  950. }
  951. AdapterJS.WebRTCPlugin.WaitForPluginReady();
  952. return AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(
  953. candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate
  954. );
  955. };
  956. // inject plugin
  957. AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.injectPlugin);
  958. AdapterJS.WebRTCPlugin.injectPlugin();
  959. };
  960. // This function will be called if the plugin is needed (browser different
  961. // from Chrome or Firefox), but the plugin is not installed.
  962. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb ||
  963. function() {
  964. AdapterJS.addEvent(document,
  965. 'readystatechange',
  966. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv);
  967. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv();
  968. };
  969. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv = function () {
  970. if (AdapterJS.options.hidePluginInstallPrompt) {
  971. return;
  972. }
  973. var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;
  974. if(downloadLink) { // if download link
  975. var popupString;
  976. if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { // is portal link
  977. popupString = 'This website requires you to install the ' +
  978. ' <a href="' + AdapterJS.WebRTCPlugin.pluginInfo.portalLink +
  979. '" target="_blank">' + AdapterJS.WebRTCPlugin.pluginInfo.companyName +
  980. ' WebRTC Plugin</a>' +
  981. ' to work on this browser.';
  982. } else { // no portal link, just print a generic explanation
  983. popupString = AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION;
  984. }
  985. AdapterJS.renderNotificationBar(popupString, AdapterJS.TEXT.PLUGIN.BUTTON, downloadLink);
  986. } else { // no download link, just print a generic explanation
  987. AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED);
  988. }
  989. };
  990. // Try to detect the plugin and act accordingly
  991. AdapterJS.WebRTCPlugin.isPluginInstalled(
  992. AdapterJS.WebRTCPlugin.pluginInfo.prefix,
  993. AdapterJS.WebRTCPlugin.pluginInfo.plugName,
  994. AdapterJS.WebRTCPlugin.defineWebRTCInterface,
  995. AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
  996. }
  997. (function () {
  998. 'use strict';
  999. var baseGetUserMedia = null;
  1000. AdapterJS.TEXT.EXTENSION = {
  1001. REQUIRE_INSTALLATION_FF: 'To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.',
  1002. REQUIRE_INSTALLATION_CHROME: 'To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.',
  1003. REQUIRE_REFRESH: 'Please refresh this page after the Skylink WebRTC tools extension has been installed.',
  1004. BUTTON_FF: 'Install Now',
  1005. BUTTON_CHROME: 'Go to Chrome Web Store'
  1006. };
  1007. var clone = function(obj) {
  1008. if (null == obj || "object" != typeof obj) return obj;
  1009. var copy = obj.constructor();
  1010. for (var attr in obj) {
  1011. if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
  1012. }
  1013. return copy;
  1014. };
  1015. if (window.navigator.mozGetUserMedia) {
  1016. baseGetUserMedia = window.navigator.getUserMedia;
  1017. navigator.getUserMedia = function (constraints, successCb, failureCb) {
  1018. if (constraints && constraints.video && !!constraints.video.mediaSource) {
  1019. // intercepting screensharing requests
  1020. if (constraints.video.mediaSource !== 'screen' && constraints.video.mediaSource !== 'window') {
  1021. throw new Error('Only "screen" and "window" option is available as mediaSource');
  1022. }
  1023. var updatedConstraints = clone(constraints);
  1024. //constraints.video.mediaSource = constraints.video.mediaSource;
  1025. updatedConstraints.video.mozMediaSource = updatedConstraints.video.mediaSource;
  1026. // so generally, it requires for document.readyState to be completed before the getUserMedia could be invoked.
  1027. // strange but this works anyway
  1028. var checkIfReady = setInterval(function () {
  1029. if (document.readyState === 'complete') {
  1030. clearInterval(checkIfReady);
  1031. baseGetUserMedia(updatedConstraints, successCb, function (error) {
  1032. if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') {
  1033. AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,
  1034. AdapterJS.TEXT.EXTENSION.BUTTON_FF,
  1035. 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true);
  1036. //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname;
  1037. } else {
  1038. failureCb(error);
  1039. }
  1040. });
  1041. }
  1042. }, 1);
  1043. } else { // regular GetUserMediaRequest
  1044. baseGetUserMedia(constraints, successCb, failureCb);
  1045. }
  1046. };
  1047. getUserMedia = navigator.getUserMedia;
  1048. } else if (window.navigator.webkitGetUserMedia) {
  1049. baseGetUserMedia = window.navigator.getUserMedia;
  1050. navigator.getUserMedia = function (constraints, successCb, failureCb) {
  1051. if (constraints && constraints.video && !!constraints.video.mediaSource) {
  1052. if (window.webrtcDetectedBrowser !== 'chrome') {
  1053. throw new Error('Current browser does not support screensharing');
  1054. }
  1055. // would be fine since no methods
  1056. var updatedConstraints = clone(constraints);
  1057. var chromeCallback = function(error, sourceId) {
  1058. if(!error) {
  1059. updatedConstraints.video.mandatory = updatedConstraints.video.mandatory || {};
  1060. updatedConstraints.video.mandatory.chromeMediaSource = 'desktop';
  1061. updatedConstraints.video.mandatory.maxWidth = window.screen.width > 1920 ? window.screen.width : 1920;
  1062. updatedConstraints.video.mandatory.maxHeight = window.screen.height > 1080 ? window.screen.height : 1080;
  1063. if (sourceId) {
  1064. updatedConstraints.video.mandatory.chromeMediaSourceId = sourceId;
  1065. }
  1066. delete updatedConstraints.video.mediaSource;
  1067. baseGetUserMedia(updatedConstraints, successCb, failureCb);
  1068. } else {
  1069. if (error === 'permission-denied') {
  1070. throw new Error('Permission denied for screen retrieval');
  1071. } else {
  1072. throw new Error('Failed retrieving selected screen');
  1073. }
  1074. }
  1075. };
  1076. var onIFrameCallback = function (event) {
  1077. if (!event.data) {
  1078. return;
  1079. }
  1080. if (event.data.chromeMediaSourceId) {
  1081. if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
  1082. chromeCallback('permission-denied');
  1083. } else {
  1084. chromeCallback(null, event.data.chromeMediaSourceId);
  1085. }
  1086. }
  1087. if (event.data.chromeExtensionStatus) {
  1088. if (event.data.chromeExtensionStatus === 'not-installed') {
  1089. AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,
  1090. AdapterJS.TEXT.EXTENSION.BUTTON_CHROME,
  1091. event.data.data, true, true);
  1092. } else {
  1093. chromeCallback(event.data.chromeExtensionStatus, null);
  1094. }
  1095. }
  1096. // this event listener is no more needed
  1097. window.removeEventListener('message', onIFrameCallback);
  1098. };
  1099. window.addEventListener('message', onIFrameCallback);
  1100. postFrameMessage({
  1101. captureSourceId: true
  1102. });
  1103. } else {
  1104. baseGetUserMedia(constraints, successCb, failureCb);
  1105. }
  1106. };
  1107. getUserMedia = navigator.getUserMedia;
  1108. } else {
  1109. baseGetUserMedia = window.navigator.getUserMedia;
  1110. navigator.getUserMedia = function (constraints, successCb, failureCb) {
  1111. if (constraints && constraints.video && !!constraints.video.mediaSource) {
  1112. // would be fine since no methods
  1113. var updatedConstraints = clone(constraints);
  1114. // wait for plugin to be ready
  1115. AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
  1116. // check if screensharing feature is available
  1117. if (!!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature &&
  1118. !!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) {
  1119. // set the constraints
  1120. updatedConstraints.video.optional = updatedConstraints.video.optional || [];
  1121. updatedConstraints.video.optional.push({
  1122. sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey || 'Screensharing'
  1123. });
  1124. delete updatedConstraints.video.mediaSource;
  1125. } else {
  1126. throw new Error('Your WebRTC plugin does not support screensharing');
  1127. }
  1128. baseGetUserMedia(updatedConstraints, successCb, failureCb);
  1129. });
  1130. } else {
  1131. baseGetUserMedia(constraints, successCb, failureCb);
  1132. }
  1133. };
  1134. getUserMedia = window.navigator.getUserMedia;
  1135. }
  1136. if (window.webrtcDetectedBrowser === 'chrome') {
  1137. var iframe = document.createElement('iframe');
  1138. iframe.onload = function() {
  1139. iframe.isLoaded = true;
  1140. };
  1141. iframe.src = 'https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html';
  1142. //'https://temasys-cdn.s3.amazonaws.com/skylink/extensions/detection-script-dev/detectRTC.html';
  1143. iframe.style.display = 'none';
  1144. (document.body || document.documentElement).appendChild(iframe);
  1145. var postFrameMessage = function (object) {
  1146. object = object || {};
  1147. if (!iframe.isLoaded) {
  1148. setTimeout(function () {
  1149. iframe.contentWindow.postMessage(object, '*');
  1150. }, 100);
  1151. return;
  1152. }
  1153. iframe.contentWindow.postMessage(object, '*');
  1154. };
  1155. }
  1156. })();