mediaelement.js 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442
  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.23.4';
  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/mp4', 'audio/mpeg', 'video/dailymotion', 'video/x-dailymotion', 'application/x-mpegURL', 'audio/ogg']}
  28. // 'video/youtube', 'video/x-youtube',
  29. // ,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
  30. ],
  31. youtube: [
  32. {version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']}
  33. ],
  34. vimeo: [
  35. {version: null, types: ['video/vimeo', 'video/x-vimeo']}
  36. ]
  37. };
  38. /*
  39. Utility methods
  40. */
  41. mejs.Utility = {
  42. encodeUrl: function(url) {
  43. return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
  44. },
  45. escapeHTML: function(s) {
  46. return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
  47. },
  48. absolutizeUrl: function(url) {
  49. var el = document.createElement('div');
  50. el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
  51. return el.firstChild.href;
  52. },
  53. getScriptPath: function(scriptNames) {
  54. var
  55. i = 0,
  56. j,
  57. codePath = '',
  58. testname = '',
  59. slashPos,
  60. filenamePos,
  61. scriptUrl,
  62. scriptPath,
  63. scriptFilename,
  64. scripts = document.getElementsByTagName('script'),
  65. il = scripts.length,
  66. jl = scriptNames.length;
  67. // go through all <script> tags
  68. for (; i < il; i++) {
  69. scriptUrl = scripts[i].src;
  70. slashPos = scriptUrl.lastIndexOf('/');
  71. if (slashPos > -1) {
  72. scriptFilename = scriptUrl.substring(slashPos + 1);
  73. scriptPath = scriptUrl.substring(0, slashPos + 1);
  74. } else {
  75. scriptFilename = scriptUrl;
  76. scriptPath = '';
  77. }
  78. // see if any <script> tags have a file name that matches the
  79. for (j = 0; j < jl; j++) {
  80. testname = scriptNames[j];
  81. filenamePos = scriptFilename.indexOf(testname);
  82. if (filenamePos > -1) {
  83. codePath = scriptPath;
  84. break;
  85. }
  86. }
  87. // if we found a path, then break and return it
  88. if (codePath !== '') {
  89. break;
  90. }
  91. }
  92. // send the best path back
  93. return codePath;
  94. },
  95. /*
  96. * Calculate the time format to use. We have a default format set in the
  97. * options but it can be imcomplete. We ajust it according to the media
  98. * duration.
  99. *
  100. * We support format like 'hh:mm:ss:ff'.
  101. */
  102. calculateTimeFormat: function(time, options, fps) {
  103. if (time < 0) {
  104. time = 0;
  105. }
  106. if(typeof fps == 'undefined') {
  107. fps = 25;
  108. }
  109. var format = options.timeFormat,
  110. firstChar = format[0],
  111. firstTwoPlaces = (format[1] == format[0]),
  112. separatorIndex = firstTwoPlaces? 2: 1,
  113. separator = ':',
  114. hours = Math.floor(time / 3600) % 24,
  115. minutes = Math.floor(time / 60) % 60,
  116. seconds = Math.floor(time % 60),
  117. frames = Math.floor(((time % 1)*fps).toFixed(3)),
  118. lis = [
  119. [frames, 'f'],
  120. [seconds, 's'],
  121. [minutes, 'm'],
  122. [hours, 'h']
  123. ];
  124. // Try to get the separator from the format
  125. if (format.length < separatorIndex) {
  126. separator = format[separatorIndex];
  127. }
  128. var required = false;
  129. for (var i=0, len=lis.length; i < len; i++) {
  130. if (format.indexOf(lis[i][1]) !== -1) {
  131. required=true;
  132. }
  133. else if (required) {
  134. var hasNextValue = false;
  135. for (var j=i; j < len; j++) {
  136. if (lis[j][0] > 0) {
  137. hasNextValue = true;
  138. break;
  139. }
  140. }
  141. if (! hasNextValue) {
  142. break;
  143. }
  144. if (!firstTwoPlaces) {
  145. format = firstChar + format;
  146. }
  147. format = lis[i][1] + separator + format;
  148. if (firstTwoPlaces) {
  149. format = lis[i][1] + format;
  150. }
  151. firstChar = lis[i][1];
  152. }
  153. }
  154. options.currentTimeFormat = format;
  155. },
  156. /*
  157. * Prefix the given number by zero if it is lower than 10.
  158. */
  159. twoDigitsString: function(n) {
  160. if (n < 10) {
  161. return '0' + n;
  162. }
  163. return String(n);
  164. },
  165. secondsToTimeCode: function(time, options) {
  166. if (time < 0) {
  167. time = 0;
  168. }
  169. // Maintain backward compatibility with method signature before v2.18.
  170. if (typeof options !== 'object') {
  171. var format = 'm:ss';
  172. format = arguments[1] ? 'hh:mm:ss' : format; // forceHours
  173. format = arguments[2] ? format + ':ff' : format; // showFrameCount
  174. options = {
  175. currentTimeFormat: format,
  176. framesPerSecond: arguments[3] || 25
  177. };
  178. }
  179. var fps = options.framesPerSecond;
  180. if(typeof fps === 'undefined') {
  181. fps = 25;
  182. }
  183. var format = options.currentTimeFormat,
  184. hours = Math.floor(time / 3600) % 24,
  185. minutes = Math.floor(time / 60) % 60,
  186. seconds = Math.floor(time % 60),
  187. frames = Math.floor(((time % 1)*fps).toFixed(3));
  188. lis = [
  189. [frames, 'f'],
  190. [seconds, 's'],
  191. [minutes, 'm'],
  192. [hours, 'h']
  193. ];
  194. var res = format;
  195. for (i=0,len=lis.length; i < len; i++) {
  196. res = res.replace(lis[i][1]+lis[i][1], this.twoDigitsString(lis[i][0]));
  197. res = res.replace(lis[i][1], lis[i][0]);
  198. }
  199. return res;
  200. },
  201. timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
  202. if (typeof showFrameCount == 'undefined') {
  203. showFrameCount=false;
  204. } else if(typeof fps == 'undefined') {
  205. fps = 25;
  206. }
  207. var tc_array = hh_mm_ss_ff.split(":"),
  208. tc_hh = parseInt(tc_array[0], 10),
  209. tc_mm = parseInt(tc_array[1], 10),
  210. tc_ss = parseInt(tc_array[2], 10),
  211. tc_ff = 0,
  212. tc_in_seconds = 0;
  213. if (showFrameCount) {
  214. tc_ff = parseInt(tc_array[3])/fps;
  215. }
  216. tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
  217. return tc_in_seconds;
  218. },
  219. convertSMPTEtoSeconds: function (SMPTE) {
  220. if (typeof SMPTE != 'string')
  221. return false;
  222. SMPTE = SMPTE.replace(',', '.');
  223. var secs = 0,
  224. decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0,
  225. multiplier = 1;
  226. SMPTE = SMPTE.split(':').reverse();
  227. for (var i = 0; i < SMPTE.length; i++) {
  228. multiplier = 1;
  229. if (i > 0) {
  230. multiplier = Math.pow(60, i);
  231. }
  232. secs += Number(SMPTE[i]) * multiplier;
  233. }
  234. return Number(secs.toFixed(decimalLen));
  235. },
  236. /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
  237. removeSwf: function(id) {
  238. var obj = document.getElementById(id);
  239. if (obj && /object|embed/i.test(obj.nodeName)) {
  240. if (mejs.MediaFeatures.isIE) {
  241. obj.style.display = "none";
  242. (function(){
  243. if (obj.readyState == 4) {
  244. mejs.Utility.removeObjectInIE(id);
  245. } else {
  246. setTimeout(arguments.callee, 10);
  247. }
  248. })();
  249. } else {
  250. obj.parentNode.removeChild(obj);
  251. }
  252. }
  253. },
  254. removeObjectInIE: function(id) {
  255. var obj = document.getElementById(id);
  256. if (obj) {
  257. for (var i in obj) {
  258. if (typeof obj[i] == "function") {
  259. obj[i] = null;
  260. }
  261. }
  262. obj.parentNode.removeChild(obj);
  263. }
  264. },
  265. determineScheme: function(url) {
  266. if (url && url.indexOf("://") != -1) {
  267. return url.substr(0, url.indexOf("://")+3);
  268. }
  269. return "//"; // let user agent figure this out
  270. },
  271. // taken from underscore
  272. debounce: function(func, wait, immediate) {
  273. var timeout;
  274. return function() {
  275. var context = this, args = arguments;
  276. var later = function() {
  277. timeout = null;
  278. if (!immediate) func.apply(context, args);
  279. };
  280. var callNow = immediate && !timeout;
  281. clearTimeout(timeout);
  282. timeout = setTimeout(later, wait);
  283. if (callNow) func.apply(context, args);
  284. };
  285. },
  286. /**
  287. * Returns true if targetNode appears after sourceNode in the dom.
  288. * @param {HTMLElement} sourceNode - the source node for comparison
  289. * @param {HTMLElement} targetNode - the node to compare against sourceNode
  290. */
  291. isNodeAfter: function(sourceNode, targetNode) {
  292. return !!(
  293. sourceNode &&
  294. targetNode &&
  295. typeof sourceNode.compareDocumentPosition === 'function' &&
  296. sourceNode.compareDocumentPosition(targetNode) & Node.DOCUMENT_POSITION_PRECEDING
  297. );
  298. }
  299. };
  300. // Core detector, plugins are added below
  301. mejs.PluginDetector = {
  302. // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
  303. hasPluginVersion: function(plugin, v) {
  304. var pv = this.plugins[plugin];
  305. v[1] = v[1] || 0;
  306. v[2] = v[2] || 0;
  307. 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;
  308. },
  309. // cached values
  310. nav: window.navigator,
  311. ua: window.navigator.userAgent.toLowerCase(),
  312. // stored version numbers
  313. plugins: [],
  314. // runs detectPlugin() and stores the version number
  315. addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
  316. this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
  317. },
  318. // get the version number from the mimetype (all but IE) or ActiveX (IE)
  319. detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
  320. var version = [0,0,0],
  321. description,
  322. i,
  323. ax;
  324. // Firefox, Webkit, Opera
  325. if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
  326. description = this.nav.plugins[pluginName].description;
  327. if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
  328. version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
  329. for (i=0; i<version.length; i++) {
  330. version[i] = parseInt(version[i].match(/\d+/), 10);
  331. }
  332. }
  333. // Internet Explorer / ActiveX
  334. } else if (typeof(window.ActiveXObject) != 'undefined') {
  335. try {
  336. ax = new ActiveXObject(activeX);
  337. if (ax) {
  338. version = axDetect(ax);
  339. }
  340. }
  341. catch (e) { }
  342. }
  343. return version;
  344. }
  345. };
  346. // Add Flash detection
  347. mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
  348. // adapted from SWFObject
  349. var version = [],
  350. d = ax.GetVariable("$version");
  351. if (d) {
  352. d = d.split(" ")[1].split(",");
  353. version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  354. }
  355. return version;
  356. });
  357. // Add Silverlight detection
  358. mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
  359. // Silverlight cannot report its version number to IE
  360. // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
  361. // adapted from http://www.silverlightversion.com/
  362. var v = [0,0,0,0],
  363. loopMatch = function(ax, v, i, n) {
  364. while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
  365. v[i]+=n;
  366. }
  367. v[i] -= n;
  368. };
  369. loopMatch(ax, v, 0, 1);
  370. loopMatch(ax, v, 1, 1);
  371. loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
  372. loopMatch(ax, v, 2, 1000);
  373. loopMatch(ax, v, 2, 100);
  374. loopMatch(ax, v, 2, 10);
  375. loopMatch(ax, v, 2, 1);
  376. loopMatch(ax, v, 3, 1);
  377. return v;
  378. });
  379. // add adobe acrobat
  380. /*
  381. PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
  382. var version = [],
  383. d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
  384. if (d) {
  385. version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  386. }
  387. return version;
  388. });
  389. */
  390. // necessary detection (fixes for <IE9)
  391. mejs.MediaFeatures = {
  392. init: function() {
  393. var
  394. t = this,
  395. d = document,
  396. nav = mejs.PluginDetector.nav,
  397. ua = mejs.PluginDetector.ua.toLowerCase(),
  398. i,
  399. v,
  400. html5Elements = ['source','track','audio','video'];
  401. // detect browsers (only the ones that have some kind of quirk we need to work around)
  402. t.isiPad = (ua.match(/ipad/i) !== null);
  403. t.isiPhone = (ua.match(/iphone/i) !== null);
  404. t.isiOS = t.isiPhone || t.isiPad;
  405. t.isAndroid = (ua.match(/android/i) !== null);
  406. t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
  407. t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null));
  408. t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
  409. t.isChrome = (ua.match(/chrome/gi) !== null);
  410. t.isChromium = (ua.match(/chromium/gi) !== null);
  411. t.isFirefox = (ua.match(/firefox/gi) !== null);
  412. t.isWebkit = (ua.match(/webkit/gi) !== null);
  413. t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE;
  414. t.isOpera = (ua.match(/opera/gi) !== null);
  415. t.hasTouch = ('ontouchstart' in window); // && window.ontouchstart != null); // this breaks iOS 7
  416. // Borrowed from `Modernizr.svgasimg`, sources:
  417. // - https://github.com/Modernizr/Modernizr/issues/687
  418. // - https://github.com/Modernizr/Modernizr/pull/1209/files
  419. t.svgAsImg = !!document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1');
  420. // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
  421. for (i=0; i<html5Elements.length; i++) {
  422. v = document.createElement(html5Elements[i]);
  423. }
  424. t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
  425. // Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer)
  426. try{
  427. v.canPlayType("video/mp4");
  428. }catch(e){
  429. t.supportsMediaTag = false;
  430. }
  431. t.supportsPointerEvents = (function() {
  432. // TAKEN FROM MODERNIZR
  433. var element = document.createElement('x'),
  434. documentElement = document.documentElement,
  435. getComputedStyle = window.getComputedStyle,
  436. supports;
  437. if(!('pointerEvents' in element.style)){
  438. return false;
  439. }
  440. element.style.pointerEvents = 'auto';
  441. element.style.pointerEvents = 'x';
  442. documentElement.appendChild(element);
  443. supports = getComputedStyle &&
  444. getComputedStyle(element, '').pointerEvents === 'auto';
  445. documentElement.removeChild(element);
  446. return !!supports;
  447. })();
  448. // Older versions of Firefox can't move plugins around without it resetting,
  449. t.hasFirefoxPluginMovingProblem = false;
  450. // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
  451. // iOS
  452. t.hasiOSFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
  453. // W3C
  454. t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined');
  455. // webkit/firefox/IE11+
  456. t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
  457. t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
  458. t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined');
  459. t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen);
  460. t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
  461. // Enabled?
  462. if (t.hasMozNativeFullScreen) {
  463. t.nativeFullScreenEnabled = document.mozFullScreenEnabled;
  464. } else if (t.hasMsNativeFullScreen) {
  465. t.nativeFullScreenEnabled = document.msFullscreenEnabled;
  466. }
  467. if (t.isChrome) {
  468. t.hasiOSFullScreen = false;
  469. }
  470. if (t.hasTrueNativeFullScreen) {
  471. t.fullScreenEventName = '';
  472. if (t.hasWebkitNativeFullScreen) {
  473. t.fullScreenEventName = 'webkitfullscreenchange';
  474. } else if (t.hasMozNativeFullScreen) {
  475. t.fullScreenEventName = 'mozfullscreenchange';
  476. } else if (t.hasMsNativeFullScreen) {
  477. t.fullScreenEventName = 'MSFullscreenChange';
  478. }
  479. t.isFullScreen = function() {
  480. if (t.hasMozNativeFullScreen) {
  481. return d.mozFullScreen;
  482. } else if (t.hasWebkitNativeFullScreen) {
  483. return d.webkitIsFullScreen;
  484. } else if (t.hasMsNativeFullScreen) {
  485. return d.msFullscreenElement !== null;
  486. }
  487. }
  488. t.requestFullScreen = function(el) {
  489. if (t.hasWebkitNativeFullScreen) {
  490. el.webkitRequestFullScreen();
  491. } else if (t.hasMozNativeFullScreen) {
  492. el.mozRequestFullScreen();
  493. } else if (t.hasMsNativeFullScreen) {
  494. el.msRequestFullscreen();
  495. }
  496. }
  497. t.cancelFullScreen = function() {
  498. if (t.hasWebkitNativeFullScreen) {
  499. document.webkitCancelFullScreen();
  500. } else if (t.hasMozNativeFullScreen) {
  501. document.mozCancelFullScreen();
  502. } else if (t.hasMsNativeFullScreen) {
  503. document.msExitFullscreen();
  504. }
  505. }
  506. }
  507. // OS X 10.5 can't do this even if it says it can :(
  508. if (t.hasiOSFullScreen && ua.match(/mac os x 10_5/i)) {
  509. t.hasNativeFullScreen = false;
  510. t.hasiOSFullScreen = false;
  511. }
  512. }
  513. };
  514. mejs.MediaFeatures.init();
  515. /*
  516. extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
  517. */
  518. mejs.HtmlMediaElement = {
  519. pluginType: 'native',
  520. isFullScreen: false,
  521. setCurrentTime: function (time) {
  522. this.currentTime = time;
  523. },
  524. setMuted: function (muted) {
  525. this.muted = muted;
  526. },
  527. setVolume: function (volume) {
  528. this.volume = volume;
  529. },
  530. // for parity with the plugin versions
  531. stop: function () {
  532. this.pause();
  533. },
  534. // This can be a url string
  535. // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
  536. setSrc: function (url) {
  537. // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
  538. var
  539. existingSources = this.getElementsByTagName('source');
  540. while (existingSources.length > 0){
  541. this.removeChild(existingSources[0]);
  542. }
  543. if (typeof url == 'string') {
  544. this.src = url;
  545. } else {
  546. var i, media;
  547. for (i=0; i<url.length; i++) {
  548. media = url[i];
  549. if (this.canPlayType(media.type)) {
  550. this.src = media.src;
  551. break;
  552. }
  553. }
  554. }
  555. },
  556. setVideoSize: function (width, height) {
  557. this.width = width;
  558. this.height = height;
  559. }
  560. };
  561. /*
  562. Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
  563. */
  564. mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
  565. this.id = pluginid;
  566. this.pluginType = pluginType;
  567. this.src = mediaUrl;
  568. this.events = {};
  569. this.attributes = {};
  570. };
  571. // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
  572. // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
  573. // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
  574. mejs.PluginMediaElement.prototype = {
  575. // special
  576. pluginElement: null,
  577. pluginType: '',
  578. isFullScreen: false,
  579. // not implemented :(
  580. playbackRate: -1,
  581. defaultPlaybackRate: -1,
  582. seekable: [],
  583. played: [],
  584. // HTML5 read-only properties
  585. paused: true,
  586. ended: false,
  587. seeking: false,
  588. duration: 0,
  589. error: null,
  590. tagName: '',
  591. // HTML5 get/set properties, but only set (updated by event handlers)
  592. muted: false,
  593. volume: 1,
  594. currentTime: 0,
  595. // HTML5 methods
  596. play: function () {
  597. if (this.pluginApi != null) {
  598. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  599. this.pluginApi.playVideo();
  600. } else {
  601. this.pluginApi.playMedia();
  602. }
  603. this.paused = false;
  604. }
  605. },
  606. load: function () {
  607. if (this.pluginApi != null) {
  608. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  609. } else {
  610. this.pluginApi.loadMedia();
  611. }
  612. this.paused = false;
  613. }
  614. },
  615. pause: function () {
  616. if (this.pluginApi != null) {
  617. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  618. if( this.pluginApi.getPlayerState() == 1 ) {
  619. this.pluginApi.pauseVideo();
  620. }
  621. } else {
  622. this.pluginApi.pauseMedia();
  623. }
  624. this.paused = true;
  625. }
  626. },
  627. stop: function () {
  628. if (this.pluginApi != null) {
  629. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  630. this.pluginApi.stopVideo();
  631. } else {
  632. this.pluginApi.stopMedia();
  633. }
  634. this.paused = true;
  635. }
  636. },
  637. canPlayType: function(type) {
  638. var i,
  639. j,
  640. pluginInfo,
  641. pluginVersions = mejs.plugins[this.pluginType];
  642. for (i=0; i<pluginVersions.length; i++) {
  643. pluginInfo = pluginVersions[i];
  644. // test if user has the correct plugin version
  645. if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
  646. // test for plugin playback types
  647. for (j=0; j<pluginInfo.types.length; j++) {
  648. // find plugin that can play the type
  649. if (type == pluginInfo.types[j]) {
  650. return 'probably';
  651. }
  652. }
  653. }
  654. }
  655. return '';
  656. },
  657. positionFullscreenButton: function(x,y,visibleAndAbove) {
  658. if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
  659. this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove);
  660. }
  661. },
  662. hideFullscreenButton: function() {
  663. if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
  664. this.pluginApi.hideFullscreenButton();
  665. }
  666. },
  667. // custom methods since not all JavaScript implementations support get/set
  668. // This can be a url string
  669. // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
  670. setSrc: function (url) {
  671. if (typeof url == 'string') {
  672. this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
  673. this.src = mejs.Utility.absolutizeUrl(url);
  674. } else {
  675. var i, media;
  676. for (i=0; i<url.length; i++) {
  677. media = url[i];
  678. if (this.canPlayType(media.type)) {
  679. this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
  680. this.src = mejs.Utility.absolutizeUrl(media.src);
  681. break;
  682. }
  683. }
  684. }
  685. },
  686. setCurrentTime: function (time) {
  687. if (this.pluginApi != null) {
  688. if (this.pluginType == 'youtube' || this.pluginType == 'vimeo') {
  689. this.pluginApi.seekTo(time);
  690. } else {
  691. this.pluginApi.setCurrentTime(time);
  692. }
  693. this.currentTime = time;
  694. }
  695. },
  696. setVolume: function (volume) {
  697. if (this.pluginApi != null) {
  698. // same on YouTube and MEjs
  699. if (this.pluginType == 'youtube') {
  700. this.pluginApi.setVolume(volume * 100);
  701. } else {
  702. this.pluginApi.setVolume(volume);
  703. }
  704. this.volume = volume;
  705. }
  706. },
  707. setMuted: function (muted) {
  708. if (this.pluginApi != null) {
  709. if (this.pluginType == 'youtube') {
  710. if (muted) {
  711. this.pluginApi.mute();
  712. } else {
  713. this.pluginApi.unMute();
  714. }
  715. this.muted = muted;
  716. this.dispatchEvent({type:'volumechange'});
  717. } else {
  718. this.pluginApi.setMuted(muted);
  719. }
  720. this.muted = muted;
  721. }
  722. },
  723. // additional non-HTML5 methods
  724. setVideoSize: function (width, height) {
  725. //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
  726. if (this.pluginElement && this.pluginElement.style) {
  727. this.pluginElement.style.width = width + 'px';
  728. this.pluginElement.style.height = height + 'px';
  729. }
  730. if (this.pluginApi != null && this.pluginApi.setVideoSize) {
  731. this.pluginApi.setVideoSize(width, height);
  732. }
  733. //}
  734. },
  735. setFullscreen: function (fullscreen) {
  736. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  737. this.pluginApi.setFullscreen(fullscreen);
  738. }
  739. },
  740. enterFullScreen: function() {
  741. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  742. this.setFullscreen(true);
  743. }
  744. },
  745. exitFullScreen: function() {
  746. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  747. this.setFullscreen(false);
  748. }
  749. },
  750. // start: fake events
  751. addEventListener: function (eventName, callback, bubble) {
  752. this.events[eventName] = this.events[eventName] || [];
  753. this.events[eventName].push(callback);
  754. },
  755. removeEventListener: function (eventName, callback) {
  756. if (!eventName) { this.events = {}; return true; }
  757. var callbacks = this.events[eventName];
  758. if (!callbacks) return true;
  759. if (!callback) { this.events[eventName] = []; return true; }
  760. for (var i = 0; i < callbacks.length; i++) {
  761. if (callbacks[i] === callback) {
  762. this.events[eventName].splice(i, 1);
  763. return true;
  764. }
  765. }
  766. return false;
  767. },
  768. dispatchEvent: function (event) {
  769. var i,
  770. args,
  771. callbacks = this.events[event.type];
  772. if (callbacks) {
  773. for (i = 0; i < callbacks.length; i++) {
  774. callbacks[i].apply(this, [event]);
  775. }
  776. }
  777. },
  778. // end: fake events
  779. // fake DOM attribute methods
  780. hasAttribute: function(name){
  781. return (name in this.attributes);
  782. },
  783. removeAttribute: function(name){
  784. delete this.attributes[name];
  785. },
  786. getAttribute: function(name){
  787. if (this.hasAttribute(name)) {
  788. return this.attributes[name];
  789. }
  790. return null;
  791. },
  792. setAttribute: function(name, value){
  793. this.attributes[name] = value;
  794. },
  795. remove: function() {
  796. mejs.Utility.removeSwf(this.pluginElement.id);
  797. }
  798. };
  799. /*
  800. Default options
  801. */
  802. mejs.MediaElementDefaults = {
  803. // allows testing on HTML5, flash, silverlight
  804. // auto: attempts to detect what the browser can do
  805. // auto_plugin: prefer plugins and then attempt native HTML5
  806. // native: forces HTML5 playback
  807. // shim: disallows HTML5, will attempt either Flash or Silverlight
  808. // none: forces fallback view
  809. mode: 'auto',
  810. // remove or reorder to change plugin priority and availability
  811. plugins: ['flash','silverlight','youtube','vimeo'],
  812. // shows debug errors on screen
  813. enablePluginDebug: false,
  814. // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites
  815. httpsBasicAuthSite: false,
  816. // overrides the type specified, useful for dynamic instantiation
  817. type: '',
  818. // path to Flash and Silverlight plugins
  819. pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
  820. // name of flash file
  821. flashName: 'flashmediaelement.swf',
  822. // streamer for RTMP streaming
  823. flashStreamer: '',
  824. // set to 'always' for CDN version
  825. flashScriptAccess: 'sameDomain',
  826. // turns on the smoothing filter in Flash
  827. enablePluginSmoothing: false,
  828. // enabled pseudo-streaming (seek) on .mp4 files
  829. enablePseudoStreaming: false,
  830. // start query parameter sent to server for pseudo-streaming
  831. pseudoStreamingStartQueryParam: 'start',
  832. // name of silverlight file
  833. silverlightName: 'silverlightmediaelement.xap',
  834. // default if the <video width> is not specified
  835. defaultVideoWidth: 480,
  836. // default if the <video height> is not specified
  837. defaultVideoHeight: 270,
  838. // overrides <video width>
  839. pluginWidth: -1,
  840. // overrides <video height>
  841. pluginHeight: -1,
  842. // additional plugin variables in 'key=value' form
  843. pluginVars: [],
  844. // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
  845. // larger number is less accurate, but less strain on plugin->JavaScript bridge
  846. timerRate: 250,
  847. // initial volume for player
  848. startVolume: 0.8,
  849. // custom error message in case media cannot be played; otherwise, Download File
  850. // link will be displayed
  851. customError: "",
  852. success: function () { },
  853. error: function () { }
  854. };
  855. /*
  856. Determines if a browser supports the <video> or <audio> element
  857. and returns either the native element or a Flash/Silverlight version that
  858. mimics HTML5 MediaElement
  859. */
  860. mejs.MediaElement = function (el, o) {
  861. return mejs.HtmlMediaElementShim.create(el,o);
  862. };
  863. mejs.HtmlMediaElementShim = {
  864. create: function(el, o) {
  865. var
  866. options = {},
  867. htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
  868. tagName = htmlMediaElement.tagName.toLowerCase(),
  869. isMediaTag = (tagName === 'audio' || tagName === 'video'),
  870. src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
  871. poster = htmlMediaElement.getAttribute('poster'),
  872. autoplay = htmlMediaElement.getAttribute('autoplay'),
  873. preload = htmlMediaElement.getAttribute('preload'),
  874. controls = htmlMediaElement.getAttribute('controls'),
  875. playback,
  876. prop;
  877. // extend options
  878. for (prop in mejs.MediaElementDefaults) {
  879. options[prop] = mejs.MediaElementDefaults[prop];
  880. }
  881. for (prop in o) {
  882. options[prop] = o[prop];
  883. }
  884. // clean up attributes
  885. src = (typeof src == 'undefined' || src === null || src == '') ? null : src;
  886. poster = (typeof poster == 'undefined' || poster === null) ? '' : poster;
  887. preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload;
  888. autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
  889. controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
  890. // test for HTML5 and plugin capabilities
  891. playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
  892. playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';
  893. playback.scheme = mejs.Utility.determineScheme(playback.url);
  894. if (playback.method == 'native') {
  895. // second fix for android
  896. if (mejs.MediaFeatures.isBustedAndroid) {
  897. htmlMediaElement.src = playback.url;
  898. htmlMediaElement.addEventListener('click', function() {
  899. htmlMediaElement.play();
  900. }, false);
  901. }
  902. // add methods to native HTMLMediaElement
  903. return this.updateNative(playback, options, autoplay, preload);
  904. } else if (playback.method !== '') {
  905. // create plugin to mimic HTMLMediaElement
  906. return this.createPlugin( playback, options, poster, autoplay, preload, controls);
  907. } else {
  908. // boo, no HTML5, no Flash, no Silverlight.
  909. this.createErrorMessage( playback, options, poster );
  910. return this;
  911. }
  912. },
  913. determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
  914. var
  915. mediaFiles = [],
  916. i,
  917. j,
  918. k,
  919. l,
  920. n,
  921. type,
  922. result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() !== 'audio'), scheme: ''},
  923. pluginName,
  924. pluginVersions,
  925. pluginInfo,
  926. dummy,
  927. media;
  928. // STEP 1: Get URL and type from <video src> or <source src>
  929. // supplied type overrides <video type> and <source type>
  930. if (typeof options.type != 'undefined' && options.type !== '') {
  931. // accept either string or array of types
  932. if (typeof options.type == 'string') {
  933. mediaFiles.push({type:options.type, url:src});
  934. } else {
  935. for (i=0; i<options.type.length; i++) {
  936. mediaFiles.push({type:options.type[i], url:src});
  937. }
  938. }
  939. // test for src attribute first
  940. } else if (src !== null) {
  941. type = this.formatType(src, htmlMediaElement.getAttribute('type'));
  942. mediaFiles.push({type:type, url:src});
  943. // then test for <source> elements
  944. } else {
  945. // test <source> types to see if they are usable
  946. for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
  947. n = htmlMediaElement.childNodes[i];
  948. if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
  949. src = n.getAttribute('src');
  950. type = this.formatType(src, n.getAttribute('type'));
  951. media = n.getAttribute('media');
  952. if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) {
  953. mediaFiles.push({type:type, url:src});
  954. }
  955. }
  956. }
  957. }
  958. // in the case of dynamicly created players
  959. // check for audio types
  960. if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
  961. result.isVideo = false;
  962. }
  963. // STEP 2: Test for playback method
  964. // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
  965. if (result.isVideo && mejs.MediaFeatures.isBustedAndroid) {
  966. htmlMediaElement.canPlayType = function(type) {
  967. return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
  968. };
  969. }
  970. // special case for Chromium to specify natively supported video codecs (i.e. WebM and Theora)
  971. if (result.isVideo && mejs.MediaFeatures.isChromium) {
  972. htmlMediaElement.canPlayType = function(type) {
  973. return (type.match(/video\/(webm|ogv|ogg)/gi) !== null) ? 'maybe' : '';
  974. };
  975. }
  976. // test for native playback first
  977. if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native') && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
  978. if (!isMediaTag) {
  979. // create a real HTML5 Media Element
  980. dummy = document.createElement( result.isVideo ? 'video' : 'audio');
  981. htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
  982. htmlMediaElement.style.display = 'none';
  983. // use this one from now on
  984. result.htmlMediaElement = htmlMediaElement = dummy;
  985. }
  986. for (i=0; i<mediaFiles.length; i++) {
  987. // normal check
  988. if (mediaFiles[i].type == "video/m3u8" || htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
  989. // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
  990. || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== ''
  991. // special case for m4a supported by detecting mp4 support
  992. || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/m4a/,'mp4')).replace(/no/, '') !== '') {
  993. result.method = 'native';
  994. result.url = mediaFiles[i].url;
  995. break;
  996. }
  997. }
  998. if (result.method === 'native') {
  999. if (result.url !== null) {
  1000. htmlMediaElement.src = result.url;
  1001. }
  1002. // if `auto_plugin` mode, then cache the native result but try plugins.
  1003. if (options.mode !== 'auto_plugin') {
  1004. return result;
  1005. }
  1006. }
  1007. }
  1008. // if native playback didn't work, then test plugins
  1009. if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') {
  1010. for (i=0; i<mediaFiles.length; i++) {
  1011. type = mediaFiles[i].type;
  1012. // test all plugins in order of preference [silverlight, flash]
  1013. for (j=0; j<options.plugins.length; j++) {
  1014. pluginName = options.plugins[j];
  1015. // test version of plugin (for future features)
  1016. pluginVersions = mejs.plugins[pluginName];
  1017. for (k=0; k<pluginVersions.length; k++) {
  1018. pluginInfo = pluginVersions[k];
  1019. // test if user has the correct plugin version
  1020. // for youtube/vimeo
  1021. if (pluginInfo.version == null ||
  1022. mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
  1023. // test for plugin playback types
  1024. for (l=0; l<pluginInfo.types.length; l++) {
  1025. // find plugin that can play the type
  1026. if (type.toLowerCase() == pluginInfo.types[l].toLowerCase()) {
  1027. result.method = pluginName;
  1028. result.url = mediaFiles[i].url;
  1029. return result;
  1030. }
  1031. }
  1032. }
  1033. }
  1034. }
  1035. }
  1036. }
  1037. // at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
  1038. // if we have native support then return that.
  1039. if (options.mode === 'auto_plugin' && result.method === 'native') {
  1040. return result;
  1041. }
  1042. // what if there's nothing to play? just grab the first available
  1043. if (result.method === '' && mediaFiles.length > 0) {
  1044. result.url = mediaFiles[0].url;
  1045. }
  1046. return result;
  1047. },
  1048. formatType: function(url, type) {
  1049. // if no type is supplied, fake it with the extension
  1050. if (url && !type) {
  1051. return this.getTypeFromFile(url);
  1052. } else {
  1053. // only return the mime part of the type in case the attribute contains the codec
  1054. // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
  1055. // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
  1056. if (type && ~type.indexOf(';')) {
  1057. return type.substr(0, type.indexOf(';'));
  1058. } else {
  1059. return type;
  1060. }
  1061. }
  1062. },
  1063. getTypeFromFile: function(url) {
  1064. url = url.split('?')[0];
  1065. var
  1066. ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(),
  1067. av = /(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video/' : 'audio/';
  1068. return this.getTypeFromExtension(ext, av);
  1069. },
  1070. getTypeFromExtension: function(ext, av) {
  1071. av = av || '';
  1072. switch (ext) {
  1073. case 'mp4':
  1074. case 'm4v':
  1075. case 'm4a':
  1076. case 'f4v':
  1077. case 'f4a':
  1078. return av + 'mp4';
  1079. case 'flv':
  1080. return av + 'x-flv';
  1081. case 'webm':
  1082. case 'webma':
  1083. case 'webmv':
  1084. return av + 'webm';
  1085. case 'ogg':
  1086. case 'oga':
  1087. case 'ogv':
  1088. return av + 'ogg';
  1089. case 'm3u8':
  1090. return 'application/x-mpegurl';
  1091. case 'ts':
  1092. return av + 'mp2t';
  1093. default:
  1094. return av + ext;
  1095. }
  1096. },
  1097. createErrorMessage: function(playback, options, poster) {
  1098. var
  1099. htmlMediaElement = playback.htmlMediaElement,
  1100. errorContainer = document.createElement('div'),
  1101. errorContent = options.customError;
  1102. errorContainer.className = 'me-cannotplay';
  1103. try {
  1104. errorContainer.style.width = htmlMediaElement.width + 'px';
  1105. errorContainer.style.height = htmlMediaElement.height + 'px';
  1106. } catch (e) {}
  1107. if (!errorContent) {
  1108. errorContent = '<a href="' + playback.url + '">';
  1109. if (poster !== '') {
  1110. errorContent += '<img src="' + poster + '" width="100%" height="100%" alt="" />';
  1111. }
  1112. errorContent += '<span>' + mejs.i18n.t('mejs.download-file') + '</span></a>';
  1113. }
  1114. errorContainer.innerHTML = errorContent;
  1115. htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
  1116. htmlMediaElement.style.display = 'none';
  1117. options.error(htmlMediaElement);
  1118. },
  1119. createPlugin:function(playback, options, poster, autoplay, preload, controls) {
  1120. var
  1121. htmlMediaElement = playback.htmlMediaElement,
  1122. width = 1,
  1123. height = 1,
  1124. pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
  1125. pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
  1126. container = document.createElement('div'),
  1127. specialIEContainer,
  1128. node,
  1129. initVars;
  1130. // copy tagName from html media element
  1131. pluginMediaElement.tagName = htmlMediaElement.tagName;
  1132. // copy attributes from html media element to plugin media element
  1133. for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
  1134. var attribute = htmlMediaElement.attributes[i];
  1135. if (attribute.specified) {
  1136. pluginMediaElement.setAttribute(attribute.name, attribute.value);
  1137. }
  1138. }
  1139. // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
  1140. node = htmlMediaElement.parentNode;
  1141. while (node !== null && node.tagName != null && node.tagName.toLowerCase() !== 'body' &&
  1142. node.parentNode != null && node.parentNode.tagName != null && node.parentNode.constructor != null && node.parentNode.constructor.name === "ShadowRoot") {
  1143. if (node.parentNode.tagName.toLowerCase() === 'p') {
  1144. node.parentNode.parentNode.insertBefore(node, node.parentNode);
  1145. break;
  1146. }
  1147. node = node.parentNode;
  1148. }
  1149. if (playback.isVideo) {
  1150. width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
  1151. height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
  1152. // in case of '%' make sure it's encoded
  1153. width = mejs.Utility.encodeUrl(width);
  1154. height = mejs.Utility.encodeUrl(height);
  1155. } else {
  1156. if (options.enablePluginDebug) {
  1157. width = 320;
  1158. height = 240;
  1159. }
  1160. }
  1161. // register plugin
  1162. pluginMediaElement.success = options.success;
  1163. // add container (must be added to DOM before inserting HTML for IE)
  1164. container.className = 'me-plugin';
  1165. container.id = pluginid + '_container';
  1166. if (playback.isVideo) {
  1167. htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
  1168. } else {
  1169. document.body.insertBefore(container, document.body.childNodes[0]);
  1170. }
  1171. if (playback.method === 'flash' || playback.method === 'silverlight') {
  1172. var canPlayVideo = htmlMediaElement.getAttribute('type') === 'audio/mp4',
  1173. childrenSources = htmlMediaElement.getElementsByTagName('source');
  1174. if (childrenSources && !canPlayVideo) {
  1175. for (var i = 0, total = childrenSources.length; i < total; i++) {
  1176. if (childrenSources[i].getAttribute('type') === 'audio/mp4') {
  1177. canPlayVideo = true;
  1178. }
  1179. }
  1180. }
  1181. // flash/silverlight vars
  1182. initVars = [
  1183. 'id=' + pluginid,
  1184. 'isvideo=' + ((playback.isVideo || canPlayVideo) ? "true" : "false"),
  1185. 'autoplay=' + ((autoplay) ? "true" : "false"),
  1186. 'preload=' + preload,
  1187. 'width=' + width,
  1188. 'startvolume=' + options.startVolume,
  1189. 'timerrate=' + options.timerRate,
  1190. 'flashstreamer=' + options.flashStreamer,
  1191. 'height=' + height,
  1192. 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];
  1193. if (playback.url !== null) {
  1194. if (playback.method == 'flash') {
  1195. initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
  1196. } else {
  1197. initVars.push('file=' + playback.url);
  1198. }
  1199. }
  1200. if (options.enablePluginDebug) {
  1201. initVars.push('debug=true');
  1202. }
  1203. if (options.enablePluginSmoothing) {
  1204. initVars.push('smoothing=true');
  1205. }
  1206. if (options.enablePseudoStreaming) {
  1207. initVars.push('pseudostreaming=true');
  1208. }
  1209. if (controls) {
  1210. initVars.push('controls=true'); // shows controls in the plugin if desired
  1211. }
  1212. if (options.pluginVars) {
  1213. initVars = initVars.concat(options.pluginVars);
  1214. }
  1215. // call from plugin
  1216. window[pluginid + '_init'] = function() {
  1217. switch (pluginMediaElement.pluginType) {
  1218. case 'flash':
  1219. pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(pluginid);
  1220. break;
  1221. case 'silverlight':
  1222. pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
  1223. pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
  1224. break;
  1225. }
  1226. if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
  1227. pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
  1228. }
  1229. };
  1230. // event call from plugin
  1231. window[pluginid + '_event'] = function(eventName, values) {
  1232. var
  1233. e,
  1234. i,
  1235. bufferedTime;
  1236. // fake event object to mimic real HTML media event.
  1237. e = {
  1238. type: eventName,
  1239. target: pluginMediaElement
  1240. };
  1241. // attach all values to element and event object
  1242. for (i in values) {
  1243. pluginMediaElement[i] = values[i];
  1244. e[i] = values[i];
  1245. }
  1246. // fake the newer W3C buffered TimeRange (loaded and total have been removed)
  1247. bufferedTime = values.bufferedTime || 0;
  1248. e.target.buffered = e.buffered = {
  1249. start: function(index) {
  1250. return 0;
  1251. },
  1252. end: function (index) {
  1253. return bufferedTime;
  1254. },
  1255. length: 1
  1256. };
  1257. pluginMediaElement.dispatchEvent(e);
  1258. }
  1259. }
  1260. switch (playback.method) {
  1261. case 'silverlight':
  1262. container.innerHTML =
  1263. '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1264. '<param name="initParams" value="' + initVars.join(',') + '" />' +
  1265. '<param name="windowless" value="true" />' +
  1266. '<param name="background" value="black" />' +
  1267. '<param name="minRuntimeVersion" value="3.0.0.0" />' +
  1268. '<param name="autoUpgrade" value="true" />' +
  1269. '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
  1270. '</object>';
  1271. break;
  1272. case 'flash':
  1273. if (mejs.MediaFeatures.isIE) {
  1274. specialIEContainer = document.createElement('div');
  1275. container.appendChild(specialIEContainer);
  1276. specialIEContainer.outerHTML =
  1277. '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
  1278. 'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1279. '<param name="movie" value="' + options.pluginPath + options.flashName + '?' + (new Date().getTime()) + '" />' +
  1280. '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
  1281. '<param name="quality" value="high" />' +
  1282. '<param name="bgcolor" value="#000000" />' +
  1283. '<param name="wmode" value="transparent" />' +
  1284. '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
  1285. '<param name="allowFullScreen" value="true" />' +
  1286. '<param name="scale" value="default" />' +
  1287. '</object>';
  1288. } else {
  1289. container.innerHTML =
  1290. '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
  1291. 'play="true" ' +
  1292. 'loop="false" ' +
  1293. 'quality="high" ' +
  1294. 'bgcolor="#000000" ' +
  1295. 'wmode="transparent" ' +
  1296. 'allowScriptAccess="' + options.flashScriptAccess + '" ' +
  1297. 'allowFullScreen="true" ' +
  1298. 'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
  1299. 'src="' + options.pluginPath + options.flashName + '" ' +
  1300. 'flashvars="' + initVars.join('&') + '" ' +
  1301. 'width="' + width + '" ' +
  1302. 'height="' + height + '" ' +
  1303. 'scale="default"' +
  1304. 'class="mejs-shim"></embed>';
  1305. }
  1306. break;
  1307. case 'youtube':
  1308. var videoId;
  1309. // youtu.be url from share button
  1310. if (playback.url.lastIndexOf("youtu.be") != -1) {
  1311. videoId = playback.url.substr(playback.url.lastIndexOf('/')+1);
  1312. if (videoId.indexOf('?') != -1) {
  1313. videoId = videoId.substr(0, videoId.indexOf('?'));
  1314. }
  1315. }
  1316. else {
  1317. // https://www.youtube.com/watch?v=
  1318. var videoIdMatch = playback.url.match( /[?&]v=([^&#]+)|&|#|$/ );
  1319. if ( videoIdMatch ) {
  1320. videoId = videoIdMatch[1];
  1321. }
  1322. }
  1323. youtubeSettings = {
  1324. container: container,
  1325. containerId: container.id,
  1326. pluginMediaElement: pluginMediaElement,
  1327. pluginId: pluginid,
  1328. videoId: videoId,
  1329. height: height,
  1330. width: width,
  1331. scheme: playback.scheme,
  1332. variables: options.youtubeIframeVars
  1333. };
  1334. // favor iframe version of YouTube
  1335. if (window.postMessage) {
  1336. mejs.YouTubeApi.enqueueIframe(youtubeSettings);
  1337. } else if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
  1338. mejs.YouTubeApi.createFlash(youtubeSettings, options);
  1339. }
  1340. break;
  1341. // DEMO Code. Does NOT work.
  1342. case 'vimeo':
  1343. var player_id = pluginid + "_player";
  1344. pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
  1345. container.innerHTML ='<iframe src="' + playback.scheme + '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>';
  1346. if (typeof($f) == 'function') { // froogaloop available
  1347. var player = $f(container.childNodes[0]),
  1348. playerState = -1;
  1349. player.addEvent('ready', function() {
  1350. player.playVideo = function() {
  1351. player.api( 'play' );
  1352. };
  1353. player.stopVideo = function() {
  1354. player.api( 'unload' );
  1355. };
  1356. player.pauseVideo = function() {
  1357. player.api( 'pause' );
  1358. };
  1359. player.seekTo = function( seconds ) {
  1360. player.api( 'seekTo', seconds );
  1361. };
  1362. player.setVolume = function( volume ) {
  1363. player.api( 'setVolume', volume );
  1364. };
  1365. player.setMuted = function( muted ) {
  1366. if( muted ) {
  1367. player.lastVolume = player.api( 'getVolume' );
  1368. player.api( 'setVolume', 0 );
  1369. } else {
  1370. player.api( 'setVolume', player.lastVolume );
  1371. delete player.lastVolume;
  1372. }
  1373. };
  1374. // parity with YT player
  1375. player.getPlayerState = function() {
  1376. return playerState;
  1377. };
  1378. function createEvent(player, pluginMediaElement, eventName, e) {
  1379. var event = {
  1380. type: eventName,
  1381. target: pluginMediaElement
  1382. };
  1383. if (eventName == 'timeupdate') {
  1384. pluginMediaElement.currentTime = event.currentTime = e.seconds;
  1385. pluginMediaElement.duration = event.duration = e.duration;
  1386. }
  1387. pluginMediaElement.dispatchEvent(event);
  1388. }
  1389. player.addEvent('play', function() {
  1390. playerState = 1;
  1391. createEvent(player, pluginMediaElement, 'play');
  1392. createEvent(player, pluginMediaElement, 'playing');
  1393. });
  1394. player.addEvent('pause', function() {
  1395. playerState = 2;
  1396. createEvent(player, pluginMediaElement, 'pause');
  1397. });
  1398. player.addEvent('finish', function() {
  1399. playerState = 0;
  1400. createEvent(player, pluginMediaElement, 'ended');
  1401. });
  1402. player.addEvent('playProgress', function(e) {
  1403. createEvent(player, pluginMediaElement, 'timeupdate', e);
  1404. });
  1405. player.addEvent('seek', function(e) {
  1406. playerState = 3;
  1407. createEvent(player, pluginMediaElement, 'seeked', e);
  1408. });
  1409. player.addEvent('loadProgress', function(e) {
  1410. playerState = 3;
  1411. createEvent(player, pluginMediaElement, 'progress', e);
  1412. });
  1413. pluginMediaElement.pluginElement = container;
  1414. pluginMediaElement.pluginApi = player;
  1415. pluginMediaElement.success(pluginMediaElement, pluginMediaElement.pluginElement);
  1416. });
  1417. }
  1418. else {
  1419. console.warn("You need to include froogaloop for vimeo to work");
  1420. }
  1421. break;
  1422. }
  1423. // hide original element
  1424. htmlMediaElement.style.display = 'none';
  1425. // prevent browser from autoplaying when using a plugin
  1426. htmlMediaElement.removeAttribute('autoplay');
  1427. return pluginMediaElement;
  1428. },
  1429. updateNative: function(playback, options, autoplay, preload) {
  1430. var htmlMediaElement = playback.htmlMediaElement,
  1431. m;
  1432. // add methods to video object to bring it into parity with Flash Object
  1433. for (m in mejs.HtmlMediaElement) {
  1434. htmlMediaElement[m] = mejs.HtmlMediaElement[m];
  1435. }
  1436. /*
  1437. Chrome now supports preload="none"
  1438. if (mejs.MediaFeatures.isChrome) {
  1439. // special case to enforce preload attribute (Chrome doesn't respect this)
  1440. if (preload === 'none' && !autoplay) {
  1441. // forces the browser to stop loading (note: fails in IE9)
  1442. htmlMediaElement.src = '';
  1443. htmlMediaElement.load();
  1444. htmlMediaElement.canceledPreload = true;
  1445. htmlMediaElement.addEventListener('play',function() {
  1446. if (htmlMediaElement.canceledPreload) {
  1447. htmlMediaElement.src = playback.url;
  1448. htmlMediaElement.load();
  1449. htmlMediaElement.play();
  1450. htmlMediaElement.canceledPreload = false;
  1451. }
  1452. }, false);
  1453. // for some reason Chrome forgets how to autoplay sometimes.
  1454. } else if (autoplay) {
  1455. htmlMediaElement.load();
  1456. htmlMediaElement.play();
  1457. }
  1458. }
  1459. */
  1460. // fire success code
  1461. options.success(htmlMediaElement, htmlMediaElement);
  1462. return htmlMediaElement;
  1463. }
  1464. };
  1465. /*
  1466. - test on IE (object vs. embed)
  1467. - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
  1468. - fullscreen?
  1469. */
  1470. // YouTube Flash and Iframe API
  1471. mejs.YouTubeApi = {
  1472. isIframeStarted: false,
  1473. isIframeLoaded: false,
  1474. loadIframeApi: function(yt) {
  1475. if (!this.isIframeStarted) {
  1476. var tag = document.createElement('script');
  1477. tag.src = yt.scheme + "www.youtube.com/player_api";
  1478. var firstScriptTag = document.getElementsByTagName('script')[0];
  1479. firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  1480. this.isIframeStarted = true;
  1481. }
  1482. },
  1483. iframeQueue: [],
  1484. enqueueIframe: function(yt) {
  1485. if (this.isLoaded) {
  1486. this.createIframe(yt);
  1487. } else {
  1488. this.loadIframeApi(yt);
  1489. this.iframeQueue.push(yt);
  1490. }
  1491. },
  1492. createIframe: function(settings) {
  1493. var
  1494. pluginMediaElement = settings.pluginMediaElement,
  1495. defaultVars = {controls:0, wmode:'transparent'},
  1496. player = new YT.Player(settings.containerId, {
  1497. height: settings.height,
  1498. width: settings.width,
  1499. videoId: settings.videoId,
  1500. playerVars: mejs.$.extend({}, defaultVars, settings.variables),
  1501. events: {
  1502. 'onReady': function(e) {
  1503. // wrapper to match
  1504. player.setVideoSize = function(width, height) {
  1505. player.setSize(width, height);
  1506. };
  1507. // hook up iframe object to MEjs
  1508. settings.pluginMediaElement.pluginApi = player;
  1509. settings.pluginMediaElement.pluginElement = document.getElementById(settings.containerId);
  1510. // init mejs
  1511. pluginMediaElement.success(pluginMediaElement, pluginMediaElement.pluginElement);
  1512. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay');
  1513. // create timer
  1514. setInterval(function() {
  1515. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
  1516. }, 250);
  1517. if (typeof pluginMediaElement.attributes.autoplay !== 'undefined') {
  1518. player.playVideo();
  1519. }
  1520. },
  1521. 'onStateChange': function(e) {
  1522. mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
  1523. }
  1524. }
  1525. });
  1526. },
  1527. createEvent: function (player, pluginMediaElement, eventName) {
  1528. var event = {
  1529. type: eventName,
  1530. target: pluginMediaElement
  1531. };
  1532. if (player && player.getDuration) {
  1533. // time
  1534. pluginMediaElement.currentTime = event.currentTime = player.getCurrentTime();
  1535. pluginMediaElement.duration = event.duration = player.getDuration();
  1536. // state
  1537. event.paused = pluginMediaElement.paused;
  1538. event.ended = pluginMediaElement.ended;
  1539. // sound
  1540. event.muted = player.isMuted();
  1541. event.volume = player.getVolume() / 100;
  1542. // progress
  1543. event.bytesTotal = player.getVideoBytesTotal();
  1544. event.bufferedBytes = player.getVideoBytesLoaded();
  1545. // fake the W3C buffered TimeRange
  1546. var bufferedTime = event.bufferedBytes / event.bytesTotal * event.duration;
  1547. event.target.buffered = event.buffered = {
  1548. start: function(index) {
  1549. return 0;
  1550. },
  1551. end: function (index) {
  1552. return bufferedTime;
  1553. },
  1554. length: 1
  1555. };
  1556. }
  1557. // send event up the chain
  1558. pluginMediaElement.dispatchEvent(event);
  1559. },
  1560. iFrameReady: function() {
  1561. this.isLoaded = true;
  1562. this.isIframeLoaded = true;
  1563. while (this.iframeQueue.length > 0) {
  1564. var settings = this.iframeQueue.pop();
  1565. this.createIframe(settings);
  1566. }
  1567. },
  1568. // FLASH!
  1569. flashPlayers: {},
  1570. createFlash: function(settings) {
  1571. this.flashPlayers[settings.pluginId] = settings;
  1572. /*
  1573. settings.container.innerHTML =
  1574. '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + settings.scheme + 'www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0" ' +
  1575. 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
  1576. '<param name="allowScriptAccess" value="sameDomain">' +
  1577. '<param name="wmode" value="transparent">' +
  1578. '</object>';
  1579. */
  1580. var specialIEContainer,
  1581. youtubeUrl = settings.scheme + 'www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0';
  1582. if (mejs.MediaFeatures.isIE) {
  1583. specialIEContainer = document.createElement('div');
  1584. settings.container.appendChild(specialIEContainer);
  1585. specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="' + settings.scheme + 'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
  1586. 'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '" class="mejs-shim">' +
  1587. '<param name="movie" value="' + youtubeUrl + '" />' +
  1588. '<param name="wmode" value="transparent" />' +
  1589. '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '" />' +
  1590. '<param name="allowFullScreen" value="true" />' +
  1591. '</object>';
  1592. } else {
  1593. settings.container.innerHTML =
  1594. '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
  1595. 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
  1596. '<param name="allowScriptAccess" value="' + options.flashScriptAccess + '">' +
  1597. '<param name="wmode" value="transparent">' +
  1598. '</object>';
  1599. }
  1600. },
  1601. flashReady: function(id) {
  1602. var
  1603. settings = this.flashPlayers[id],
  1604. player = document.getElementById(id),
  1605. pluginMediaElement = settings.pluginMediaElement;
  1606. // hook up and return to MediaELementPlayer.success
  1607. pluginMediaElement.pluginApi =
  1608. pluginMediaElement.pluginElement = player;
  1609. settings.success(pluginMediaElement, pluginMediaElement.pluginElement);
  1610. // load the youtube video
  1611. player.cueVideoById(settings.videoId);
  1612. var callbackName = settings.containerId + '_callback';
  1613. window[callbackName] = function(e) {
  1614. mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
  1615. };
  1616. player.addEventListener('onStateChange', callbackName);
  1617. setInterval(function() {
  1618. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
  1619. }, 250);
  1620. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'canplay');
  1621. },
  1622. handleStateChange: function(youTubeState, player, pluginMediaElement) {
  1623. switch (youTubeState) {
  1624. case -1: // not started
  1625. pluginMediaElement.paused = true;
  1626. pluginMediaElement.ended = true;
  1627. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
  1628. //createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
  1629. break;
  1630. case 0:
  1631. pluginMediaElement.paused = false;
  1632. pluginMediaElement.ended = true;
  1633. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
  1634. break;
  1635. case 1:
  1636. pluginMediaElement.paused = false;
  1637. pluginMediaElement.ended = false;
  1638. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
  1639. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
  1640. break;
  1641. case 2:
  1642. pluginMediaElement.paused = true;
  1643. pluginMediaElement.ended = false;
  1644. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
  1645. break;
  1646. case 3: // buffering
  1647. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
  1648. break;
  1649. case 5:
  1650. // cued?
  1651. break;
  1652. }
  1653. }
  1654. }
  1655. // IFRAME
  1656. window.onYouTubePlayerAPIReady = function() {
  1657. mejs.YouTubeApi.iFrameReady();
  1658. };
  1659. // FLASH
  1660. window.onYouTubePlayerReady = function(id) {
  1661. mejs.YouTubeApi.flashReady(id);
  1662. };
  1663. window.mejs = mejs;
  1664. window.MediaElement = mejs.MediaElement;
  1665. /**
  1666. * Localize strings
  1667. *
  1668. * Include translations from JS files and method to pluralize properly strings.
  1669. *
  1670. */
  1671. (function (doc, win, mejs, undefined) {
  1672. var i18n = {
  1673. /**
  1674. * @type {String}
  1675. */
  1676. 'default': 'en',
  1677. /**
  1678. * @type {String[]}
  1679. */
  1680. locale: {
  1681. language: (mejs.i18n && mejs.i18n.locale.language) || '',
  1682. strings: (mejs.i18n && mejs.i18n.locale.strings) || {}
  1683. },
  1684. /**
  1685. * Filters for available languages.
  1686. *
  1687. * This plural forms are grouped in family groups based on
  1688. * https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals#List_of_Plural_Rules
  1689. * with some additions and corrections according to the Localization Guide list
  1690. * (http://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html)
  1691. *
  1692. * Arguments are dynamic following the structure:
  1693. * - argument1 : Number to determine form
  1694. * - argument2...argumentN: Possible matches
  1695. *
  1696. * @type {Function[]}
  1697. */
  1698. pluralForms: [
  1699. // 0: Chinese, Japanese, Korean, Persian, Turkish, Thai, Lao, Aymará,
  1700. // Tibetan, Chiga, Dzongkha, Indonesian, Lojban, Georgian, Kazakh, Khmer, Kyrgyz, Malay,
  1701. // Burmese, Yakut, Sundanese, Tatar, Uyghur, Vietnamese, Wolof
  1702. function () {
  1703. return arguments[1];
  1704. },
  1705. // 1: Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish, Estonian, Finnish,
  1706. // Hungarian, Basque, Greek, Hebrew, Italian, Portuguese, Spanish, Catalan, Afrikaans,
  1707. // Angika, Assamese, Asturian, Azerbaijani, Bulgarian, Bengali, Bodo, Aragonese, Dogri,
  1708. // Esperanto, Argentinean Spanish, Fulah, Friulian, Galician, Gujarati, Hausa,
  1709. // Hindi, Chhattisgarhi, Armenian, Interlingua, Greenlandic, Kannada, Kurdish, Letzeburgesch,
  1710. // Maithili, Malayalam, Mongolian, Manipuri, Marathi, Nahuatl, Neapolitan, Norwegian Bokmal,
  1711. // Nepali, Norwegian Nynorsk, Norwegian (old code), Northern Sotho, Oriya, Punjabi, Papiamento,
  1712. // Piemontese, Pashto, Romansh, Kinyarwanda, Santali, Scots, Sindhi, Northern Sami, Sinhala,
  1713. // Somali, Songhay, Albanian, Swahili, Tamil, Telugu, Turkmen, Urdu, Yoruba
  1714. function () {
  1715. var args = arguments;
  1716. if (args[0] === 1) {
  1717. return args[1];
  1718. } else {
  1719. return args[2];
  1720. }
  1721. },
  1722. // 2: French, Brazilian Portuguese, Acholi, Akan, Amharic, Mapudungun, Breton, Filipino,
  1723. // Gun, Lingala, Mauritian Creole, Malagasy, Maori, Occitan, Tajik, Tigrinya, Uzbek, Walloon
  1724. function () {
  1725. var args = arguments;
  1726. if ([0, 1].indexOf(args[0]) > -1) {
  1727. return args[1];
  1728. } else {
  1729. return args[2];
  1730. }
  1731. },
  1732. // 3: Latvian
  1733. function () {
  1734. var args = arguments;
  1735. if (args[0] % 10 === 1 && args[0] % 100 !== 11) {
  1736. return args[1];
  1737. } else if (args[0] !== 0) {
  1738. return args[2];
  1739. } else {
  1740. return args[3];
  1741. }
  1742. },
  1743. // 4: Scottish Gaelic
  1744. function () {
  1745. var args = arguments;
  1746. if (args[0] === 1 || args[0] === 11) {
  1747. return args[1];
  1748. } else if (args[0] === 2 || args[0] === 12) {
  1749. return args[2];
  1750. } else if (args[0] > 2 && args[0] < 20) {
  1751. return args[3];
  1752. } else {
  1753. return args[4];
  1754. }
  1755. },
  1756. // 5: Romanian
  1757. function () {
  1758. if (args[0] === 1) {
  1759. return args[1];
  1760. } else if (args[0] === 0 || (args[0] % 100 > 0 && args[0] % 100 < 20)) {
  1761. return args[2];
  1762. } else {
  1763. return args[3];
  1764. }
  1765. },
  1766. // 6: Lithuanian
  1767. function () {
  1768. var args = arguments;
  1769. if (args[0] % 10 === 1 && args[0] % 100 !== 11) {
  1770. return args[1];
  1771. } else if (args[0] % 10 >= 2 && (args[0] % 100 < 10 || args[0] % 100 >= 20)) {
  1772. return args[2];
  1773. } else {
  1774. return [3];
  1775. }
  1776. },
  1777. // 7: Belarusian, Bosnian, Croatian, Serbian, Russian, Ukrainian
  1778. function () {
  1779. var args = arguments;
  1780. if (args[0] % 10 === 1 && args[0] % 100 !== 11) {
  1781. return args[1];
  1782. } else if (args[0] % 10 >= 2 && args[0] % 10 <= 4 && (args[0] % 100 < 10 || args[0] % 100 >= 20)) {
  1783. return args[2];
  1784. } else {
  1785. return args[3];
  1786. }
  1787. },
  1788. // 8: Slovak, Czech
  1789. function () {
  1790. var args = arguments;
  1791. if (args[0] === 1) {
  1792. return args[1];
  1793. } else if (args[0] >= 2 && args[0] <= 4) {
  1794. return args[2];
  1795. } else {
  1796. return args[3];
  1797. }
  1798. },
  1799. // 9: Polish
  1800. function () {
  1801. var args = arguments;
  1802. if (args[0] === 1) {
  1803. return args[1];
  1804. } else if (args[0] % 10 >= 2 && args[0] % 10 <= 4 && (args[0] % 100 < 10 || args[0] % 100 >= 20)) {
  1805. return args[2];
  1806. } else {
  1807. return args[3];
  1808. }
  1809. },
  1810. // 10: Slovenian
  1811. function () {
  1812. var args = arguments;
  1813. if (args[0] % 100 === 1) {
  1814. return args[2];
  1815. } else if (args[0] % 100 === 2) {
  1816. return args[3];
  1817. } else if (args[0] % 100 === 3 || args[0] % 100 === 4) {
  1818. return args[4];
  1819. } else {
  1820. return args[1];
  1821. }
  1822. },
  1823. // 11: Irish Gaelic
  1824. function () {
  1825. var args = arguments;
  1826. if (args[0] === 1) {
  1827. return args[1];
  1828. } else if (args[0] === 2) {
  1829. return args[2];
  1830. } else if (args[0] > 2 && args[0] < 7) {
  1831. return args[3];
  1832. } else if (args[0] > 6 && args[0] < 11) {
  1833. return args[4];
  1834. } else {
  1835. return args[5];
  1836. }
  1837. },
  1838. // 12: Arabic
  1839. function () {
  1840. var args = arguments;
  1841. if (args[0] === 0) {
  1842. return args[1];
  1843. } else if (args[0] === 1) {
  1844. return args[2];
  1845. } else if (args[0] === 2) {
  1846. return args[3];
  1847. } else if (args[0] % 100 >= 3 && args[0] % 100 <= 10) {
  1848. return args[4];
  1849. } else if (args[0] % 100 >= 11) {
  1850. return args[5];
  1851. } else {
  1852. return args[6];
  1853. }
  1854. },
  1855. // 13: Maltese
  1856. function () {
  1857. var args = arguments;
  1858. if (args[0] === 1) {
  1859. return args[1];
  1860. } else if (args[0] === 0 || (args[0] % 100 > 1 && args[0] % 100 < 11)) {
  1861. return args[2];
  1862. } else if (args[0] % 100 > 10 && args[0] % 100 < 20) {
  1863. return args[3];
  1864. } else {
  1865. return args[4];
  1866. }
  1867. },
  1868. // 14: Macedonian
  1869. function () {
  1870. var args = arguments;
  1871. if (args[0] % 10 === 1) {
  1872. return args[1];
  1873. } else if (args[0] % 10 === 2) {
  1874. return args[2];
  1875. } else {
  1876. return args[3];
  1877. }
  1878. },
  1879. // 15: Icelandic
  1880. function () {
  1881. var args = arguments;
  1882. if (args[0] !== 11 && args[0] % 10 === 1) {
  1883. return args[1];
  1884. } else {
  1885. return args[2];
  1886. }
  1887. },
  1888. // New additions
  1889. // 16: Kashubian
  1890. // Note: in https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals#List_of_Plural_Rules
  1891. // Breton is listed as #16 but in the Localization Guide it belongs to the group 2
  1892. function () {
  1893. var args = arguments;
  1894. if (args[0] === 1) {
  1895. return args[1];
  1896. } else if (args[0] % 10 >= 2 && args[0] % 10 <= 4 && (args[0] % 100 < 10 || args[0] % 100 >= 20)) {
  1897. return args[2];
  1898. } else {
  1899. return args[3];
  1900. }
  1901. },
  1902. // 17: Welsh
  1903. function () {
  1904. var args = arguments;
  1905. if (args[0] === 1) {
  1906. return args[1];
  1907. } else if (args[0] === 2) {
  1908. return args[2];
  1909. } else if (args[0] !== 8 && args[0] !== 11) {
  1910. return args[3];
  1911. } else {
  1912. return args[4];
  1913. }
  1914. },
  1915. // 18: Javanese
  1916. function () {
  1917. var args = arguments;
  1918. if (args[0] === 0) {
  1919. return args[1];
  1920. } else {
  1921. return args[2];
  1922. }
  1923. },
  1924. // 19: Cornish
  1925. function () {
  1926. var args = arguments;
  1927. if (args[0] === 1) {
  1928. return args[1];
  1929. } else if (args[0] === 2) {
  1930. return args[2];
  1931. } else if (args[0] === 3) {
  1932. return args[3];
  1933. } else {
  1934. return args[4];
  1935. }
  1936. },
  1937. // 20: Mandinka
  1938. function () {
  1939. var args = arguments;
  1940. if (args[0] === 0) {
  1941. return args[1];
  1942. } else if (args[0] === 1) {
  1943. return args[2];
  1944. } else {
  1945. return args[3];
  1946. }
  1947. }
  1948. ],
  1949. /**
  1950. * Get specified language
  1951. *
  1952. */
  1953. getLanguage: function () {
  1954. var language = i18n.locale.language || i18n['default'];
  1955. return /^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/.exec(language) ? language : i18n['default'];
  1956. },
  1957. /**
  1958. * Translate a string to a specified language, including optionally a number to pluralize translation
  1959. *
  1960. * @param {String} message
  1961. * @param {Number} pluralParam
  1962. * @return {String}
  1963. */
  1964. t: function (message, pluralParam) {
  1965. if (typeof message === 'string' && message.length) {
  1966. var
  1967. language = i18n.getLanguage(),
  1968. str,
  1969. pluralForm,
  1970. /**
  1971. * Modify string using algorithm to detect plural forms.
  1972. *
  1973. * @private
  1974. * @see http://stackoverflow.com/questions/1353408/messageformat-in-javascript-parameters-in-localized-ui-strings
  1975. * @param {String|String[]} input - String or array of strings to pick the plural form
  1976. * @param {Number} number - Number to determine the proper plural form
  1977. * @param {Number} form - Number of language family to apply plural form
  1978. * @return {String}
  1979. */
  1980. plural = function (input, number, form) {
  1981. if (typeof input !== 'object' || typeof number !== 'number' || typeof form !== 'number') {
  1982. return input;
  1983. }
  1984. if (typeof input === 'string') {
  1985. return input;
  1986. }
  1987. // Perform plural form or return original text
  1988. return i18n.pluralForms[form].apply(null, [number].concat(input));
  1989. },
  1990. /**
  1991. *
  1992. * @param {String} input
  1993. * @return {String}
  1994. */
  1995. escapeHTML = function (input) {
  1996. var map = {
  1997. '&': '&amp;',
  1998. '<': '&lt;',
  1999. '>': '&gt;',
  2000. '"': '&quot;'
  2001. };
  2002. return input.replace(/[&<>"]/g, function(c) {
  2003. return map[c];
  2004. });
  2005. }
  2006. ;
  2007. // Fetch the localized version of the string
  2008. if (i18n.locale.strings && i18n.locale.strings[language]) {
  2009. str = i18n.locale.strings[language][message];
  2010. if (typeof pluralParam === 'number') {
  2011. pluralForm = i18n.locale.strings[language]['mejs.plural-form'];
  2012. str = plural.apply(null, [str, pluralParam, pluralForm]);
  2013. }
  2014. }
  2015. // Fallback to default language if requested uid is not translated
  2016. if (!str && i18n.locale.strings && i18n.locale.strings[i18n['default']]) {
  2017. str = i18n.locale.strings[i18n['default']][message];
  2018. if (typeof pluralParam === 'number') {
  2019. pluralForm = i18n.locale.strings[i18n['default']]['mejs.plural-form'];
  2020. str = plural.apply(null, [str, pluralParam, pluralForm]);
  2021. }
  2022. }
  2023. // As a last resort, use the requested uid, to mimic original behavior of i18n utils (in which uid was the english text)
  2024. str = str || message;
  2025. // Replace token
  2026. if (typeof pluralParam === 'number') {
  2027. str = str.replace('%1', pluralParam);
  2028. }
  2029. return escapeHTML(str);
  2030. }
  2031. return message;
  2032. }
  2033. };
  2034. // i18n fixes for compatibility with WordPress
  2035. if (typeof mejsL10n !== 'undefined') {
  2036. i18n.locale.language = mejsL10n.language;
  2037. }
  2038. // Register variable
  2039. mejs.i18n = i18n;
  2040. }(document, window, mejs));
  2041. // i18n fixes for compatibility with WordPress
  2042. ;(function (mejs, undefined) {
  2043. "use strict";
  2044. if (typeof mejsL10n !== 'undefined') {
  2045. mejs[mejsL10n.lang] = mejsL10n.strings;
  2046. }
  2047. }(mejs.i18n.locale.strings));
  2048. /*!
  2049. * This is a i18n.locale language object.
  2050. *
  2051. * English; This can serve as a template for other languages to translate
  2052. *
  2053. * @author
  2054. * TBD
  2055. * Sascha Greuel (Twitter: @SoftCreatR)
  2056. *
  2057. * @see
  2058. * me-i18n.js
  2059. *
  2060. * @params
  2061. * - exports - CommonJS, window ..
  2062. */
  2063. (function (exports) {
  2064. "use strict";
  2065. if (exports.en === undefined) {
  2066. exports.en = {
  2067. "mejs.plural-form": 1,
  2068. // me-shim
  2069. "mejs.download-file": "Download File",
  2070. // mep-feature-contextmenu
  2071. "mejs.fullscreen-off": "Turn off Fullscreen",
  2072. "mejs.fullscreen-on": "Go Fullscreen",
  2073. "mejs.download-video": "Download Video",
  2074. // mep-feature-fullscreen
  2075. "mejs.fullscreen": "Fullscreen",
  2076. // mep-feature-jumpforward
  2077. "mejs.time-jump-forward": ["Jump forward 1 second", "Jump forward %1 seconds"],
  2078. // mep-feature-playpause
  2079. "mejs.play": "Play",
  2080. "mejs.pause": "Pause",
  2081. // mep-feature-postroll
  2082. "mejs.close": "Close",
  2083. // mep-feature-progress
  2084. "mejs.time-slider": "Time Slider",
  2085. "mejs.time-help-text": "Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.",
  2086. // mep-feature-skipback
  2087. "mejs.time-skip-back": ["Skip back 1 second", "Skip back %1 seconds"],
  2088. // mep-feature-tracks
  2089. "mejs.captions-subtitles": "Captions/Subtitles",
  2090. "mejs.none": "None",
  2091. // mep-feature-volume
  2092. "mejs.mute-toggle": "Mute Toggle",
  2093. "mejs.volume-help-text": "Use Up/Down Arrow keys to increase or decrease volume.",
  2094. "mejs.unmute": "Unmute",
  2095. "mejs.mute": "Mute",
  2096. "mejs.volume-slider": "Volume Slider",
  2097. // mep-player
  2098. "mejs.video-player": "Video Player",
  2099. "mejs.audio-player": "Audio Player",
  2100. // mep-feature-ads
  2101. "mejs.ad-skip": "Skip ad",
  2102. "mejs.ad-skip-info": ["Skip in 1 second", "Skip in %1 seconds"],
  2103. // mep-feature-sourcechooser
  2104. "mejs.source-chooser": "Source Chooser"
  2105. };
  2106. }
  2107. }(mejs.i18n.locale.strings));