PageRenderTime 61ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/files/mediaelement/2.20.0/mediaelement-and-player.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 1699 lines | 1598 code | 36 blank | 65 comment | 21 complexity | 1c025e2e28da8996a885f05506deb7a0 MD5 | raw file
  1. /*!
  2. *
  3. * MediaElement.js
  4. * HTML5 <video> and <audio> shim and player
  5. * http://mediaelementjs.com/
  6. *
  7. * Creates a JavaScript object that mimics HTML5 MediaElement API
  8. * for browsers that don't understand HTML5 or can't play the provided codec
  9. * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
  10. *
  11. * Copyright 2010-2014, John Dyer (http://j.hn)
  12. * License: MIT
  13. *
  14. */
  15. // Namespace
  16. var mejs = mejs || {};
  17. // version number
  18. mejs.version = '2.20.0';
  19. // player number (for missing, same id attr)
  20. mejs.meIndex = 0;
  21. // media types accepted by plugins
  22. mejs.plugins = {
  23. silverlight: [
  24. {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
  25. ],
  26. flash: [
  27. {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/dailymotion', 'video/x-dailymotion', 'application/x-mpegURL']}
  28. // 'video/youtube', 'video/x-youtube',
  29. // ,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
  30. ],
  31. youtube: [
  32. {version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']}
  33. ],
  34. vimeo: [
  35. {version: null, types: ['video/vimeo', 'video/x-vimeo']}
  36. ]
  37. };
  38. /*
  39. Utility methods
  40. */
  41. mejs.Utility = {
  42. encodeUrl: function(url) {
  43. return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
  44. },
  45. escapeHTML: function(s) {
  46. return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
  47. },
  48. absolutizeUrl: function(url) {
  49. var el = document.createElement('div');
  50. el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
  51. return el.firstChild.href;
  52. },
  53. getScriptPath: function(scriptNames) {
  54. var
  55. i = 0,
  56. j,
  57. codePath = '',
  58. testname = '',
  59. slashPos,
  60. filenamePos,
  61. scriptUrl,
  62. scriptPath,
  63. scriptFilename,
  64. scripts = document.getElementsByTagName('script'),
  65. il = scripts.length,
  66. jl = scriptNames.length;
  67. // go through all <script> tags
  68. for (; i < il; i++) {
  69. scriptUrl = scripts[i].src;
  70. slashPos = scriptUrl.lastIndexOf('/');
  71. if (slashPos > -1) {
  72. scriptFilename = scriptUrl.substring(slashPos + 1);
  73. scriptPath = scriptUrl.substring(0, slashPos + 1);
  74. } else {
  75. scriptFilename = scriptUrl;
  76. scriptPath = '';
  77. }
  78. // see if any <script> tags have a file name that matches the
  79. for (j = 0; j < jl; j++) {
  80. testname = scriptNames[j];
  81. filenamePos = scriptFilename.indexOf(testname);
  82. if (filenamePos > -1) {
  83. codePath = scriptPath;
  84. break;
  85. }
  86. }
  87. // if we found a path, then break and return it
  88. if (codePath !== '') {
  89. break;
  90. }
  91. }
  92. // send the best path back
  93. return codePath;
  94. },
  95. /*
  96. * Calculate the time format to use. We have a default format set in the
  97. * options but it can be imcomplete. We ajust it according to the media
  98. * duration.
  99. *
  100. * We support format like 'hh:mm:ss:ff'.
  101. */
  102. calculateTimeFormat: function(time, options, fps) {
  103. if (time < 0) {
  104. time = 0;
  105. }
  106. if(typeof fps == 'undefined') {
  107. fps = 25;
  108. }
  109. var format = options.timeFormat,
  110. firstChar = format[0],
  111. firstTwoPlaces = (format[1] == format[0]),
  112. separatorIndex = firstTwoPlaces? 2: 1,
  113. separator = ':',
  114. hours = Math.floor(time / 3600) % 24,
  115. minutes = Math.floor(time / 60) % 60,
  116. seconds = Math.floor(time % 60),
  117. frames = Math.floor(((time % 1)*fps).toFixed(3)),
  118. lis = [
  119. [frames, 'f'],
  120. [seconds, 's'],
  121. [minutes, 'm'],
  122. [hours, 'h']
  123. ];
  124. // Try to get the separator from the format
  125. if (format.length < separatorIndex) {
  126. separator = format[separatorIndex];
  127. }
  128. var required = false;
  129. for (var i=0, len=lis.length; i < len; i++) {
  130. if (format.indexOf(lis[i][1]) !== -1) {
  131. required=true;
  132. }
  133. else if (required) {
  134. var hasNextValue = false;
  135. for (var j=i; j < len; j++) {
  136. if (lis[j][0] > 0) {
  137. hasNextValue = true;
  138. break;
  139. }
  140. }
  141. if (! hasNextValue) {
  142. break;
  143. }
  144. if (!firstTwoPlaces) {
  145. format = firstChar + format;
  146. }
  147. format = lis[i][1] + separator + format;
  148. if (firstTwoPlaces) {
  149. format = lis[i][1] + format;
  150. }
  151. firstChar = lis[i][1];
  152. }
  153. }
  154. options.currentTimeFormat = format;
  155. },
  156. /*
  157. * Prefix the given number by zero if it is lower than 10.
  158. */
  159. twoDigitsString: function(n) {
  160. if (n < 10) {
  161. return '0' + n;
  162. }
  163. return String(n);
  164. },
  165. secondsToTimeCode: function(time, options) {
  166. if (time < 0) {
  167. time = 0;
  168. }
  169. // Maintain backward compatibility with method signature before v2.18.
  170. if (typeof options !== 'object') {
  171. var format = 'm:ss';
  172. format = arguments[1] ? 'hh:mm:ss' : format; // forceHours
  173. format = arguments[2] ? format + ':ff' : format; // showFrameCount
  174. options = {
  175. currentTimeFormat: format,
  176. framesPerSecond: arguments[3] || 25
  177. };
  178. }
  179. var fps = options.framesPerSecond;
  180. if(typeof fps === 'undefined') {
  181. fps = 25;
  182. }
  183. var format = options.currentTimeFormat,
  184. hours = Math.floor(time / 3600) % 24,
  185. minutes = Math.floor(time / 60) % 60,
  186. seconds = Math.floor(time % 60),
  187. frames = Math.floor(((time % 1)*fps).toFixed(3));
  188. lis = [
  189. [frames, 'f'],
  190. [seconds, 's'],
  191. [minutes, 'm'],
  192. [hours, 'h']
  193. ];
  194. var res = format;
  195. for (i=0,len=lis.length; i < len; i++) {
  196. res = res.replace(lis[i][1]+lis[i][1], this.twoDigitsString(lis[i][0]));
  197. res = res.replace(lis[i][1], lis[i][0]);
  198. }
  199. return res;
  200. },
  201. timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
  202. if (typeof showFrameCount == 'undefined') {
  203. showFrameCount=false;
  204. } else if(typeof fps == 'undefined') {
  205. fps = 25;
  206. }
  207. var tc_array = hh_mm_ss_ff.split(":"),
  208. tc_hh = parseInt(tc_array[0], 10),
  209. tc_mm = parseInt(tc_array[1], 10),
  210. tc_ss = parseInt(tc_array[2], 10),
  211. tc_ff = 0,
  212. tc_in_seconds = 0;
  213. if (showFrameCount) {
  214. tc_ff = parseInt(tc_array[3])/fps;
  215. }
  216. tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
  217. return tc_in_seconds;
  218. },
  219. convertSMPTEtoSeconds: function (SMPTE) {
  220. if (typeof SMPTE != 'string')
  221. return false;
  222. SMPTE = SMPTE.replace(',', '.');
  223. var secs = 0,
  224. decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0,
  225. multiplier = 1;
  226. SMPTE = SMPTE.split(':').reverse();
  227. for (var i = 0; i < SMPTE.length; i++) {
  228. multiplier = 1;
  229. if (i > 0) {
  230. multiplier = Math.pow(60, i);
  231. }
  232. secs += Number(SMPTE[i]) * multiplier;
  233. }
  234. return Number(secs.toFixed(decimalLen));
  235. },
  236. /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
  237. removeSwf: function(id) {
  238. var obj = document.getElementById(id);
  239. if (obj && /object|embed/i.test(obj.nodeName)) {
  240. if (mejs.MediaFeatures.isIE) {
  241. obj.style.display = "none";
  242. (function(){
  243. if (obj.readyState == 4) {
  244. mejs.Utility.removeObjectInIE(id);
  245. } else {
  246. setTimeout(arguments.callee, 10);
  247. }
  248. })();
  249. } else {
  250. obj.parentNode.removeChild(obj);
  251. }
  252. }
  253. },
  254. removeObjectInIE: function(id) {
  255. var obj = document.getElementById(id);
  256. if (obj) {
  257. for (var i in obj) {
  258. if (typeof obj[i] == "function") {
  259. obj[i] = null;
  260. }
  261. }
  262. obj.parentNode.removeChild(obj);
  263. }
  264. }
  265. };
  266. // Core detector, plugins are added below
  267. mejs.PluginDetector = {
  268. // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
  269. hasPluginVersion: function(plugin, v) {
  270. var pv = this.plugins[plugin];
  271. v[1] = v[1] || 0;
  272. v[2] = v[2] || 0;
  273. return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
  274. },
  275. // cached values
  276. nav: window.navigator,
  277. ua: window.navigator.userAgent.toLowerCase(),
  278. // stored version numbers
  279. plugins: [],
  280. // runs detectPlugin() and stores the version number
  281. addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
  282. this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
  283. },
  284. // get the version number from the mimetype (all but IE) or ActiveX (IE)
  285. detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
  286. var version = [0,0,0],
  287. description,
  288. i,
  289. ax;
  290. // Firefox, Webkit, Opera
  291. if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
  292. description = this.nav.plugins[pluginName].description;
  293. if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
  294. version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
  295. for (i=0; i<version.length; i++) {
  296. version[i] = parseInt(version[i].match(/\d+/), 10);
  297. }
  298. }
  299. // Internet Explorer / ActiveX
  300. } else if (typeof(window.ActiveXObject) != 'undefined') {
  301. try {
  302. ax = new ActiveXObject(activeX);
  303. if (ax) {
  304. version = axDetect(ax);
  305. }
  306. }
  307. catch (e) { }
  308. }
  309. return version;
  310. }
  311. };
  312. // Add Flash detection
  313. mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
  314. // adapted from SWFObject
  315. var version = [],
  316. d = ax.GetVariable("$version");
  317. if (d) {
  318. d = d.split(" ")[1].split(",");
  319. version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  320. }
  321. return version;
  322. });
  323. // Add Silverlight detection
  324. mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
  325. // Silverlight cannot report its version number to IE
  326. // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
  327. // adapted from http://www.silverlightversion.com/
  328. var v = [0,0,0,0],
  329. loopMatch = function(ax, v, i, n) {
  330. while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
  331. v[i]+=n;
  332. }
  333. v[i] -= n;
  334. };
  335. loopMatch(ax, v, 0, 1);
  336. loopMatch(ax, v, 1, 1);
  337. loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
  338. loopMatch(ax, v, 2, 1000);
  339. loopMatch(ax, v, 2, 100);
  340. loopMatch(ax, v, 2, 10);
  341. loopMatch(ax, v, 2, 1);
  342. loopMatch(ax, v, 3, 1);
  343. return v;
  344. });
  345. // add adobe acrobat
  346. /*
  347. PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
  348. var version = [],
  349. d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
  350. if (d) {
  351. version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  352. }
  353. return version;
  354. });
  355. */
  356. // necessary detection (fixes for <IE9)
  357. mejs.MediaFeatures = {
  358. init: function() {
  359. var
  360. t = this,
  361. d = document,
  362. nav = mejs.PluginDetector.nav,
  363. ua = mejs.PluginDetector.ua.toLowerCase(),
  364. i,
  365. v,
  366. html5Elements = ['source','track','audio','video'];
  367. // detect browsers (only the ones that have some kind of quirk we need to work around)
  368. t.isiPad = (ua.match(/ipad/i) !== null);
  369. t.isiPhone = (ua.match(/iphone/i) !== null);
  370. t.isiOS = t.isiPhone || t.isiPad;
  371. t.isAndroid = (ua.match(/android/i) !== null);
  372. t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
  373. t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null));
  374. t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
  375. t.isChrome = (ua.match(/chrome/gi) !== null);
  376. t.isChromium = (ua.match(/chromium/gi) !== null);
  377. t.isFirefox = (ua.match(/firefox/gi) !== null);
  378. t.isWebkit = (ua.match(/webkit/gi) !== null);
  379. t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE;
  380. t.isOpera = (ua.match(/opera/gi) !== null);
  381. t.hasTouch = ('ontouchstart' in window); // && window.ontouchstart != null); // this breaks iOS 7
  382. // Borrowed from `Modernizr.svgasimg`, sources:
  383. // - https://github.com/Modernizr/Modernizr/issues/687
  384. // - https://github.com/Modernizr/Modernizr/pull/1209/files
  385. t.svgAsImg = !!document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1');
  386. // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
  387. for (i=0; i<html5Elements.length; i++) {
  388. v = document.createElement(html5Elements[i]);
  389. }
  390. t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
  391. // Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer)
  392. try{
  393. v.canPlayType("video/mp4");
  394. }catch(e){
  395. t.supportsMediaTag = false;
  396. }
  397. t.supportsPointerEvents = (function() {
  398. // TAKEN FROM MODERNIZR
  399. var element = document.createElement('x'),
  400. documentElement = document.documentElement,
  401. getComputedStyle = window.getComputedStyle,
  402. supports;
  403. if(!('pointerEvents' in element.style)){
  404. return false;
  405. }
  406. element.style.pointerEvents = 'auto';
  407. element.style.pointerEvents = 'x';
  408. documentElement.appendChild(element);
  409. supports = getComputedStyle &&
  410. getComputedStyle(element, '').pointerEvents === 'auto';
  411. documentElement.removeChild(element);
  412. return !!supports;
  413. })();
  414. // Older versions of Firefox can't move plugins around without it resetting,
  415. t.hasFirefoxPluginMovingProblem = false;
  416. // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
  417. // iOS
  418. t.hasiOSFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
  419. // W3C
  420. t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined');
  421. // webkit/firefox/IE11+
  422. t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
  423. t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
  424. t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined');
  425. t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen);
  426. t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
  427. // Enabled?
  428. if (t.hasMozNativeFullScreen) {
  429. t.nativeFullScreenEnabled = document.mozFullScreenEnabled;
  430. } else if (t.hasMsNativeFullScreen) {
  431. t.nativeFullScreenEnabled = document.msFullscreenEnabled;
  432. }
  433. if (t.isChrome) {
  434. t.hasiOSFullScreen = false;
  435. }
  436. if (t.hasTrueNativeFullScreen) {
  437. t.fullScreenEventName = '';
  438. if (t.hasWebkitNativeFullScreen) {
  439. t.fullScreenEventName = 'webkitfullscreenchange';
  440. } else if (t.hasMozNativeFullScreen) {
  441. t.fullScreenEventName = 'mozfullscreenchange';
  442. } else if (t.hasMsNativeFullScreen) {
  443. t.fullScreenEventName = 'MSFullscreenChange';
  444. }
  445. t.isFullScreen = function() {
  446. if (t.hasMozNativeFullScreen) {
  447. return d.mozFullScreen;
  448. } else if (t.hasWebkitNativeFullScreen) {
  449. return d.webkitIsFullScreen;
  450. } else if (t.hasMsNativeFullScreen) {
  451. return d.msFullscreenElement !== null;
  452. }
  453. }
  454. t.requestFullScreen = function(el) {
  455. if (t.hasWebkitNativeFullScreen) {
  456. el.webkitRequestFullScreen();
  457. } else if (t.hasMozNativeFullScreen) {
  458. el.mozRequestFullScreen();
  459. } else if (t.hasMsNativeFullScreen) {
  460. el.msRequestFullscreen();
  461. }
  462. }
  463. t.cancelFullScreen = function() {
  464. if (t.hasWebkitNativeFullScreen) {
  465. document.webkitCancelFullScreen();
  466. } else if (t.hasMozNativeFullScreen) {
  467. document.mozCancelFullScreen();
  468. } else if (t.hasMsNativeFullScreen) {
  469. document.msExitFullscreen();
  470. }
  471. }
  472. }
  473. // OS X 10.5 can't do this even if it says it can :(
  474. if (t.hasiOSFullScreen && ua.match(/mac os x 10_5/i)) {
  475. t.hasNativeFullScreen = false;
  476. t.hasiOSFullScreen = false;
  477. }
  478. }
  479. };
  480. mejs.MediaFeatures.init();
  481. /*
  482. extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
  483. */
  484. mejs.HtmlMediaElement = {
  485. pluginType: 'native',
  486. isFullScreen: false,
  487. setCurrentTime: function (time) {
  488. this.currentTime = time;
  489. },
  490. setMuted: function (muted) {
  491. this.muted = muted;
  492. },
  493. setVolume: function (volume) {
  494. this.volume = volume;
  495. },
  496. // for parity with the plugin versions
  497. stop: function () {
  498. this.pause();
  499. },
  500. // This can be a url string
  501. // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
  502. setSrc: function (url) {
  503. // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
  504. var
  505. existingSources = this.getElementsByTagName('source');
  506. while (existingSources.length > 0){
  507. this.removeChild(existingSources[0]);
  508. }
  509. if (typeof url == 'string') {
  510. this.src = url;
  511. } else {
  512. var i, media;
  513. for (i=0; i<url.length; i++) {
  514. media = url[i];
  515. if (this.canPlayType(media.type)) {
  516. this.src = media.src;
  517. break;
  518. }
  519. }
  520. }
  521. },
  522. setVideoSize: function (width, height) {
  523. this.width = width;
  524. this.height = height;
  525. }
  526. };
  527. /*
  528. Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
  529. */
  530. mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
  531. this.id = pluginid;
  532. this.pluginType = pluginType;
  533. this.src = mediaUrl;
  534. this.events = {};
  535. this.attributes = {};
  536. };
  537. // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
  538. // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
  539. // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
  540. mejs.PluginMediaElement.prototype = {
  541. // special
  542. pluginElement: null,
  543. pluginType: '',
  544. isFullScreen: false,
  545. // not implemented :(
  546. playbackRate: -1,
  547. defaultPlaybackRate: -1,
  548. seekable: [],
  549. played: [],
  550. // HTML5 read-only properties
  551. paused: true,
  552. ended: false,
  553. seeking: false,
  554. duration: 0,
  555. error: null,
  556. tagName: '',
  557. // HTML5 get/set properties, but only set (updated by event handlers)
  558. muted: false,
  559. volume: 1,
  560. currentTime: 0,
  561. // HTML5 methods
  562. play: function () {
  563. if (this.pluginApi != null) {
  564. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  565. this.pluginApi.playVideo();
  566. } else {
  567. this.pluginApi.playMedia();
  568. }
  569. this.paused = false;
  570. }
  571. },
  572. load: function () {
  573. if (this.pluginApi != null) {
  574. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  575. } else {
  576. this.pluginApi.loadMedia();
  577. }
  578. this.paused = false;
  579. }
  580. },
  581. pause: function () {
  582. if (this.pluginApi != null) {
  583. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  584. this.pluginApi.pauseVideo();
  585. } else {
  586. this.pluginApi.pauseMedia();
  587. }
  588. this.paused = true;
  589. }
  590. },
  591. stop: function () {
  592. if (this.pluginApi != null) {
  593. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  594. this.pluginApi.stopVideo();
  595. } else {
  596. this.pluginApi.stopMedia();
  597. }
  598. this.paused = true;
  599. }
  600. },
  601. canPlayType: function(type) {
  602. var i,
  603. j,
  604. pluginInfo,
  605. pluginVersions = mejs.plugins[this.pluginType];
  606. for (i=0; i<pluginVersions.length; i++) {
  607. pluginInfo = pluginVersions[i];
  608. // test if user has the correct plugin version
  609. if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
  610. // test for plugin playback types
  611. for (j=0; j<pluginInfo.types.length; j++) {
  612. // find plugin that can play the type
  613. if (type == pluginInfo.types[j]) {
  614. return 'probably';
  615. }
  616. }
  617. }
  618. }
  619. return '';
  620. },
  621. positionFullscreenButton: function(x,y,visibleAndAbove) {
  622. if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
  623. this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove);
  624. }
  625. },
  626. hideFullscreenButton: function() {
  627. if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
  628. this.pluginApi.hideFullscreenButton();
  629. }
  630. },
  631. // custom methods since not all JavaScript implementations support get/set
  632. // This can be a url string
  633. // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
  634. setSrc: function (url) {
  635. if (typeof url == 'string') {
  636. this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
  637. this.src = mejs.Utility.absolutizeUrl(url);
  638. } else {
  639. var i, media;
  640. for (i=0; i<url.length; i++) {
  641. media = url[i];
  642. if (this.canPlayType(media.type)) {
  643. this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
  644. this.src = mejs.Utility.absolutizeUrl(media.src);
  645. break;
  646. }
  647. }
  648. }
  649. },
  650. setCurrentTime: function (time) {
  651. if (this.pluginApi != null) {
  652. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  653. this.pluginApi.seekTo(time);
  654. } else {
  655. this.pluginApi.setCurrentTime(time);
  656. }
  657. this.currentTime = time;
  658. }
  659. },
  660. setVolume: function (volume) {
  661. if (this.pluginApi != null) {
  662. // same on YouTube and MEjs
  663. if (this.pluginType == 'youtube') {
  664. this.pluginApi.setVolume(volume * 100);
  665. } else {
  666. this.pluginApi.setVolume(volume);
  667. }
  668. this.volume = volume;
  669. }
  670. },
  671. setMuted: function (muted) {
  672. if (this.pluginApi != null) {
  673. if (this.pluginType == 'youtube') {
  674. if (muted) {
  675. this.pluginApi.mute();
  676. } else {
  677. this.pluginApi.unMute();
  678. }
  679. this.muted = muted;
  680. this.dispatchEvent({type:'volumechange'});
  681. } else {
  682. this.pluginApi.setMuted(muted);
  683. }
  684. this.muted = muted;
  685. }
  686. },
  687. // additional non-HTML5 methods
  688. setVideoSize: function (width, height) {
  689. //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
  690. if (this.pluginElement && this.pluginElement.style) {
  691. this.pluginElement.style.width = width + 'px';
  692. this.pluginElement.style.height = height + 'px';
  693. }
  694. if (this.pluginApi != null && this.pluginApi.setVideoSize) {
  695. this.pluginApi.setVideoSize(width, height);
  696. }
  697. //}
  698. },
  699. setFullscreen: function (fullscreen) {
  700. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  701. this.pluginApi.setFullscreen(fullscreen);
  702. }
  703. },
  704. enterFullScreen: function() {
  705. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  706. this.setFullscreen(true);
  707. }
  708. },
  709. exitFullScreen: function() {
  710. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  711. this.setFullscreen(false);
  712. }
  713. },
  714. // start: fake events
  715. addEventListener: function (eventName, callback, bubble) {
  716. this.events[eventName] = this.events[eventName] || [];
  717. this.events[eventName].push(callback);
  718. },
  719. removeEventListener: function (eventName, callback) {
  720. if (!eventName) { this.events = {}; return true; }
  721. var callbacks = this.events[eventName];
  722. if (!callbacks) return true;
  723. if (!callback) { this.events[eventName] = []; return true; }
  724. for (var i = 0; i < callbacks.length; i++) {
  725. if (callbacks[i] === callback) {
  726. this.events[eventName].splice(i, 1);
  727. return true;
  728. }
  729. }
  730. return false;
  731. },
  732. dispatchEvent: function (event) {
  733. var i,
  734. args,
  735. callbacks = this.events[event.type];
  736. if (callbacks) {
  737. for (i = 0; i < callbacks.length; i++) {
  738. callbacks[i].apply(this, [event]);
  739. }
  740. }
  741. },
  742. // end: fake events
  743. // fake DOM attribute methods
  744. hasAttribute: function(name){
  745. return (name in this.attributes);
  746. },
  747. removeAttribute: function(name){
  748. delete this.attributes[name];
  749. },
  750. getAttribute: function(name){
  751. if (this.hasAttribute(name)) {
  752. return this.attributes[name];
  753. }
  754. return '';
  755. },
  756. setAttribute: function(name, value){
  757. this.attributes[name] = value;
  758. },
  759. remove: function() {
  760. mejs.Utility.removeSwf(this.pluginElement.id);
  761. mejs.MediaPluginBridge.unregisterPluginElement(this.pluginElement.id);
  762. }
  763. };
  764. // Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
  765. mejs.MediaPluginBridge = {
  766. pluginMediaElements:{},
  767. htmlMediaElements:{},
  768. registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
  769. this.pluginMediaElements[id] = pluginMediaElement;
  770. this.htmlMediaElements[id] = htmlMediaElement;
  771. },
  772. unregisterPluginElement: function (id) {
  773. delete this.pluginMediaElements[id];
  774. delete this.htmlMediaElements[id];
  775. },
  776. // when Flash/Silverlight is ready, it calls out to this method
  777. initPlugin: function (id) {
  778. var pluginMediaElement = this.pluginMediaElements[id],
  779. htmlMediaElement = this.htmlMediaElements[id];
  780. if (pluginMediaElement) {
  781. // find the javascript bridge
  782. switch (pluginMediaElement.pluginType) {
  783. case "flash":
  784. pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
  785. break;
  786. case "silverlight":
  787. pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
  788. pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
  789. break;
  790. }
  791. if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
  792. pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
  793. }
  794. }
  795. },
  796. // receives events from Flash/Silverlight and sends them out as HTML5 media events
  797. // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
  798. fireEvent: function (id, eventName, values) {
  799. var
  800. e,
  801. i,
  802. bufferedTime,
  803. pluginMediaElement = this.pluginMediaElements[id];
  804. if(!pluginMediaElement){
  805. return;
  806. }
  807. // fake event object to mimic real HTML media event.
  808. e = {
  809. type: eventName,
  810. target: pluginMediaElement
  811. };
  812. // attach all values to element and event object
  813. for (i in values) {
  814. pluginMediaElement[i] = values[i];
  815. e[i] = values[i];
  816. }
  817. // fake the newer W3C buffered TimeRange (loaded and total have been removed)
  818. bufferedTime = values.bufferedTime || 0;
  819. e.target.buffered = e.buffered = {
  820. start: function(index) {
  821. return 0;
  822. },
  823. end: function (index) {
  824. return bufferedTime;
  825. },
  826. length: 1
  827. };
  828. pluginMediaElement.dispatchEvent(e);
  829. }
  830. };
  831. /*
  832. Default options
  833. */
  834. mejs.MediaElementDefaults = {
  835. // allows testing on HTML5, flash, silverlight
  836. // auto: attempts to detect what the browser can do
  837. // auto_plugin: prefer plugins and then attempt native HTML5
  838. // native: forces HTML5 playback
  839. // shim: disallows HTML5, will attempt either Flash or Silverlight
  840. // none: forces fallback view
  841. mode: 'auto',
  842. // remove or reorder to change plugin priority and availability
  843. plugins: ['flash','silverlight','youtube','vimeo'],
  844. // shows debug errors on screen
  845. enablePluginDebug: false,
  846. // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites
  847. httpsBasicAuthSite: false,
  848. // overrides the type specified, useful for dynamic instantiation
  849. type: '',
  850. // path to Flash and Silverlight plugins
  851. pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
  852. // name of flash file
  853. flashName: 'flashmediaelement.swf',
  854. // streamer for RTMP streaming
  855. flashStreamer: '',
  856. // set to 'always' for CDN version
  857. flashScriptAccess: 'sameDomain',
  858. // turns on the smoothing filter in Flash
  859. enablePluginSmoothing: false,
  860. // enabled pseudo-streaming (seek) on .mp4 files
  861. enablePseudoStreaming: false,
  862. // start query parameter sent to server for pseudo-streaming
  863. pseudoStreamingStartQueryParam: 'start',
  864. // name of silverlight file
  865. silverlightName: 'silverlightmediaelement.xap',
  866. // default if the <video width> is not specified
  867. defaultVideoWidth: 480,
  868. // default if the <video height> is not specified
  869. defaultVideoHeight: 270,
  870. // overrides <video width>
  871. pluginWidth: -1,
  872. // overrides <video height>
  873. pluginHeight: -1,
  874. // additional plugin variables in 'key=value' form
  875. pluginVars: [],
  876. // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
  877. // larger number is less accurate, but less strain on plugin->JavaScript bridge
  878. timerRate: 250,
  879. // initial volume for player
  880. startVolume: 0.8,
  881. success: function () { },
  882. error: function () { }
  883. };
  884. /*
  885. Determines if a browser supports the <video> or <audio> element
  886. and returns either the native element or a Flash/Silverlight version that
  887. mimics HTML5 MediaElement
  888. */
  889. mejs.MediaElement = function (el, o) {
  890. return mejs.HtmlMediaElementShim.create(el,o);
  891. };
  892. mejs.HtmlMediaElementShim = {
  893. create: function(el, o) {
  894. var
  895. options = {},
  896. htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
  897. tagName = htmlMediaElement.tagName.toLowerCase(),
  898. isMediaTag = (tagName === 'audio' || tagName === 'video'),
  899. src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
  900. poster = htmlMediaElement.getAttribute('poster'),
  901. autoplay = htmlMediaElement.getAttribute('autoplay'),
  902. preload = htmlMediaElement.getAttribute('preload'),
  903. controls = htmlMediaElement.getAttribute('controls'),
  904. playback,
  905. prop;
  906. // extend options
  907. for (prop in mejs.MediaElementDefaults) {
  908. options[prop] = mejs.MediaElementDefaults[prop];
  909. }
  910. for (prop in o) {
  911. options[prop] = o[prop];
  912. }
  913. // clean up attributes
  914. src = (typeof src == 'undefined' || src === null || src == '') ? null : src;
  915. poster = (typeof poster == 'undefined' || poster === null) ? '' : poster;
  916. preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload;
  917. autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
  918. controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
  919. // test for HTML5 and plugin capabilities
  920. playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
  921. playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';
  922. if (playback.method == 'native') {
  923. // second fix for android
  924. if (mejs.MediaFeatures.isBustedAndroid) {
  925. htmlMediaElement.src = playback.url;
  926. htmlMediaElement.addEventListener('click', function() {
  927. htmlMediaElement.play();
  928. }, false);
  929. }
  930. // add methods to native HTMLMediaElement
  931. return this.updateNative(playback, options, autoplay, preload);
  932. } else if (playback.method !== '') {
  933. // create plugin to mimic HTMLMediaElement
  934. return this.createPlugin( playback, options, poster, autoplay, preload, controls);
  935. } else {
  936. // boo, no HTML5, no Flash, no Silverlight.
  937. this.createErrorMessage( playback, options, poster );
  938. return this;
  939. }
  940. },
  941. determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
  942. var
  943. mediaFiles = [],
  944. i,
  945. j,
  946. k,
  947. l,
  948. n,
  949. type,
  950. result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')},
  951. pluginName,
  952. pluginVersions,
  953. pluginInfo,
  954. dummy,
  955. media;
  956. // STEP 1: Get URL and type from <video src> or <source src>
  957. // supplied type overrides <video type> and <source type>
  958. if (typeof options.type != 'undefined' && options.type !== '') {
  959. // accept either string or array of types
  960. if (typeof options.type == 'string') {
  961. mediaFiles.push({type:options.type, url:src});
  962. } else {
  963. for (i=0; i<options.type.length; i++) {
  964. mediaFiles.push({type:options.type[i], url:src});
  965. }
  966. }
  967. // test for src attribute first
  968. } else if (src !== null) {
  969. type = this.formatType(src, htmlMediaElement.getAttribute('type'));
  970. mediaFiles.push({type:type, url:src});
  971. // then test for <source> elements
  972. } else {
  973. // test <source> types to see if they are usable
  974. for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
  975. n = htmlMediaElement.childNodes[i];
  976. if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
  977. src = n.getAttribute('src');
  978. type = this.formatType(src, n.getAttribute('type'));
  979. media = n.getAttribute('media');
  980. if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) {
  981. mediaFiles.push({type:type, url:src});
  982. }
  983. }
  984. }
  985. }
  986. // in the case of dynamicly created players
  987. // check for audio types
  988. if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
  989. result.isVideo = false;
  990. }
  991. // STEP 2: Test for playback method
  992. // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
  993. if (mejs.MediaFeatures.isBustedAndroid) {
  994. htmlMediaElement.canPlayType = function(type) {
  995. return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
  996. };
  997. }
  998. // special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora)
  999. if (mejs.MediaFeatures.isChromium) {
  1000. htmlMediaElement.canPlayType = function(type) {
  1001. return (type.match(/video\/(webm|ogv|ogg)/gi) !== null) ? 'maybe' : '';
  1002. };
  1003. }
  1004. // test for native playback first
  1005. if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native') && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
  1006. if (!isMediaTag) {
  1007. // create a real HTML5 Media Element
  1008. dummy = document.createElement( result.isVideo ? 'video' : 'audio');
  1009. htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
  1010. htmlMediaElement.style.display = 'none';
  1011. // use this one from now on
  1012. result.htmlMediaElement = htmlMediaElement = dummy;
  1013. }
  1014. for (i=0; i<mediaFiles.length; i++) {
  1015. // normal check
  1016. if (mediaFiles[i].type == "video/m3u8" || htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
  1017. // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
  1018. || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== ''
  1019. // special case for m4a supported by detecting mp4 support
  1020. || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/m4a/,'mp4')).replace(/no/, '') !== '') {
  1021. result.method = 'native';
  1022. result.url = mediaFiles[i].url;
  1023. break;
  1024. }
  1025. }
  1026. if (result.method === 'native') {
  1027. if (result.url !== null) {
  1028. htmlMediaElement.src = result.url;
  1029. }
  1030. // if `auto_plugin` mode, then cache the native result but try plugins.
  1031. if (options.mode !== 'auto_plugin') {
  1032. return result;
  1033. }
  1034. }
  1035. }
  1036. // if native playback didn't work, then test plugins
  1037. if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') {
  1038. for (i=0; i<mediaFiles.length; i++) {
  1039. type = mediaFiles[i].type;
  1040. // test all plugins in order of preference [silverlight, flash]
  1041. for (j=0; j<options.plugins.length; j++) {
  1042. pluginName = options.plugins[j];
  1043. // test version of plugin (for future features)
  1044. pluginVersions = mejs.plugins[pluginName];
  1045. for (k=0; k<pluginVersions.length; k++) {
  1046. pluginInfo = pluginVersions[k];
  1047. // test if user has the correct plugin version
  1048. // for youtube/vimeo
  1049. if (pluginInfo.version == null ||
  1050. mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
  1051. // test for plugin playback types
  1052. for (l=0; l<pluginInfo.types.length; l++) {
  1053. // find plugin that can play the type
  1054. if (type.toLowerCase() == pluginInfo.types[l].toLowerCase()) {
  1055. result.method = pluginName;
  1056. result.url = mediaFiles[i].url;
  1057. return result;
  1058. }
  1059. }
  1060. }
  1061. }
  1062. }
  1063. }
  1064. }
  1065. // at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
  1066. // if we have native support then return that.
  1067. if (options.mode === 'auto_plugin' && result.method === 'native') {
  1068. return result;
  1069. }
  1070. // what if there's nothing to play? just grab the first available
  1071. if (result.method === '' && mediaFiles.length > 0) {
  1072. result.url = mediaFiles[0].url;
  1073. }
  1074. return result;
  1075. },
  1076. formatType: function(url, type) {
  1077. // if no type is supplied, fake it with the extension
  1078. if (url && !type) {
  1079. return this.getTypeFromFile(url);
  1080. } else {
  1081. // only return the mime part of the type in case the attribute contains the codec
  1082. // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
  1083. // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
  1084. if (type && ~type.indexOf(';')) {
  1085. return type.substr(0, type.indexOf(';'));
  1086. } else {
  1087. return type;
  1088. }
  1089. }
  1090. },
  1091. getTypeFromFile: function(url) {
  1092. url = url.split('?')[0];
  1093. var
  1094. ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(),
  1095. av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video/' : 'audio/';
  1096. return this.getTypeFromExtension(ext, av);
  1097. },
  1098. getTypeFromExtension: function(ext, av) {
  1099. av = av || '';
  1100. switch (ext) {
  1101. case 'mp4':
  1102. case 'm4v':
  1103. case 'm4a':
  1104. case 'f4v':
  1105. case 'f4a':
  1106. return av + 'mp4';
  1107. case 'flv':
  1108. return av + 'x-flv';
  1109. case 'webm':
  1110. case 'webma':
  1111. case 'webmv':
  1112. return av + 'webm';
  1113. case 'ogg':
  1114. case 'oga':
  1115. case 'ogv':
  1116. return av + 'ogg';
  1117. case 'm3u8':
  1118. return 'application/x-mpegurl';
  1119. case 'ts':
  1120. return av + 'mp2t';
  1121. default:
  1122. return av + ext;
  1123. }
  1124. },
  1125. createErrorMessage: function(playback, options, poster) {
  1126. var
  1127. htmlMediaElement = playback.htmlMediaElement,
  1128. errorContainer = document.createElement('div'),
  1129. errorContent = options.customError;
  1130. errorContainer.className = 'me-cannotplay';
  1131. try {
  1132. errorContainer.style.width = htmlMediaElement.width + 'px';
  1133. errorContainer.style.height = htmlMediaElement.height + 'px';
  1134. } catch (e) {}
  1135. if (!errorContent) {
  1136. errorContent = '<a href="' + playback.url + '">';
  1137. if (poster !== '') {
  1138. errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />';
  1139. }
  1140. errorContent += '<span>' + mejs.i18n.t('Download File') + '</span></a>';
  1141. }
  1142. errorContainer.innerHTML = errorContent;
  1143. htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
  1144. htmlMediaElement.style.display = 'none';
  1145. options.error(htmlMediaElement);
  1146. },
  1147. createPlugin:function(playback, options, poster, autoplay, preload, controls) {
  1148. var
  1149. htmlMediaElement = playback.htmlMediaElement,
  1150. width = 1,
  1151. height = 1,
  1152. pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
  1153. pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
  1154. container = document.createElement('div'),
  1155. specialIEContainer,
  1156. node,
  1157. initVars;
  1158. // copy tagName from html media element
  1159. pluginMediaElement.tagName = htmlMediaElement.tagName
  1160. // copy attributes from html media element to plugin media element
  1161. for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
  1162. var attribute = htmlMediaElement.attributes[i];
  1163. if (attribute.specified) {
  1164. pluginMediaElement.setAttribute(attribute.name, attribute.value);
  1165. }
  1166. }
  1167. // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
  1168. node = htmlMediaElement.parentNode;
  1169. while (node !== null && node.tagName != null && node.tagName.toLowerCase() !== 'body' &&
  1170. node.parentNode != null && node.parentNode.tagName != null && node.parentNode.constructor != null && node.parentNode.constructor.name === "ShadowRoot") {
  1171. if (node.parentNode.tagName.toLowerCase() === 'p') {
  1172. node.parentNode.parentNode.insertBefore(node, node.parentNode);
  1173. break;
  1174. }
  1175. node = node.parentNode;
  1176. }
  1177. if (playback.isVideo) {
  1178. width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
  1179. height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
  1180. // in case of '%' make sure it's encoded
  1181. width = mejs.Utility.encodeUrl(width);
  1182. height = mejs.Utility.encodeUrl(height);
  1183. } else {
  1184. if (options.enablePluginDebug) {
  1185. width = 320;
  1186. height = 240;
  1187. }
  1188. }
  1189. // register plugin
  1190. pluginMediaElement.success = options.success;
  1191. mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
  1192. // add container (must be added to DOM before inserting HTML for IE)
  1193. container.className = 'me-plugin';
  1194. container.id = pluginid + '_container';
  1195. if (playback.isVideo) {
  1196. htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
  1197. } else {
  1198. document.body.insertBefore(container, document.body.childNodes[0]);
  1199. }
  1200. // flash/silverlight vars
  1201. initVars = [
  1202. 'id=' + pluginid,
  1203. 'jsinitfunction=' + "mejs.MediaPluginBridge.initPlugin",
  1204. 'jscallbackfunction=' + "mejs.MediaPluginBridge.fireEvent",
  1205. 'isvideo=' + ((playback.isVideo) ? "true" : "false"),
  1206. 'autoplay=' + ((autoplay) ? "true" : "false"),
  1207. 'preload=' + preload,
  1208. 'width=' + width,
  1209. 'startvolume=' + options.startVolume,
  1210. 'timerrate=' + options.timerRate,
  1211. 'flashstreamer=' + options.flashStreamer,
  1212. 'height=' + height,
  1213. 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];
  1214. if (playback.url !== null) {
  1215. if (playback.method == 'flash') {
  1216. initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
  1217. } else {
  1218. initVars.push('file=' + playback.url);
  1219. }
  1220. }
  1221. if (options.enablePluginDebug) {
  1222. initVars.push('debug=true');
  1223. }
  1224. if (options.enablePluginSmoothing) {
  1225. initVars.push('smoothing=true');
  1226. }
  1227. if (options.enablePseudoStreaming) {
  1228. initVars.push('pseudostreaming=true');
  1229. }
  1230. if (controls) {
  1231. initVars.push('controls=true'); // shows controls in the plugin if desired
  1232. }
  1233. if (options.pluginVars) {
  1234. initVars = initVars.concat(options.pluginVars);
  1235. }
  1236. switch (playback.method) {
  1237. case 'silverlight':
  1238. container.innerHTML =
  1239. '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1240. '<param name="initParams" value="' + initVars.join(',') + '" />' +
  1241. '<param name="windowless" value="true" />' +
  1242. '<param name="background" value="black" />' +
  1243. '<param name="minRuntimeVersion" value="3.0.0.0" />' +
  1244. '<param name="autoUpgrade" value="true" />' +
  1245. '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
  1246. '</object>';
  1247. break;
  1248. case 'flash':
  1249. if (mejs.MediaFeatures.isIE) {
  1250. specialIEContainer = document.createElement('div');
  1251. container.appendChild(specialIEContainer);
  1252. specialIEContainer.outerHTML =
  1253. '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
  1254. 'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1255. '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
  1256. '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
  1257. '<param name="quality" value="high" />' +
  1258. '<param name="bgcolor" value="#000000" />' +
  1259. '<param name="wmode" value="transparent" />' +
  1260. '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
  1261. '<param name="allowFullScreen" value="true" />' +
  1262. '<param name="scale" value="default" />' +
  1263. '</object>';
  1264. } else {
  1265. container.innerHTML =
  1266. '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
  1267. 'play="true" ' +
  1268. 'loop="false" ' +
  1269. 'quality="high" ' +
  1270. 'bgcolor="#000000" ' +
  1271. 'wmode="transparent" ' +
  1272. 'allowScriptAccess="' + options.flashScriptAccess + '" ' +
  1273. 'allowFullScreen="true" ' +
  1274. 'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
  1275. 'src="' + options.pluginPath + options.flashName + '" ' +
  1276. 'flashvars="' + initVars.join('&') + '" ' +
  1277. 'width="' + width + '" ' +
  1278. 'height="' + height + '" ' +
  1279. 'scale="default"' +
  1280. 'class="mejs-shim"></embed>';
  1281. }
  1282. break;
  1283. case 'youtube':
  1284. var videoId;
  1285. // youtu.be url from share button
  1286. if (playback.url.lastIndexOf("youtu.be") != -1) {
  1287. videoId = playback.url.substr(playback.url.lastIndexOf('/')+1);
  1288. if (videoId.indexOf('?') != -1) {
  1289. videoId = videoId.substr(0, videoId.indexOf('?'));
  1290. }
  1291. }
  1292. else {
  1293. videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
  1294. }
  1295. youtubeSettings = {
  1296. container: container,
  1297. containerId: container.id,
  1298. pluginMediaElement: pluginMediaElement,
  1299. pluginId: pluginid,
  1300. videoId: videoId,
  1301. height: height,
  1302. width: width
  1303. };
  1304. // favor iframe version of YouTube
  1305. if (window.postMessage) {
  1306. mejs.YouTubeApi.enqueueIframe(youtubeSettings);
  1307. } else if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
  1308. mejs.YouTubeApi.createFlash(youtubeSettings, options);
  1309. }
  1310. break;
  1311. // DEMO Code. Does NOT work.
  1312. case 'vimeo':
  1313. var player_id = pluginid + "_player";
  1314. pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
  1315. container.innerHTML ='<iframe src="//player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?api=1&portrait=0&byline=0&title=0&player_id=' + player_id + '" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim" id="' + player_id + '" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
  1316. if (typeof($f) == 'function') { // froogaloop available
  1317. var player = $f(container.childNodes[0]);
  1318. player.addEvent('ready', function() {
  1319. player.playVideo = function() {
  1320. player.api( 'play' );
  1321. }
  1322. player.stopVideo = function() {
  1323. player.api( 'unload' );
  1324. }
  1325. player.pauseVideo = function() {
  1326. player.api( 'pause' );
  1327. }
  1328. player.seekTo = function( seconds ) {
  1329. player.api( 'seekTo', seconds );
  1330. }
  1331. player.setVolume = function( volume ) {
  1332. player.api( 'setVolume', volume );
  1333. }
  1334. player.setMuted = function( muted ) {
  1335. if( muted ) {
  1336. player.lastVolume = player.api( 'getVolume' );
  1337. player.api( 'setVolume', 0 );
  1338. } else {
  1339. player.api( 'setVolume', player.lastVolume );
  1340. delete player.lastVolume;
  1341. }
  1342. }
  1343. function createEvent(player, pluginMediaElement, eventName, e) {
  1344. var event = {
  1345. type: eventName,
  1346. target: pluginMediaElement
  1347. };
  1348. if (eventName == 'timeupdate') {
  1349. pluginMediaElement.currentTime = event.currentTime = e.seconds;
  1350. pluginMediaElement.duration = event.duration = e.duration;
  1351. }
  1352. pluginMediaElement.dispatchEvent(event);
  1353. }
  1354. player.addEvent('play', function() {
  1355. createEvent(player, pluginMediaElement, 'play');
  1356. createEvent(player, pluginMediaElement, 'playing');
  1357. });
  1358. player.addEvent('pause', function() {
  1359. createEvent(player, pluginMediaElement, 'pause');
  1360. });
  1361. player.addEvent('finish', function() {
  1362. createEvent(player, pluginMediaElement, 'ended');
  1363. });
  1364. player.addEvent('playProgress', function(e) {
  1365. createEvent(player, pluginMediaElement, 'timeupdate', e);
  1366. });
  1367. pluginMediaElement.pluginElement = container;
  1368. pluginMediaElement.pluginApi = player;
  1369. // init mejs
  1370. mejs.MediaPluginBridge.initPlugin(pluginid);
  1371. });
  1372. }
  1373. else {
  1374. console.warn("You need to include froogaloop for vimeo to work");
  1375. }
  1376. break;
  1377. }
  1378. // hide original element
  1379. htmlMediaElement.style.display = 'none';
  1380. // prevent browser from autoplaying when using a plugin
  1381. htmlMediaElement.removeAttribute('autoplay');
  1382. // FYI: options.success will be fired by the MediaPluginBridge
  1383. return pluginMediaElement;
  1384. },
  1385. updateNative: function(playback, options, autoplay, preload) {
  1386. var htmlMediaElement = playback.htmlMediaElement,
  1387. m;
  1388. // add methods to video object to bring it into parity with Flash Object
  1389. for (m in mejs.HtmlMediaElement) {
  1390. htmlMediaElement[m] = mejs.HtmlMediaElement[m];
  1391. }
  1392. /*
  1393. Chrome now supports preload="none"
  1394. if (mejs.MediaFeatures.isChrome) {
  1395. // special case to enforce preload attribute (Chrome doesn't respect this)
  1396. if (preload === 'none' && !autoplay) {
  1397. // forces the browser to stop loading (note: fails in IE9)
  1398. htmlMediaElement.src = '';
  1399. htmlMediaElement.load();
  1400. htmlMediaElement.canceledPreload = true;
  1401. htmlMediaElement.addEventListener('play',function() {
  1402. if (htmlMediaElement.canceledPreload) {
  1403. htmlMediaElement.src = playback.url;
  1404. htmlMediaElement.load();
  1405. htmlMediaElement.play();
  1406. htmlMediaElement.canceledPreload = false;
  1407. }
  1408. }, false);
  1409. // for some reason Chrome forgets how to autoplay sometimes.
  1410. } else if (autoplay) {
  1411. htmlMediaElement.load();
  1412. htmlMediaElement.play();
  1413. }
  1414. }
  1415. */
  1416. // fire success code
  1417. options.success(htmlMediaElement, htmlMediaElement);
  1418. return htmlMediaElement;
  1419. }
  1420. };
  1421. /*
  1422. - test on IE (object vs. embed)
  1423. - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
  1424. - fullscreen?
  1425. */
  1426. // YouTube Flash and Iframe API
  1427. mejs.YouTubeApi = {
  1428. isIframeStarted: false,
  1429. isIframeLoaded: false,
  1430. loadIframeApi: function() {
  1431. if (!this.isIframeStarted) {
  1432. var tag = document.createElement('script');
  1433. tag.src = "//www.youtube.com/player_api";
  1434. var firstScriptTag = document.getElementsByTagName('script')[0];
  1435. firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  1436. this.isIframeStarted = true;
  1437. }
  1438. },
  1439. iframeQueue: [],
  1440. enqueueIframe: function(yt) {
  1441. if (this.isLoaded) {
  1442. this.createIframe(yt);
  1443. } else {
  1444. this.loadIframeApi();
  1445. this.iframeQueue.push(yt);
  1446. }
  1447. },
  1448. createIframe: function(settings) {
  1449. var
  1450. pluginMediaElement = settings.plug