PageRenderTime 62ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

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

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