mediaelement.js 60 KB

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