mediaelement.js 54 KB

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