mediaelement-and-player.js 138 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050
  1. /*!
  2. * MediaElement.js
  3. * HTML5 <video> and <audio> shim and player
  4. * http://mediaelementjs.com/
  5. *
  6. * Creates a JavaScript object that mimics HTML5 MediaElement API
  7. * for browsers that don't understand HTML5 or can't play the provided codec
  8. * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
  9. *
  10. * Copyright 2010-2013, John Dyer (http://j.hn)
  11. * License: MIT
  12. *
  13. */
  14. // Namespace
  15. var mejs = mejs || {};
  16. // version number
  17. mejs.version = '2.13.2';
  18. // player number (for missing, same id attr)
  19. mejs.meIndex = 0;
  20. // media types accepted by plugins
  21. mejs.plugins = {
  22. silverlight: [
  23. {version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
  24. ],
  25. flash: [
  26. {version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube']}
  27. //,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
  28. ],
  29. youtube: [
  30. {version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']}
  31. ],
  32. vimeo: [
  33. {version: null, types: ['video/vimeo', 'video/x-vimeo']}
  34. ]
  35. };
  36. /*
  37. Utility methods
  38. */
  39. mejs.Utility = {
  40. encodeUrl: function(url) {
  41. return encodeURIComponent(url); //.replace(/\?/gi,'%3F').replace(/=/gi,'%3D').replace(/&/gi,'%26');
  42. },
  43. escapeHTML: function(s) {
  44. return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('"').join('&quot;');
  45. },
  46. absolutizeUrl: function(url) {
  47. var el = document.createElement('div');
  48. el.innerHTML = '<a href="' + this.escapeHTML(url) + '">x</a>';
  49. return el.firstChild.href;
  50. },
  51. getScriptPath: function(scriptNames) {
  52. var
  53. i = 0,
  54. j,
  55. codePath = '',
  56. testname = '',
  57. slashPos,
  58. filenamePos,
  59. scriptUrl,
  60. scriptPath,
  61. scriptFilename,
  62. scripts = document.getElementsByTagName('script'),
  63. il = scripts.length,
  64. jl = scriptNames.length;
  65. // go through all <script> tags
  66. for (; i < il; i++) {
  67. scriptUrl = scripts[i].src;
  68. slashPos = scriptUrl.lastIndexOf('/');
  69. if (slashPos > -1) {
  70. scriptFilename = scriptUrl.substring(slashPos + 1);
  71. scriptPath = scriptUrl.substring(0, slashPos + 1);
  72. } else {
  73. scriptFilename = scriptUrl;
  74. scriptPath = '';
  75. }
  76. // see if any <script> tags have a file name that matches the
  77. for (j = 0; j < jl; j++) {
  78. testname = scriptNames[j];
  79. filenamePos = scriptFilename.indexOf(testname);
  80. if (filenamePos > -1) {
  81. codePath = scriptPath;
  82. break;
  83. }
  84. }
  85. // if we found a path, then break and return it
  86. if (codePath !== '') {
  87. break;
  88. }
  89. }
  90. // send the best path back
  91. return codePath;
  92. },
  93. secondsToTimeCode: function(time, forceHours, showFrameCount, fps) {
  94. //add framecount
  95. if (typeof showFrameCount == 'undefined') {
  96. showFrameCount=false;
  97. } else if(typeof fps == 'undefined') {
  98. fps = 25;
  99. }
  100. var hours = Math.floor(time / 3600) % 24,
  101. minutes = Math.floor(time / 60) % 60,
  102. seconds = Math.floor(time % 60),
  103. frames = Math.floor(((time % 1)*fps).toFixed(3)),
  104. result =
  105. ( (forceHours || hours > 0) ? (hours < 10 ? '0' + hours : hours) + ':' : '')
  106. + (minutes < 10 ? '0' + minutes : minutes) + ':'
  107. + (seconds < 10 ? '0' + seconds : seconds)
  108. + ((showFrameCount) ? ':' + (frames < 10 ? '0' + frames : frames) : '');
  109. return result;
  110. },
  111. timeCodeToSeconds: function(hh_mm_ss_ff, forceHours, showFrameCount, fps){
  112. if (typeof showFrameCount == 'undefined') {
  113. showFrameCount=false;
  114. } else if(typeof fps == 'undefined') {
  115. fps = 25;
  116. }
  117. var tc_array = hh_mm_ss_ff.split(":"),
  118. tc_hh = parseInt(tc_array[0], 10),
  119. tc_mm = parseInt(tc_array[1], 10),
  120. tc_ss = parseInt(tc_array[2], 10),
  121. tc_ff = 0,
  122. tc_in_seconds = 0;
  123. if (showFrameCount) {
  124. tc_ff = parseInt(tc_array[3])/fps;
  125. }
  126. tc_in_seconds = ( tc_hh * 3600 ) + ( tc_mm * 60 ) + tc_ss + tc_ff;
  127. return tc_in_seconds;
  128. },
  129. convertSMPTEtoSeconds: function (SMPTE) {
  130. if (typeof SMPTE != 'string')
  131. return false;
  132. SMPTE = SMPTE.replace(',', '.');
  133. var secs = 0,
  134. decimalLen = (SMPTE.indexOf('.') != -1) ? SMPTE.split('.')[1].length : 0,
  135. multiplier = 1;
  136. SMPTE = SMPTE.split(':').reverse();
  137. for (var i = 0; i < SMPTE.length; i++) {
  138. multiplier = 1;
  139. if (i > 0) {
  140. multiplier = Math.pow(60, i);
  141. }
  142. secs += Number(SMPTE[i]) * multiplier;
  143. }
  144. return Number(secs.toFixed(decimalLen));
  145. },
  146. /* borrowed from SWFObject: http://code.google.com/p/swfobject/source/browse/trunk/swfobject/src/swfobject.js#474 */
  147. removeSwf: function(id) {
  148. var obj = document.getElementById(id);
  149. if (obj && /object|embed/i.test(obj.nodeName)) {
  150. if (mejs.MediaFeatures.isIE) {
  151. obj.style.display = "none";
  152. (function(){
  153. if (obj.readyState == 4) {
  154. mejs.Utility.removeObjectInIE(id);
  155. } else {
  156. setTimeout(arguments.callee, 10);
  157. }
  158. })();
  159. } else {
  160. obj.parentNode.removeChild(obj);
  161. }
  162. }
  163. },
  164. removeObjectInIE: function(id) {
  165. var obj = document.getElementById(id);
  166. if (obj) {
  167. for (var i in obj) {
  168. if (typeof obj[i] == "function") {
  169. obj[i] = null;
  170. }
  171. }
  172. obj.parentNode.removeChild(obj);
  173. }
  174. }
  175. };
  176. // Core detector, plugins are added below
  177. mejs.PluginDetector = {
  178. // main public function to test a plug version number PluginDetector.hasPluginVersion('flash',[9,0,125]);
  179. hasPluginVersion: function(plugin, v) {
  180. var pv = this.plugins[plugin];
  181. v[1] = v[1] || 0;
  182. v[2] = v[2] || 0;
  183. return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
  184. },
  185. // cached values
  186. nav: window.navigator,
  187. ua: window.navigator.userAgent.toLowerCase(),
  188. // stored version numbers
  189. plugins: [],
  190. // runs detectPlugin() and stores the version number
  191. addPlugin: function(p, pluginName, mimeType, activeX, axDetect) {
  192. this.plugins[p] = this.detectPlugin(pluginName, mimeType, activeX, axDetect);
  193. },
  194. // get the version number from the mimetype (all but IE) or ActiveX (IE)
  195. detectPlugin: function(pluginName, mimeType, activeX, axDetect) {
  196. var version = [0,0,0],
  197. description,
  198. i,
  199. ax;
  200. // Firefox, Webkit, Opera
  201. if (typeof(this.nav.plugins) != 'undefined' && typeof this.nav.plugins[pluginName] == 'object') {
  202. description = this.nav.plugins[pluginName].description;
  203. if (description && !(typeof this.nav.mimeTypes != 'undefined' && this.nav.mimeTypes[mimeType] && !this.nav.mimeTypes[mimeType].enabledPlugin)) {
  204. version = description.replace(pluginName, '').replace(/^\s+/,'').replace(/\sr/gi,'.').split('.');
  205. for (i=0; i<version.length; i++) {
  206. version[i] = parseInt(version[i].match(/\d+/), 10);
  207. }
  208. }
  209. // Internet Explorer / ActiveX
  210. } else if (typeof(window.ActiveXObject) != 'undefined') {
  211. try {
  212. ax = new ActiveXObject(activeX);
  213. if (ax) {
  214. version = axDetect(ax);
  215. }
  216. }
  217. catch (e) { }
  218. }
  219. return version;
  220. }
  221. };
  222. // Add Flash detection
  223. mejs.PluginDetector.addPlugin('flash','Shockwave Flash','application/x-shockwave-flash','ShockwaveFlash.ShockwaveFlash', function(ax) {
  224. // adapted from SWFObject
  225. var version = [],
  226. d = ax.GetVariable("$version");
  227. if (d) {
  228. d = d.split(" ")[1].split(",");
  229. version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  230. }
  231. return version;
  232. });
  233. // Add Silverlight detection
  234. mejs.PluginDetector.addPlugin('silverlight','Silverlight Plug-In','application/x-silverlight-2','AgControl.AgControl', function (ax) {
  235. // Silverlight cannot report its version number to IE
  236. // but it does have a isVersionSupported function, so we have to loop through it to get a version number.
  237. // adapted from http://www.silverlightversion.com/
  238. var v = [0,0,0,0],
  239. loopMatch = function(ax, v, i, n) {
  240. while(ax.isVersionSupported(v[0]+ "."+ v[1] + "." + v[2] + "." + v[3])){
  241. v[i]+=n;
  242. }
  243. v[i] -= n;
  244. };
  245. loopMatch(ax, v, 0, 1);
  246. loopMatch(ax, v, 1, 1);
  247. loopMatch(ax, v, 2, 10000); // the third place in the version number is usually 5 digits (4.0.xxxxx)
  248. loopMatch(ax, v, 2, 1000);
  249. loopMatch(ax, v, 2, 100);
  250. loopMatch(ax, v, 2, 10);
  251. loopMatch(ax, v, 2, 1);
  252. loopMatch(ax, v, 3, 1);
  253. return v;
  254. });
  255. // add adobe acrobat
  256. /*
  257. PluginDetector.addPlugin('acrobat','Adobe Acrobat','application/pdf','AcroPDF.PDF', function (ax) {
  258. var version = [],
  259. d = ax.GetVersions().split(',')[0].split('=')[1].split('.');
  260. if (d) {
  261. version = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
  262. }
  263. return version;
  264. });
  265. */
  266. // necessary detection (fixes for <IE9)
  267. mejs.MediaFeatures = {
  268. init: function() {
  269. var
  270. t = this,
  271. d = document,
  272. nav = mejs.PluginDetector.nav,
  273. ua = mejs.PluginDetector.ua.toLowerCase(),
  274. i,
  275. v,
  276. html5Elements = ['source','track','audio','video'];
  277. // detect browsers (only the ones that have some kind of quirk we need to work around)
  278. t.isiPad = (ua.match(/ipad/i) !== null);
  279. t.isiPhone = (ua.match(/iphone/i) !== null);
  280. t.isiOS = t.isiPhone || t.isiPad;
  281. t.isAndroid = (ua.match(/android/i) !== null);
  282. t.isBustedAndroid = (ua.match(/android 2\.[12]/) !== null);
  283. t.isBustedNativeHTTPS = (location.protocol === 'https:' && (ua.match(/android [12]\./) !== null || ua.match(/macintosh.* version.* safari/) !== null));
  284. t.isIE = (nav.appName.toLowerCase().indexOf("microsoft") != -1 || nav.appName.toLowerCase().match(/trident/gi) !== null);
  285. t.isChrome = (ua.match(/chrome/gi) !== null);
  286. t.isFirefox = (ua.match(/firefox/gi) !== null);
  287. t.isWebkit = (ua.match(/webkit/gi) !== null);
  288. t.isGecko = (ua.match(/gecko/gi) !== null) && !t.isWebkit && !t.isIE;
  289. t.isOpera = (ua.match(/opera/gi) !== null);
  290. t.hasTouch = ('ontouchstart' in window); // && window.ontouchstart != null); // this breaks iOS 7
  291. // borrowed from Modernizr
  292. t.svg = !! document.createElementNS &&
  293. !! document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect;
  294. // create HTML5 media elements for IE before 9, get a <video> element for fullscreen detection
  295. for (i=0; i<html5Elements.length; i++) {
  296. v = document.createElement(html5Elements[i]);
  297. }
  298. t.supportsMediaTag = (typeof v.canPlayType !== 'undefined' || t.isBustedAndroid);
  299. // Fix for IE9 on Windows 7N / Windows 7KN (Media Player not installer)
  300. try{
  301. v.canPlayType("video/mp4");
  302. }catch(e){
  303. t.supportsMediaTag = false;
  304. }
  305. // detect native JavaScript fullscreen (Safari/Firefox only, Chrome still fails)
  306. // iOS
  307. t.hasSemiNativeFullScreen = (typeof v.webkitEnterFullscreen !== 'undefined');
  308. // W3C
  309. t.hasNativeFullscreen = (typeof v.requestFullscreen !== 'undefined');
  310. // webkit/firefox/IE11+
  311. t.hasWebkitNativeFullScreen = (typeof v.webkitRequestFullScreen !== 'undefined');
  312. t.hasMozNativeFullScreen = (typeof v.mozRequestFullScreen !== 'undefined');
  313. t.hasMsNativeFullScreen = (typeof v.msRequestFullscreen !== 'undefined');
  314. t.hasTrueNativeFullScreen = (t.hasWebkitNativeFullScreen || t.hasMozNativeFullScreen || t.hasMsNativeFullScreen);
  315. t.nativeFullScreenEnabled = t.hasTrueNativeFullScreen;
  316. // Enabled?
  317. if (t.hasMozNativeFullScreen) {
  318. t.nativeFullScreenEnabled = document.mozFullScreenEnabled;
  319. } else if (t.hasMsNativeFullScreen) {
  320. t.nativeFullScreenEnabled = document.msFullscreenEnabled;
  321. }
  322. if (t.isChrome) {
  323. t.hasSemiNativeFullScreen = false;
  324. }
  325. if (t.hasTrueNativeFullScreen) {
  326. t.fullScreenEventName = '';
  327. if (t.hasWebkitNativeFullScreen) {
  328. t.fullScreenEventName = 'webkitfullscreenchange';
  329. } else if (t.hasMozNativeFullScreen) {
  330. t.fullScreenEventName = 'mozfullscreenchange';
  331. } else if (t.hasMsNativeFullScreen) {
  332. t.fullScreenEventName = 'MSFullscreenChange';
  333. }
  334. t.isFullScreen = function() {
  335. if (v.mozRequestFullScreen) {
  336. return d.mozFullScreen;
  337. } else if (v.webkitRequestFullScreen) {
  338. return d.webkitIsFullScreen;
  339. } else if (v.hasMsNativeFullScreen) {
  340. return d.msFullscreenElement !== null;
  341. }
  342. }
  343. t.requestFullScreen = function(el) {
  344. if (t.hasWebkitNativeFullScreen) {
  345. el.webkitRequestFullScreen();
  346. } else if (t.hasMozNativeFullScreen) {
  347. el.mozRequestFullScreen();
  348. } else if (t.hasMsNativeFullScreen) {
  349. el.msRequestFullscreen();
  350. }
  351. }
  352. t.cancelFullScreen = function() {
  353. if (t.hasWebkitNativeFullScreen) {
  354. document.webkitCancelFullScreen();
  355. } else if (t.hasMozNativeFullScreen) {
  356. document.mozCancelFullScreen();
  357. } else if (t.hasMsNativeFullScreen) {
  358. document.msExitFullscreen();
  359. }
  360. }
  361. }
  362. // OS X 10.5 can't do this even if it says it can :(
  363. if (t.hasSemiNativeFullScreen && ua.match(/mac os x 10_5/i)) {
  364. t.hasNativeFullScreen = false;
  365. t.hasSemiNativeFullScreen = false;
  366. }
  367. }
  368. };
  369. mejs.MediaFeatures.init();
  370. /*
  371. extension methods to <video> or <audio> object to bring it into parity with PluginMediaElement (see below)
  372. */
  373. mejs.HtmlMediaElement = {
  374. pluginType: 'native',
  375. isFullScreen: false,
  376. setCurrentTime: function (time) {
  377. this.currentTime = time;
  378. },
  379. setMuted: function (muted) {
  380. this.muted = muted;
  381. },
  382. setVolume: function (volume) {
  383. this.volume = volume;
  384. },
  385. // for parity with the plugin versions
  386. stop: function () {
  387. this.pause();
  388. },
  389. // This can be a url string
  390. // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
  391. setSrc: function (url) {
  392. // Fix for IE9 which can't set .src when there are <source> elements. Awesome, right?
  393. var
  394. existingSources = this.getElementsByTagName('source');
  395. while (existingSources.length > 0){
  396. this.removeChild(existingSources[0]);
  397. }
  398. if (typeof url == 'string') {
  399. this.src = url;
  400. } else {
  401. var i, media;
  402. for (i=0; i<url.length; i++) {
  403. media = url[i];
  404. if (this.canPlayType(media.type)) {
  405. this.src = media.src;
  406. break;
  407. }
  408. }
  409. }
  410. },
  411. setVideoSize: function (width, height) {
  412. this.width = width;
  413. this.height = height;
  414. }
  415. };
  416. /*
  417. Mimics the <video/audio> element by calling Flash's External Interface or Silverlights [ScriptableMember]
  418. */
  419. mejs.PluginMediaElement = function (pluginid, pluginType, mediaUrl) {
  420. this.id = pluginid;
  421. this.pluginType = pluginType;
  422. this.src = mediaUrl;
  423. this.events = {};
  424. this.attributes = {};
  425. };
  426. // JavaScript values and ExternalInterface methods that match HTML5 video properties methods
  427. // http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/FLVPlayback.html
  428. // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
  429. mejs.PluginMediaElement.prototype = {
  430. // special
  431. pluginElement: null,
  432. pluginType: '',
  433. isFullScreen: false,
  434. // not implemented :(
  435. playbackRate: -1,
  436. defaultPlaybackRate: -1,
  437. seekable: [],
  438. played: [],
  439. // HTML5 read-only properties
  440. paused: true,
  441. ended: false,
  442. seeking: false,
  443. duration: 0,
  444. error: null,
  445. tagName: '',
  446. // HTML5 get/set properties, but only set (updated by event handlers)
  447. muted: false,
  448. volume: 1,
  449. currentTime: 0,
  450. // HTML5 methods
  451. play: function () {
  452. if (this.pluginApi != null) {
  453. if (this.pluginType == 'youtube') {
  454. this.pluginApi.playVideo();
  455. } else {
  456. this.pluginApi.playMedia();
  457. }
  458. this.paused = false;
  459. }
  460. },
  461. load: function () {
  462. if (this.pluginApi != null) {
  463. if (this.pluginType == 'youtube') {
  464. } else {
  465. this.pluginApi.loadMedia();
  466. }
  467. this.paused = false;
  468. }
  469. },
  470. pause: function () {
  471. if (this.pluginApi != null) {
  472. if (this.pluginType == 'youtube') {
  473. this.pluginApi.pauseVideo();
  474. } else {
  475. this.pluginApi.pauseMedia();
  476. }
  477. this.paused = true;
  478. }
  479. },
  480. stop: function () {
  481. if (this.pluginApi != null) {
  482. if (this.pluginType == 'youtube') {
  483. this.pluginApi.stopVideo();
  484. } else {
  485. this.pluginApi.stopMedia();
  486. }
  487. this.paused = true;
  488. }
  489. },
  490. canPlayType: function(type) {
  491. var i,
  492. j,
  493. pluginInfo,
  494. pluginVersions = mejs.plugins[this.pluginType];
  495. for (i=0; i<pluginVersions.length; i++) {
  496. pluginInfo = pluginVersions[i];
  497. // test if user has the correct plugin version
  498. if (mejs.PluginDetector.hasPluginVersion(this.pluginType, pluginInfo.version)) {
  499. // test for plugin playback types
  500. for (j=0; j<pluginInfo.types.length; j++) {
  501. // find plugin that can play the type
  502. if (type == pluginInfo.types[j]) {
  503. return 'probably';
  504. }
  505. }
  506. }
  507. }
  508. return '';
  509. },
  510. positionFullscreenButton: function(x,y,visibleAndAbove) {
  511. if (this.pluginApi != null && this.pluginApi.positionFullscreenButton) {
  512. this.pluginApi.positionFullscreenButton(Math.floor(x),Math.floor(y),visibleAndAbove);
  513. }
  514. },
  515. hideFullscreenButton: function() {
  516. if (this.pluginApi != null && this.pluginApi.hideFullscreenButton) {
  517. this.pluginApi.hideFullscreenButton();
  518. }
  519. },
  520. // custom methods since not all JavaScript implementations support get/set
  521. // This can be a url string
  522. // or an array [{src:'file.mp4',type:'video/mp4'},{src:'file.webm',type:'video/webm'}]
  523. setSrc: function (url) {
  524. if (typeof url == 'string') {
  525. this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(url));
  526. this.src = mejs.Utility.absolutizeUrl(url);
  527. } else {
  528. var i, media;
  529. for (i=0; i<url.length; i++) {
  530. media = url[i];
  531. if (this.canPlayType(media.type)) {
  532. this.pluginApi.setSrc(mejs.Utility.absolutizeUrl(media.src));
  533. this.src = mejs.Utility.absolutizeUrl(url);
  534. break;
  535. }
  536. }
  537. }
  538. },
  539. setCurrentTime: function (time) {
  540. if (this.pluginApi != null) {
  541. if (this.pluginType == 'youtube') {
  542. this.pluginApi.seekTo(time);
  543. } else {
  544. this.pluginApi.setCurrentTime(time);
  545. }
  546. this.currentTime = time;
  547. }
  548. },
  549. setVolume: function (volume) {
  550. if (this.pluginApi != null) {
  551. // same on YouTube and MEjs
  552. if (this.pluginType == 'youtube') {
  553. this.pluginApi.setVolume(volume * 100);
  554. } else {
  555. this.pluginApi.setVolume(volume);
  556. }
  557. this.volume = volume;
  558. }
  559. },
  560. setMuted: function (muted) {
  561. if (this.pluginApi != null) {
  562. if (this.pluginType == 'youtube') {
  563. if (muted) {
  564. this.pluginApi.mute();
  565. } else {
  566. this.pluginApi.unMute();
  567. }
  568. this.muted = muted;
  569. this.dispatchEvent('volumechange');
  570. } else {
  571. this.pluginApi.setMuted(muted);
  572. }
  573. this.muted = muted;
  574. }
  575. },
  576. // additional non-HTML5 methods
  577. setVideoSize: function (width, height) {
  578. //if (this.pluginType == 'flash' || this.pluginType == 'silverlight') {
  579. if ( this.pluginElement.style) {
  580. this.pluginElement.style.width = width + 'px';
  581. this.pluginElement.style.height = height + 'px';
  582. }
  583. if (this.pluginApi != null && this.pluginApi.setVideoSize) {
  584. this.pluginApi.setVideoSize(width, height);
  585. }
  586. //}
  587. },
  588. setFullscreen: function (fullscreen) {
  589. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  590. this.pluginApi.setFullscreen(fullscreen);
  591. }
  592. },
  593. enterFullScreen: function() {
  594. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  595. this.setFullscreen(true);
  596. }
  597. },
  598. exitFullScreen: function() {
  599. if (this.pluginApi != null && this.pluginApi.setFullscreen) {
  600. this.setFullscreen(false);
  601. }
  602. },
  603. // start: fake events
  604. addEventListener: function (eventName, callback, bubble) {
  605. this.events[eventName] = this.events[eventName] || [];
  606. this.events[eventName].push(callback);
  607. },
  608. removeEventListener: function (eventName, callback) {
  609. if (!eventName) { this.events = {}; return true; }
  610. var callbacks = this.events[eventName];
  611. if (!callbacks) return true;
  612. if (!callback) { this.events[eventName] = []; return true; }
  613. for (i = 0; i < callbacks.length; i++) {
  614. if (callbacks[i] === callback) {
  615. this.events[eventName].splice(i, 1);
  616. return true;
  617. }
  618. }
  619. return false;
  620. },
  621. dispatchEvent: function (eventName) {
  622. var i,
  623. args,
  624. callbacks = this.events[eventName];
  625. if (callbacks) {
  626. args = Array.prototype.slice.call(arguments, 1);
  627. for (i = 0; i < callbacks.length; i++) {
  628. callbacks[i].apply(null, args);
  629. }
  630. }
  631. },
  632. // end: fake events
  633. // fake DOM attribute methods
  634. hasAttribute: function(name){
  635. return (name in this.attributes);
  636. },
  637. removeAttribute: function(name){
  638. delete this.attributes[name];
  639. },
  640. getAttribute: function(name){
  641. if (this.hasAttribute(name)) {
  642. return this.attributes[name];
  643. }
  644. return '';
  645. },
  646. setAttribute: function(name, value){
  647. this.attributes[name] = value;
  648. },
  649. remove: function() {
  650. mejs.Utility.removeSwf(this.pluginElement.id);
  651. mejs.MediaPluginBridge.unregisterPluginElement(this.pluginElement.id);
  652. }
  653. };
  654. // Handles calls from Flash/Silverlight and reports them as native <video/audio> events and properties
  655. mejs.MediaPluginBridge = {
  656. pluginMediaElements:{},
  657. htmlMediaElements:{},
  658. registerPluginElement: function (id, pluginMediaElement, htmlMediaElement) {
  659. this.pluginMediaElements[id] = pluginMediaElement;
  660. this.htmlMediaElements[id] = htmlMediaElement;
  661. },
  662. unregisterPluginElement: function (id) {
  663. delete this.pluginMediaElements[id];
  664. delete this.htmlMediaElements[id];
  665. },
  666. // when Flash/Silverlight is ready, it calls out to this method
  667. initPlugin: function (id) {
  668. var pluginMediaElement = this.pluginMediaElements[id],
  669. htmlMediaElement = this.htmlMediaElements[id];
  670. if (pluginMediaElement) {
  671. // find the javascript bridge
  672. switch (pluginMediaElement.pluginType) {
  673. case "flash":
  674. pluginMediaElement.pluginElement = pluginMediaElement.pluginApi = document.getElementById(id);
  675. break;
  676. case "silverlight":
  677. pluginMediaElement.pluginElement = document.getElementById(pluginMediaElement.id);
  678. pluginMediaElement.pluginApi = pluginMediaElement.pluginElement.Content.MediaElementJS;
  679. break;
  680. }
  681. if (pluginMediaElement.pluginApi != null && pluginMediaElement.success) {
  682. pluginMediaElement.success(pluginMediaElement, htmlMediaElement);
  683. }
  684. }
  685. },
  686. // receives events from Flash/Silverlight and sends them out as HTML5 media events
  687. // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html
  688. fireEvent: function (id, eventName, values) {
  689. var
  690. e,
  691. i,
  692. bufferedTime,
  693. pluginMediaElement = this.pluginMediaElements[id];
  694. if(!pluginMediaElement){
  695. return;
  696. }
  697. // fake event object to mimic real HTML media event.
  698. e = {
  699. type: eventName,
  700. target: pluginMediaElement
  701. };
  702. // attach all values to element and event object
  703. for (i in values) {
  704. pluginMediaElement[i] = values[i];
  705. e[i] = values[i];
  706. }
  707. // fake the newer W3C buffered TimeRange (loaded and total have been removed)
  708. bufferedTime = values.bufferedTime || 0;
  709. e.target.buffered = e.buffered = {
  710. start: function(index) {
  711. return 0;
  712. },
  713. end: function (index) {
  714. return bufferedTime;
  715. },
  716. length: 1
  717. };
  718. pluginMediaElement.dispatchEvent(e.type, e);
  719. }
  720. };
  721. /*
  722. Default options
  723. */
  724. mejs.MediaElementDefaults = {
  725. // allows testing on HTML5, flash, silverlight
  726. // auto: attempts to detect what the browser can do
  727. // auto_plugin: prefer plugins and then attempt native HTML5
  728. // native: forces HTML5 playback
  729. // shim: disallows HTML5, will attempt either Flash or Silverlight
  730. // none: forces fallback view
  731. mode: 'auto',
  732. // remove or reorder to change plugin priority and availability
  733. plugins: ['flash','silverlight','youtube','vimeo'],
  734. // shows debug errors on screen
  735. enablePluginDebug: false,
  736. // use plugin for browsers that have trouble with Basic Authentication on HTTPS sites
  737. httpsBasicAuthSite: false,
  738. // overrides the type specified, useful for dynamic instantiation
  739. type: '',
  740. // path to Flash and Silverlight plugins
  741. pluginPath: mejs.Utility.getScriptPath(['mediaelement.js','mediaelement.min.js','mediaelement-and-player.js','mediaelement-and-player.min.js']),
  742. // name of flash file
  743. flashName: 'flashmediaelement.swf',
  744. // streamer for RTMP streaming
  745. flashStreamer: '',
  746. // turns on the smoothing filter in Flash
  747. enablePluginSmoothing: false,
  748. // enabled pseudo-streaming (seek) on .mp4 files
  749. enablePseudoStreaming: false,
  750. // start query parameter sent to server for pseudo-streaming
  751. pseudoStreamingStartQueryParam: 'start',
  752. // name of silverlight file
  753. silverlightName: 'silverlightmediaelement.xap',
  754. // default if the <video width> is not specified
  755. defaultVideoWidth: 480,
  756. // default if the <video height> is not specified
  757. defaultVideoHeight: 270,
  758. // overrides <video width>
  759. pluginWidth: -1,
  760. // overrides <video height>
  761. pluginHeight: -1,
  762. // additional plugin variables in 'key=value' form
  763. pluginVars: [],
  764. // rate in milliseconds for Flash and Silverlight to fire the timeupdate event
  765. // larger number is less accurate, but less strain on plugin->JavaScript bridge
  766. timerRate: 250,
  767. // initial volume for player
  768. startVolume: 0.8,
  769. success: function () { },
  770. error: function () { }
  771. };
  772. /*
  773. Determines if a browser supports the <video> or <audio> element
  774. and returns either the native element or a Flash/Silverlight version that
  775. mimics HTML5 MediaElement
  776. */
  777. mejs.MediaElement = function (el, o) {
  778. return mejs.HtmlMediaElementShim.create(el,o);
  779. };
  780. mejs.HtmlMediaElementShim = {
  781. create: function(el, o) {
  782. var
  783. options = mejs.MediaElementDefaults,
  784. htmlMediaElement = (typeof(el) == 'string') ? document.getElementById(el) : el,
  785. tagName = htmlMediaElement.tagName.toLowerCase(),
  786. isMediaTag = (tagName === 'audio' || tagName === 'video'),
  787. src = (isMediaTag) ? htmlMediaElement.getAttribute('src') : htmlMediaElement.getAttribute('href'),
  788. poster = htmlMediaElement.getAttribute('poster'),
  789. autoplay = htmlMediaElement.getAttribute('autoplay'),
  790. preload = htmlMediaElement.getAttribute('preload'),
  791. controls = htmlMediaElement.getAttribute('controls'),
  792. playback,
  793. prop;
  794. // extend options
  795. for (prop in o) {
  796. options[prop] = o[prop];
  797. }
  798. // clean up attributes
  799. src = (typeof src == 'undefined' || src === null || src == '') ? null : src;
  800. poster = (typeof poster == 'undefined' || poster === null) ? '' : poster;
  801. preload = (typeof preload == 'undefined' || preload === null || preload === 'false') ? 'none' : preload;
  802. autoplay = !(typeof autoplay == 'undefined' || autoplay === null || autoplay === 'false');
  803. controls = !(typeof controls == 'undefined' || controls === null || controls === 'false');
  804. // test for HTML5 and plugin capabilities
  805. playback = this.determinePlayback(htmlMediaElement, options, mejs.MediaFeatures.supportsMediaTag, isMediaTag, src);
  806. playback.url = (playback.url !== null) ? mejs.Utility.absolutizeUrl(playback.url) : '';
  807. if (playback.method == 'native') {
  808. // second fix for android
  809. if (mejs.MediaFeatures.isBustedAndroid) {
  810. htmlMediaElement.src = playback.url;
  811. htmlMediaElement.addEventListener('click', function() {
  812. htmlMediaElement.play();
  813. }, false);
  814. }
  815. // add methods to native HTMLMediaElement
  816. return this.updateNative(playback, options, autoplay, preload);
  817. } else if (playback.method !== '') {
  818. // create plugin to mimic HTMLMediaElement
  819. return this.createPlugin( playback, options, poster, autoplay, preload, controls);
  820. } else {
  821. // boo, no HTML5, no Flash, no Silverlight.
  822. this.createErrorMessage( playback, options, poster );
  823. return this;
  824. }
  825. },
  826. determinePlayback: function(htmlMediaElement, options, supportsMediaTag, isMediaTag, src) {
  827. var
  828. mediaFiles = [],
  829. i,
  830. j,
  831. k,
  832. l,
  833. n,
  834. type,
  835. result = { method: '', url: '', htmlMediaElement: htmlMediaElement, isVideo: (htmlMediaElement.tagName.toLowerCase() != 'audio')},
  836. pluginName,
  837. pluginVersions,
  838. pluginInfo,
  839. dummy,
  840. media;
  841. // STEP 1: Get URL and type from <video src> or <source src>
  842. // supplied type overrides <video type> and <source type>
  843. if (typeof options.type != 'undefined' && options.type !== '') {
  844. // accept either string or array of types
  845. if (typeof options.type == 'string') {
  846. mediaFiles.push({type:options.type, url:src});
  847. } else {
  848. for (i=0; i<options.type.length; i++) {
  849. mediaFiles.push({type:options.type[i], url:src});
  850. }
  851. }
  852. // test for src attribute first
  853. } else if (src !== null) {
  854. type = this.formatType(src, htmlMediaElement.getAttribute('type'));
  855. mediaFiles.push({type:type, url:src});
  856. // then test for <source> elements
  857. } else {
  858. // test <source> types to see if they are usable
  859. for (i = 0; i < htmlMediaElement.childNodes.length; i++) {
  860. n = htmlMediaElement.childNodes[i];
  861. if (n.nodeType == 1 && n.tagName.toLowerCase() == 'source') {
  862. src = n.getAttribute('src');
  863. type = this.formatType(src, n.getAttribute('type'));
  864. media = n.getAttribute('media');
  865. if (!media || !window.matchMedia || (window.matchMedia && window.matchMedia(media).matches)) {
  866. mediaFiles.push({type:type, url:src});
  867. }
  868. }
  869. }
  870. }
  871. // in the case of dynamicly created players
  872. // check for audio types
  873. if (!isMediaTag && mediaFiles.length > 0 && mediaFiles[0].url !== null && this.getTypeFromFile(mediaFiles[0].url).indexOf('audio') > -1) {
  874. result.isVideo = false;
  875. }
  876. // STEP 2: Test for playback method
  877. // special case for Android which sadly doesn't implement the canPlayType function (always returns '')
  878. if (mejs.MediaFeatures.isBustedAndroid) {
  879. htmlMediaElement.canPlayType = function(type) {
  880. return (type.match(/video\/(mp4|m4v)/gi) !== null) ? 'maybe' : '';
  881. };
  882. }
  883. // test for native playback first
  884. if (supportsMediaTag && (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'native') && !(mejs.MediaFeatures.isBustedNativeHTTPS && options.httpsBasicAuthSite === true)) {
  885. if (!isMediaTag) {
  886. // create a real HTML5 Media Element
  887. dummy = document.createElement( result.isVideo ? 'video' : 'audio');
  888. htmlMediaElement.parentNode.insertBefore(dummy, htmlMediaElement);
  889. htmlMediaElement.style.display = 'none';
  890. // use this one from now on
  891. result.htmlMediaElement = htmlMediaElement = dummy;
  892. }
  893. for (i=0; i<mediaFiles.length; i++) {
  894. // normal check
  895. if (htmlMediaElement.canPlayType(mediaFiles[i].type).replace(/no/, '') !== ''
  896. // special case for Mac/Safari 5.0.3 which answers '' to canPlayType('audio/mp3') but 'maybe' to canPlayType('audio/mpeg')
  897. || htmlMediaElement.canPlayType(mediaFiles[i].type.replace(/mp3/,'mpeg')).replace(/no/, '') !== '') {
  898. result.method = 'native';
  899. result.url = mediaFiles[i].url;
  900. break;
  901. }
  902. }
  903. if (result.method === 'native') {
  904. if (result.url !== null) {
  905. htmlMediaElement.src = result.url;
  906. }
  907. // if `auto_plugin` mode, then cache the native result but try plugins.
  908. if (options.mode !== 'auto_plugin') {
  909. return result;
  910. }
  911. }
  912. }
  913. // if native playback didn't work, then test plugins
  914. if (options.mode === 'auto' || options.mode === 'auto_plugin' || options.mode === 'shim') {
  915. for (i=0; i<mediaFiles.length; i++) {
  916. type = mediaFiles[i].type;
  917. // test all plugins in order of preference [silverlight, flash]
  918. for (j=0; j<options.plugins.length; j++) {
  919. pluginName = options.plugins[j];
  920. // test version of plugin (for future features)
  921. pluginVersions = mejs.plugins[pluginName];
  922. for (k=0; k<pluginVersions.length; k++) {
  923. pluginInfo = pluginVersions[k];
  924. // test if user has the correct plugin version
  925. // for youtube/vimeo
  926. if (pluginInfo.version == null ||
  927. mejs.PluginDetector.hasPluginVersion(pluginName, pluginInfo.version)) {
  928. // test for plugin playback types
  929. for (l=0; l<pluginInfo.types.length; l++) {
  930. // find plugin that can play the type
  931. if (type == pluginInfo.types[l]) {
  932. result.method = pluginName;
  933. result.url = mediaFiles[i].url;
  934. return result;
  935. }
  936. }
  937. }
  938. }
  939. }
  940. }
  941. }
  942. // at this point, being in 'auto_plugin' mode implies that we tried plugins but failed.
  943. // if we have native support then return that.
  944. if (options.mode === 'auto_plugin' && result.method === 'native') {
  945. return result;
  946. }
  947. // what if there's nothing to play? just grab the first available
  948. if (result.method === '' && mediaFiles.length > 0) {
  949. result.url = mediaFiles[0].url;
  950. }
  951. return result;
  952. },
  953. formatType: function(url, type) {
  954. var ext;
  955. // if no type is supplied, fake it with the extension
  956. if (url && !type) {
  957. return this.getTypeFromFile(url);
  958. } else {
  959. // only return the mime part of the type in case the attribute contains the codec
  960. // see http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
  961. // `video/mp4; codecs="avc1.42E01E, mp4a.40.2"` becomes `video/mp4`
  962. if (type && ~type.indexOf(';')) {
  963. return type.substr(0, type.indexOf(';'));
  964. } else {
  965. return type;
  966. }
  967. }
  968. },
  969. getTypeFromFile: function(url) {
  970. url = url.split('?')[0];
  971. var ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
  972. return (/(mp4|m4v|ogg|ogv|webm|webmv|flv|wmv|mpeg|mov)/gi.test(ext) ? 'video' : 'audio') + '/' + this.getTypeFromExtension(ext);
  973. },
  974. getTypeFromExtension: function(ext) {
  975. switch (ext) {
  976. case 'mp4':
  977. case 'm4v':
  978. return 'mp4';
  979. case 'webm':
  980. case 'webma':
  981. case 'webmv':
  982. return 'webm';
  983. case 'ogg':
  984. case 'oga':
  985. case 'ogv':
  986. return 'ogg';
  987. default:
  988. return ext;
  989. }
  990. },
  991. createErrorMessage: function(playback, options, poster) {
  992. var
  993. htmlMediaElement = playback.htmlMediaElement,
  994. errorContainer = document.createElement('div');
  995. errorContainer.className = 'me-cannotplay';
  996. try {
  997. errorContainer.style.width = htmlMediaElement.width + 'px';
  998. errorContainer.style.height = htmlMediaElement.height + 'px';
  999. } catch (e) {}
  1000. if (options.customError) {
  1001. errorContainer.innerHTML = options.customError;
  1002. } else {
  1003. errorContainer.innerHTML = (poster !== '') ?
  1004. '<a href="' + playback.url + '"><img src="' + poster + '" width="100%" height="100%" /></a>' :
  1005. '<a href="' + playback.url + '"><span>' + mejs.i18n.t('Download File') + '</span></a>';
  1006. }
  1007. htmlMediaElement.parentNode.insertBefore(errorContainer, htmlMediaElement);
  1008. htmlMediaElement.style.display = 'none';
  1009. options.error(htmlMediaElement);
  1010. },
  1011. createPlugin:function(playback, options, poster, autoplay, preload, controls) {
  1012. var
  1013. htmlMediaElement = playback.htmlMediaElement,
  1014. width = 1,
  1015. height = 1,
  1016. pluginid = 'me_' + playback.method + '_' + (mejs.meIndex++),
  1017. pluginMediaElement = new mejs.PluginMediaElement(pluginid, playback.method, playback.url),
  1018. container = document.createElement('div'),
  1019. specialIEContainer,
  1020. node,
  1021. initVars;
  1022. // copy tagName from html media element
  1023. pluginMediaElement.tagName = htmlMediaElement.tagName
  1024. // copy attributes from html media element to plugin media element
  1025. for (var i = 0; i < htmlMediaElement.attributes.length; i++) {
  1026. var attribute = htmlMediaElement.attributes[i];
  1027. if (attribute.specified == true) {
  1028. pluginMediaElement.setAttribute(attribute.name, attribute.value);
  1029. }
  1030. }
  1031. // check for placement inside a <p> tag (sometimes WYSIWYG editors do this)
  1032. node = htmlMediaElement.parentNode;
  1033. while (node !== null && node.tagName.toLowerCase() != 'body') {
  1034. if (node.parentNode.tagName.toLowerCase() == 'p') {
  1035. node.parentNode.parentNode.insertBefore(node, node.parentNode);
  1036. break;
  1037. }
  1038. node = node.parentNode;
  1039. }
  1040. if (playback.isVideo) {
  1041. width = (options.pluginWidth > 0) ? options.pluginWidth : (options.videoWidth > 0) ? options.videoWidth : (htmlMediaElement.getAttribute('width') !== null) ? htmlMediaElement.getAttribute('width') : options.defaultVideoWidth;
  1042. height = (options.pluginHeight > 0) ? options.pluginHeight : (options.videoHeight > 0) ? options.videoHeight : (htmlMediaElement.getAttribute('height') !== null) ? htmlMediaElement.getAttribute('height') : options.defaultVideoHeight;
  1043. // in case of '%' make sure it's encoded
  1044. width = mejs.Utility.encodeUrl(width);
  1045. height = mejs.Utility.encodeUrl(height);
  1046. } else {
  1047. if (options.enablePluginDebug) {
  1048. width = 320;
  1049. height = 240;
  1050. }
  1051. }
  1052. // register plugin
  1053. pluginMediaElement.success = options.success;
  1054. mejs.MediaPluginBridge.registerPluginElement(pluginid, pluginMediaElement, htmlMediaElement);
  1055. // add container (must be added to DOM before inserting HTML for IE)
  1056. container.className = 'me-plugin';
  1057. container.id = pluginid + '_container';
  1058. if (playback.isVideo) {
  1059. htmlMediaElement.parentNode.insertBefore(container, htmlMediaElement);
  1060. } else {
  1061. document.body.insertBefore(container, document.body.childNodes[0]);
  1062. }
  1063. // flash/silverlight vars
  1064. initVars = [
  1065. 'id=' + pluginid,
  1066. 'isvideo=' + ((playback.isVideo) ? "true" : "false"),
  1067. 'autoplay=' + ((autoplay) ? "true" : "false"),
  1068. 'preload=' + preload,
  1069. 'width=' + width,
  1070. 'startvolume=' + options.startVolume,
  1071. 'timerrate=' + options.timerRate,
  1072. 'flashstreamer=' + options.flashStreamer,
  1073. 'height=' + height,
  1074. 'pseudostreamstart=' + options.pseudoStreamingStartQueryParam];
  1075. if (playback.url !== null) {
  1076. if (playback.method == 'flash') {
  1077. initVars.push('file=' + mejs.Utility.encodeUrl(playback.url));
  1078. } else {
  1079. initVars.push('file=' + playback.url);
  1080. }
  1081. }
  1082. if (options.enablePluginDebug) {
  1083. initVars.push('debug=true');
  1084. }
  1085. if (options.enablePluginSmoothing) {
  1086. initVars.push('smoothing=true');
  1087. }
  1088. if (options.enablePseudoStreaming) {
  1089. initVars.push('pseudostreaming=true');
  1090. }
  1091. if (controls) {
  1092. initVars.push('controls=true'); // shows controls in the plugin if desired
  1093. }
  1094. if (options.pluginVars) {
  1095. initVars = initVars.concat(options.pluginVars);
  1096. }
  1097. switch (playback.method) {
  1098. case 'silverlight':
  1099. container.innerHTML =
  1100. '<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" id="' + pluginid + '" name="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1101. '<param name="initParams" value="' + initVars.join(',') + '" />' +
  1102. '<param name="windowless" value="true" />' +
  1103. '<param name="background" value="black" />' +
  1104. '<param name="minRuntimeVersion" value="3.0.0.0" />' +
  1105. '<param name="autoUpgrade" value="true" />' +
  1106. '<param name="source" value="' + options.pluginPath + options.silverlightName + '" />' +
  1107. '</object>';
  1108. break;
  1109. case 'flash':
  1110. if (mejs.MediaFeatures.isIE) {
  1111. specialIEContainer = document.createElement('div');
  1112. container.appendChild(specialIEContainer);
  1113. specialIEContainer.outerHTML =
  1114. '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
  1115. 'id="' + pluginid + '" width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1116. '<param name="movie" value="' + options.pluginPath + options.flashName + '?x=' + (new Date()) + '" />' +
  1117. '<param name="flashvars" value="' + initVars.join('&amp;') + '" />' +
  1118. '<param name="quality" value="high" />' +
  1119. '<param name="bgcolor" value="#000000" />' +
  1120. '<param name="wmode" value="transparent" />' +
  1121. '<param name="allowScriptAccess" value="always" />' +
  1122. '<param name="allowFullScreen" value="true" />' +
  1123. '<param name="scale" value="default" />' +
  1124. '</object>';
  1125. } else {
  1126. container.innerHTML =
  1127. '<embed id="' + pluginid + '" name="' + pluginid + '" ' +
  1128. 'play="true" ' +
  1129. 'loop="false" ' +
  1130. 'quality="high" ' +
  1131. 'bgcolor="#000000" ' +
  1132. 'wmode="transparent" ' +
  1133. 'allowScriptAccess="always" ' +
  1134. 'allowFullScreen="true" ' +
  1135. 'type="application/x-shockwave-flash" pluginspage="//www.macromedia.com/go/getflashplayer" ' +
  1136. 'src="' + options.pluginPath + options.flashName + '" ' +
  1137. 'flashvars="' + initVars.join('&') + '" ' +
  1138. 'width="' + width + '" ' +
  1139. 'height="' + height + '" ' +
  1140. 'scale="default"' +
  1141. 'class="mejs-shim"></embed>';
  1142. }
  1143. break;
  1144. case 'youtube':
  1145. var
  1146. videoId = playback.url.substr(playback.url.lastIndexOf('=')+1);
  1147. youtubeSettings = {
  1148. container: container,
  1149. containerId: container.id,
  1150. pluginMediaElement: pluginMediaElement,
  1151. pluginId: pluginid,
  1152. videoId: videoId,
  1153. height: height,
  1154. width: width
  1155. };
  1156. if (mejs.PluginDetector.hasPluginVersion('flash', [10,0,0]) ) {
  1157. mejs.YouTubeApi.createFlash(youtubeSettings);
  1158. } else {
  1159. mejs.YouTubeApi.enqueueIframe(youtubeSettings);
  1160. }
  1161. break;
  1162. // DEMO Code. Does NOT work.
  1163. case 'vimeo':
  1164. //
  1165. pluginMediaElement.vimeoid = playback.url.substr(playback.url.lastIndexOf('/')+1);
  1166. container.innerHTML ='<iframe src="http://player.vimeo.com/video/' + pluginMediaElement.vimeoid + '?portrait=0&byline=0&title=0" width="' + width +'" height="' + height +'" frameborder="0" class="mejs-shim"></iframe>';
  1167. /*
  1168. container.innerHTML =
  1169. '<object width="' + width + '" height="' + height + '" class="mejs-shim">' +
  1170. '<param name="allowfullscreen" value="true" />' +
  1171. '<param name="allowscriptaccess" value="always" />' +
  1172. '<param name="flashvars" value="api=1" />' +
  1173. '<param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=' + pluginMediaElement.vimeoid + '&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00adef&amp;fullscreen=1&amp;autoplay=0&amp;loop=0" />' +
  1174. '<embed src="//vimeo.com/moogaloop.swf?api=1&amp;clip_id=' + pluginMediaElement.vimeoid + '&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00adef&amp;fullscreen=1&amp;autoplay=0&amp;loop=0" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="' + width + '" height="' + height + '" class="mejs-shim"></embed>' +
  1175. '</object>';
  1176. */
  1177. break;
  1178. }
  1179. // hide original element
  1180. htmlMediaElement.style.display = 'none';
  1181. // prevent browser from autoplaying when using a plugin
  1182. htmlMediaElement.removeAttribute('autoplay');
  1183. // FYI: options.success will be fired by the MediaPluginBridge
  1184. return pluginMediaElement;
  1185. },
  1186. updateNative: function(playback, options, autoplay, preload) {
  1187. var htmlMediaElement = playback.htmlMediaElement,
  1188. m;
  1189. // add methods to video object to bring it into parity with Flash Object
  1190. for (m in mejs.HtmlMediaElement) {
  1191. htmlMediaElement[m] = mejs.HtmlMediaElement[m];
  1192. }
  1193. /*
  1194. Chrome now supports preload="none"
  1195. if (mejs.MediaFeatures.isChrome) {
  1196. // special case to enforce preload attribute (Chrome doesn't respect this)
  1197. if (preload === 'none' && !autoplay) {
  1198. // forces the browser to stop loading (note: fails in IE9)
  1199. htmlMediaElement.src = '';
  1200. htmlMediaElement.load();
  1201. htmlMediaElement.canceledPreload = true;
  1202. htmlMediaElement.addEventListener('play',function() {
  1203. if (htmlMediaElement.canceledPreload) {
  1204. htmlMediaElement.src = playback.url;
  1205. htmlMediaElement.load();
  1206. htmlMediaElement.play();
  1207. htmlMediaElement.canceledPreload = false;
  1208. }
  1209. }, false);
  1210. // for some reason Chrome forgets how to autoplay sometimes.
  1211. } else if (autoplay) {
  1212. htmlMediaElement.load();
  1213. htmlMediaElement.play();
  1214. }
  1215. }
  1216. */
  1217. // fire success code
  1218. options.success(htmlMediaElement, htmlMediaElement);
  1219. return htmlMediaElement;
  1220. }
  1221. };
  1222. /*
  1223. - test on IE (object vs. embed)
  1224. - determine when to use iframe (Firefox, Safari, Mobile) vs. Flash (Chrome, IE)
  1225. - fullscreen?
  1226. */
  1227. // YouTube Flash and Iframe API
  1228. mejs.YouTubeApi = {
  1229. isIframeStarted: false,
  1230. isIframeLoaded: false,
  1231. loadIframeApi: function() {
  1232. if (!this.isIframeStarted) {
  1233. var tag = document.createElement('script');
  1234. tag.src = "//www.youtube.com/player_api";
  1235. var firstScriptTag = document.getElementsByTagName('script')[0];
  1236. firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  1237. this.isIframeStarted = true;
  1238. }
  1239. },
  1240. iframeQueue: [],
  1241. enqueueIframe: function(yt) {
  1242. if (this.isLoaded) {
  1243. this.createIframe(yt);
  1244. } else {
  1245. this.loadIframeApi();
  1246. this.iframeQueue.push(yt);
  1247. }
  1248. },
  1249. createIframe: function(settings) {
  1250. var
  1251. pluginMediaElement = settings.pluginMediaElement,
  1252. player = new YT.Player(settings.containerId, {
  1253. height: settings.height,
  1254. width: settings.width,
  1255. videoId: settings.videoId,
  1256. playerVars: {controls:0},
  1257. events: {
  1258. 'onReady': function() {
  1259. // hook up iframe object to MEjs
  1260. settings.pluginMediaElement.pluginApi = player;
  1261. // init mejs
  1262. mejs.MediaPluginBridge.initPlugin(settings.pluginId);
  1263. // create timer
  1264. setInterval(function() {
  1265. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
  1266. }, 250);
  1267. },
  1268. 'onStateChange': function(e) {
  1269. mejs.YouTubeApi.handleStateChange(e.data, player, pluginMediaElement);
  1270. }
  1271. }
  1272. });
  1273. },
  1274. createEvent: function (player, pluginMediaElement, eventName) {
  1275. var obj = {
  1276. type: eventName,
  1277. target: pluginMediaElement
  1278. };
  1279. if (player && player.getDuration) {
  1280. // time
  1281. pluginMediaElement.currentTime = obj.currentTime = player.getCurrentTime();
  1282. pluginMediaElement.duration = obj.duration = player.getDuration();
  1283. // state
  1284. obj.paused = pluginMediaElement.paused;
  1285. obj.ended = pluginMediaElement.ended;
  1286. // sound
  1287. obj.muted = player.isMuted();
  1288. obj.volume = player.getVolume() / 100;
  1289. // progress
  1290. obj.bytesTotal = player.getVideoBytesTotal();
  1291. obj.bufferedBytes = player.getVideoBytesLoaded();
  1292. // fake the W3C buffered TimeRange
  1293. var bufferedTime = obj.bufferedBytes / obj.bytesTotal * obj.duration;
  1294. obj.target.buffered = obj.buffered = {
  1295. start: function(index) {
  1296. return 0;
  1297. },
  1298. end: function (index) {
  1299. return bufferedTime;
  1300. },
  1301. length: 1
  1302. };
  1303. }
  1304. // send event up the chain
  1305. pluginMediaElement.dispatchEvent(obj.type, obj);
  1306. },
  1307. iFrameReady: function() {
  1308. this.isLoaded = true;
  1309. this.isIframeLoaded = true;
  1310. while (this.iframeQueue.length > 0) {
  1311. var settings = this.iframeQueue.pop();
  1312. this.createIframe(settings);
  1313. }
  1314. },
  1315. // FLASH!
  1316. flashPlayers: {},
  1317. createFlash: function(settings) {
  1318. this.flashPlayers[settings.pluginId] = settings;
  1319. /*
  1320. settings.container.innerHTML =
  1321. '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0" ' +
  1322. 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
  1323. '<param name="allowScriptAccess" value="always">' +
  1324. '<param name="wmode" value="transparent">' +
  1325. '</object>';
  1326. */
  1327. var specialIEContainer,
  1328. youtubeUrl = '//www.youtube.com/apiplayer?enablejsapi=1&amp;playerapiid=' + settings.pluginId + '&amp;version=3&amp;autoplay=0&amp;controls=0&amp;modestbranding=1&loop=0';
  1329. if (mejs.MediaFeatures.isIE) {
  1330. specialIEContainer = document.createElement('div');
  1331. settings.container.appendChild(specialIEContainer);
  1332. specialIEContainer.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="//download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" ' +
  1333. 'id="' + settings.pluginId + '" width="' + settings.width + '" height="' + settings.height + '" class="mejs-shim">' +
  1334. '<param name="movie" value="' + youtubeUrl + '" />' +
  1335. '<param name="wmode" value="transparent" />' +
  1336. '<param name="allowScriptAccess" value="always" />' +
  1337. '<param name="allowFullScreen" value="true" />' +
  1338. '</object>';
  1339. } else {
  1340. settings.container.innerHTML =
  1341. '<object type="application/x-shockwave-flash" id="' + settings.pluginId + '" data="' + youtubeUrl + '" ' +
  1342. 'width="' + settings.width + '" height="' + settings.height + '" style="visibility: visible; " class="mejs-shim">' +
  1343. '<param name="allowScriptAccess" value="always">' +
  1344. '<param name="wmode" value="transparent">' +
  1345. '</object>';
  1346. }
  1347. },
  1348. flashReady: function(id) {
  1349. var
  1350. settings = this.flashPlayers[id],
  1351. player = document.getElementById(id),
  1352. pluginMediaElement = settings.pluginMediaElement;
  1353. // hook up and return to MediaELementPlayer.success
  1354. pluginMediaElement.pluginApi =
  1355. pluginMediaElement.pluginElement = player;
  1356. mejs.MediaPluginBridge.initPlugin(id);
  1357. // load the youtube video
  1358. player.cueVideoById(settings.videoId);
  1359. var callbackName = settings.containerId + '_callback';
  1360. window[callbackName] = function(e) {
  1361. mejs.YouTubeApi.handleStateChange(e, player, pluginMediaElement);
  1362. }
  1363. player.addEventListener('onStateChange', callbackName);
  1364. setInterval(function() {
  1365. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'timeupdate');
  1366. }, 250);
  1367. },
  1368. handleStateChange: function(youTubeState, player, pluginMediaElement) {
  1369. switch (youTubeState) {
  1370. case -1: // not started
  1371. pluginMediaElement.paused = true;
  1372. pluginMediaElement.ended = true;
  1373. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'loadedmetadata');
  1374. //createYouTubeEvent(player, pluginMediaElement, 'loadeddata');
  1375. break;
  1376. case 0:
  1377. pluginMediaElement.paused = false;
  1378. pluginMediaElement.ended = true;
  1379. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'ended');
  1380. break;
  1381. case 1:
  1382. pluginMediaElement.paused = false;
  1383. pluginMediaElement.ended = false;
  1384. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'play');
  1385. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'playing');
  1386. break;
  1387. case 2:
  1388. pluginMediaElement.paused = true;
  1389. pluginMediaElement.ended = false;
  1390. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'pause');
  1391. break;
  1392. case 3: // buffering
  1393. mejs.YouTubeApi.createEvent(player, pluginMediaElement, 'progress');
  1394. break;
  1395. case 5:
  1396. // cued?
  1397. break;
  1398. }
  1399. }
  1400. }
  1401. // IFRAME
  1402. function onYouTubePlayerAPIReady() {
  1403. mejs.YouTubeApi.iFrameReady();
  1404. }
  1405. // FLASH
  1406. function onYouTubePlayerReady(id) {
  1407. mejs.YouTubeApi.flashReady(id);
  1408. }
  1409. window.mejs = mejs;
  1410. window.MediaElement = mejs.MediaElement;
  1411. /*!
  1412. * Adds Internationalization and localization to mediaelement.
  1413. *
  1414. * This file does not contain translations, you have to add the manually.
  1415. * The schema is always the same: me-i18n-locale-[ISO_639-1 Code].js
  1416. *
  1417. * Examples are provided both for german and chinese translation.
  1418. *
  1419. *
  1420. * What is the concept beyond i18n?
  1421. * http://en.wikipedia.org/wiki/Internationalization_and_localization
  1422. *
  1423. * What langcode should i use?
  1424. * http://en.wikipedia.org/wiki/ISO_639-1
  1425. *
  1426. *
  1427. * License?
  1428. *
  1429. * The i18n file uses methods from the Drupal project (drupal.js):
  1430. * - i18n.methods.t() (modified)
  1431. * - i18n.methods.checkPlain() (full copy)
  1432. *
  1433. * The Drupal project is (like mediaelementjs) licensed under GPLv2.
  1434. * - http://drupal.org/licensing/faq/#q1
  1435. * - https://github.com/johndyer/mediaelement
  1436. * - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  1437. *
  1438. *
  1439. * @author
  1440. * Tim Latz (latz.tim@gmail.com)
  1441. *
  1442. *
  1443. * @params
  1444. * - context - document, iframe ..
  1445. * - exports - CommonJS, window ..
  1446. *
  1447. */
  1448. ;(function(context, exports, undefined) {
  1449. "use strict";
  1450. var i18n = {
  1451. "locale": {
  1452. "language" : '',
  1453. "strings" : {}
  1454. },
  1455. "methods" : {}
  1456. };
  1457. // start i18n
  1458. /**
  1459. * Get language, fallback to browser's language if empty
  1460. */
  1461. i18n.getLanguage = function () {
  1462. var language = i18n.locale.language || window.navigator.userLanguage || window.navigator.language;
  1463. // convert to iso 639-1 (2-letters, lower case)
  1464. return language.substr(0, 2).toLowerCase();
  1465. };
  1466. // i18n fixes for compatibility with WordPress
  1467. if ( typeof mejsL10n != 'undefined' ) {
  1468. i18n.locale.language = mejsL10n.language;
  1469. }
  1470. /**
  1471. * Encode special characters in a plain-text string for display as HTML.
  1472. */
  1473. i18n.methods.checkPlain = function (str) {
  1474. var character, regex,
  1475. replace = {
  1476. '&': '&amp;',
  1477. '"': '&quot;',
  1478. '<': '&lt;',
  1479. '>': '&gt;'
  1480. };
  1481. str = String(str);
  1482. for (character in replace) {
  1483. if (replace.hasOwnProperty(character)) {
  1484. regex = new RegExp(character, 'g');
  1485. str = str.replace(regex, replace[character]);
  1486. }
  1487. }
  1488. return str;
  1489. };
  1490. /**
  1491. * Translate strings to the page language or a given language.
  1492. *
  1493. *
  1494. * @param str
  1495. * A string containing the English string to translate.
  1496. *
  1497. * @param options
  1498. * - 'context' (defaults to the default context): The context the source string
  1499. * belongs to.
  1500. *
  1501. * @return
  1502. * The translated string, escaped via i18n.methods.checkPlain()
  1503. */
  1504. i18n.methods.t = function (str, options) {
  1505. // Fetch the localized version of the string.
  1506. if (i18n.locale.strings && i18n.locale.strings[options.context] && i18n.locale.strings[options.context][str]) {
  1507. str = i18n.locale.strings[options.context][str];
  1508. }
  1509. return i18n.methods.checkPlain(str);
  1510. };
  1511. /**
  1512. * Wrapper for i18n.methods.t()
  1513. *
  1514. * @see i18n.methods.t()
  1515. * @throws InvalidArgumentException
  1516. */
  1517. i18n.t = function(str, options) {
  1518. if (typeof str === 'string' && str.length > 0) {
  1519. // check every time due language can change for
  1520. // different reasons (translation, lang switcher ..)
  1521. var language = i18n.getLanguage();
  1522. options = options || {
  1523. "context" : language
  1524. };
  1525. return i18n.methods.t(str, options);
  1526. }
  1527. else {
  1528. throw {
  1529. "name" : 'InvalidArgumentException',
  1530. "message" : 'First argument is either not a string or empty.'
  1531. };
  1532. }
  1533. };
  1534. // end i18n
  1535. exports.i18n = i18n;
  1536. }(document, mejs));
  1537. // i18n fixes for compatibility with WordPress
  1538. ;(function(exports, undefined) {
  1539. "use strict";
  1540. if ( typeof mejsL10n != 'undefined' ) {
  1541. exports[mejsL10n.language] = mejsL10n.strings;
  1542. }
  1543. }(mejs.i18n.locale.strings));
  1544. /*!
  1545. * This is a i18n.locale language object.
  1546. *
  1547. * German translation by Tim Latz, latz.tim@gmail.com
  1548. *
  1549. * @author
  1550. * Tim Latz (latz.tim@gmail.com)
  1551. *
  1552. * @see
  1553. * me-i18n.js
  1554. *
  1555. * @params
  1556. * - exports - CommonJS, window ..
  1557. */
  1558. ;(function(exports, undefined) {
  1559. "use strict";
  1560. if (typeof exports.de === 'undefined') {
  1561. exports.de = {
  1562. "Fullscreen" : "Vollbild",
  1563. "Go Fullscreen" : "Vollbild an",
  1564. "Turn off Fullscreen" : "Vollbild aus",
  1565. "Close" : "Schließen"
  1566. };
  1567. }
  1568. }(mejs.i18n.locale.strings));
  1569. /*!
  1570. * This is a i18n.locale language object.
  1571. *
  1572. * Traditional chinese translation by Tim Latz, latz.tim@gmail.com
  1573. *
  1574. * @author
  1575. * Tim Latz (latz.tim@gmail.com)
  1576. *
  1577. * @see
  1578. * me-i18n.js
  1579. *
  1580. * @params
  1581. * - exports - CommonJS, window ..
  1582. */
  1583. ;(function(exports, undefined) {
  1584. "use strict";
  1585. if (typeof exports.zh === 'undefined') {
  1586. exports.zh = {
  1587. "Fullscreen" : "全螢幕",
  1588. "Go Fullscreen" : "全屏模式",
  1589. "Turn off Fullscreen" : "退出全屏模式",
  1590. "Close" : "關閉"
  1591. };
  1592. }
  1593. }(mejs.i18n.locale.strings));
  1594. /*!
  1595. * MediaElementPlayer
  1596. * http://mediaelementjs.com/
  1597. *
  1598. * Creates a controller bar for HTML5 <video> add <audio> tags
  1599. * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
  1600. *
  1601. * Copyright 2010-2013, John Dyer (http://j.hn/)
  1602. * License: MIT
  1603. *
  1604. */
  1605. if (typeof jQuery != 'undefined') {
  1606. mejs.$ = jQuery;
  1607. } else if (typeof ender != 'undefined') {
  1608. mejs.$ = ender;
  1609. }
  1610. (function ($) {
  1611. // default player values
  1612. mejs.MepDefaults = {
  1613. // url to poster (to fix iOS 3.x)
  1614. poster: '',
  1615. // When the video is ended, we can show the poster.
  1616. showPosterWhenEnded: false,
  1617. // default if the <video width> is not specified
  1618. defaultVideoWidth: 480,
  1619. // default if the <video height> is not specified
  1620. defaultVideoHeight: 270,
  1621. // if set, overrides <video width>
  1622. videoWidth: -1,
  1623. // if set, overrides <video height>
  1624. videoHeight: -1,
  1625. // default if the user doesn't specify
  1626. defaultAudioWidth: 400,
  1627. // default if the user doesn't specify
  1628. defaultAudioHeight: 30,
  1629. // default amount to move back when back key is pressed
  1630. defaultSeekBackwardInterval: function(media) {
  1631. return (media.duration * 0.05);
  1632. },
  1633. // default amount to move forward when forward key is pressed
  1634. defaultSeekForwardInterval: function(media) {
  1635. return (media.duration * 0.05);
  1636. },
  1637. // width of audio player
  1638. audioWidth: -1,
  1639. // height of audio player
  1640. audioHeight: -1,
  1641. // initial volume when the player starts (overrided by user cookie)
  1642. startVolume: 0.8,
  1643. // useful for <audio> player loops
  1644. loop: false,
  1645. // rewind to beginning when media ends
  1646. autoRewind: true,
  1647. // resize to media dimensions
  1648. enableAutosize: true,
  1649. // forces the hour marker (##:00:00)
  1650. alwaysShowHours: false,
  1651. // show framecount in timecode (##:00:00:00)
  1652. showTimecodeFrameCount: false,
  1653. // used when showTimecodeFrameCount is set to true
  1654. framesPerSecond: 25,
  1655. // automatically calculate the width of the progress bar based on the sizes of other elements
  1656. autosizeProgress : true,
  1657. // Hide controls when playing and mouse is not over the video
  1658. alwaysShowControls: false,
  1659. // Display the video control
  1660. hideVideoControlsOnLoad: false,
  1661. // Enable click video element to toggle play/pause
  1662. clickToPlayPause: true,
  1663. // force iPad's native controls
  1664. iPadUseNativeControls: false,
  1665. // force iPhone's native controls
  1666. iPhoneUseNativeControls: false,
  1667. // force Android's native controls
  1668. AndroidUseNativeControls: false,
  1669. // features to show
  1670. features: ['playpause','current','progress','duration','tracks','volume','fullscreen'],
  1671. // only for dynamic
  1672. isVideo: true,
  1673. // turns keyboard support on and off for this instance
  1674. enableKeyboard: true,
  1675. // whenthis player starts, it will pause other players
  1676. pauseOtherPlayers: true,
  1677. // array of keyboard actions such as play pause
  1678. keyActions: [
  1679. {
  1680. keys: [
  1681. 32, // SPACE
  1682. 179 // GOOGLE play/pause button
  1683. ],
  1684. action: function(player, media) {
  1685. if (media.paused || media.ended) {
  1686. player.play();
  1687. } else {
  1688. player.pause();
  1689. }
  1690. }
  1691. },
  1692. {
  1693. keys: [38], // UP
  1694. action: function(player, media) {
  1695. var newVolume = Math.min(media.volume + 0.1, 1);
  1696. media.setVolume(newVolume);
  1697. }
  1698. },
  1699. {
  1700. keys: [40], // DOWN
  1701. action: function(player, media) {
  1702. var newVolume = Math.max(media.volume - 0.1, 0);
  1703. media.setVolume(newVolume);
  1704. }
  1705. },
  1706. {
  1707. keys: [
  1708. 37, // LEFT
  1709. 227 // Google TV rewind
  1710. ],
  1711. action: function(player, media) {
  1712. if (!isNaN(media.duration) && media.duration > 0) {
  1713. if (player.isVideo) {
  1714. player.showControls();
  1715. player.startControlsTimer();
  1716. }
  1717. // 5%
  1718. var newTime = Math.max(media.currentTime - player.options.defaultSeekBackwardInterval(media), 0);
  1719. media.setCurrentTime(newTime);
  1720. }
  1721. }
  1722. },
  1723. {
  1724. keys: [
  1725. 39, // RIGHT
  1726. 228 // Google TV forward
  1727. ],
  1728. action: function(player, media) {
  1729. if (!isNaN(media.duration) && media.duration > 0) {
  1730. if (player.isVideo) {
  1731. player.showControls();
  1732. player.startControlsTimer();
  1733. }
  1734. // 5%
  1735. var newTime = Math.min(media.currentTime + player.options.defaultSeekForwardInterval(media), media.duration);
  1736. media.setCurrentTime(newTime);
  1737. }
  1738. }
  1739. },
  1740. {
  1741. keys: [70], // f
  1742. action: function(player, media) {
  1743. if (typeof player.enterFullScreen != 'undefined') {
  1744. if (player.isFullScreen) {
  1745. player.exitFullScreen();
  1746. } else {
  1747. player.enterFullScreen();
  1748. }
  1749. }
  1750. }
  1751. }
  1752. ]
  1753. };
  1754. mejs.mepIndex = 0;
  1755. mejs.players = {};
  1756. // wraps a MediaElement object in player controls
  1757. mejs.MediaElementPlayer = function(node, o) {
  1758. // enforce object, even without "new" (via John Resig)
  1759. if ( !(this instanceof mejs.MediaElementPlayer) ) {
  1760. return new mejs.MediaElementPlayer(node, o);
  1761. }
  1762. var t = this;
  1763. // these will be reset after the MediaElement.success fires
  1764. t.$media = t.$node = $(node);
  1765. t.node = t.media = t.$media[0];
  1766. // check for existing player
  1767. if (typeof t.node.player != 'undefined') {
  1768. return t.node.player;
  1769. } else {
  1770. // attach player to DOM node for reference
  1771. t.node.player = t;
  1772. }
  1773. // try to get options from data-mejsoptions
  1774. if (typeof o == 'undefined') {
  1775. o = t.$node.data('mejsoptions');
  1776. }
  1777. // extend default options
  1778. t.options = $.extend({},mejs.MepDefaults,o);
  1779. // unique ID
  1780. t.id = 'mep_' + mejs.mepIndex++;
  1781. // add to player array (for focus events)
  1782. mejs.players[t.id] = t;
  1783. // start up
  1784. t.init();
  1785. return t;
  1786. };
  1787. // actual player
  1788. mejs.MediaElementPlayer.prototype = {
  1789. hasFocus: false,
  1790. controlsAreVisible: true,
  1791. init: function() {
  1792. var
  1793. t = this,
  1794. mf = mejs.MediaFeatures,
  1795. // options for MediaElement (shim)
  1796. meOptions = $.extend(true, {}, t.options, {
  1797. success: function(media, domNode) { t.meReady(media, domNode); },
  1798. error: function(e) { t.handleError(e);}
  1799. }),
  1800. tagName = t.media.tagName.toLowerCase();
  1801. t.isDynamic = (tagName !== 'audio' && tagName !== 'video');
  1802. if (t.isDynamic) {
  1803. // get video from src or href?
  1804. t.isVideo = t.options.isVideo;
  1805. } else {
  1806. t.isVideo = (tagName !== 'audio' && t.options.isVideo);
  1807. }
  1808. // use native controls in iPad, iPhone, and Android
  1809. if ((mf.isiPad && t.options.iPadUseNativeControls) || (mf.isiPhone && t.options.iPhoneUseNativeControls)) {
  1810. // add controls and stop
  1811. t.$media.attr('controls', 'controls');
  1812. // attempt to fix iOS 3 bug
  1813. //t.$media.removeAttr('poster');
  1814. // no Issue found on iOS3 -ttroxell
  1815. // override Apple's autoplay override for iPads
  1816. if (mf.isiPad && t.media.getAttribute('autoplay') !== null) {
  1817. t.play();
  1818. }
  1819. } else if (mf.isAndroid && t.options.AndroidUseNativeControls) {
  1820. // leave default player
  1821. } else {
  1822. // DESKTOP: use MediaElementPlayer controls
  1823. // remove native controls
  1824. t.$media.removeAttr('controls');
  1825. // build container
  1826. t.container =
  1827. $('<div id="' + t.id + '" class="mejs-container ' + (mejs.MediaFeatures.svg ? 'svg' : 'no-svg') + '">'+
  1828. '<div class="mejs-inner">'+
  1829. '<div class="mejs-mediaelement"></div>'+
  1830. '<div class="mejs-layers"></div>'+
  1831. '<div class="mejs-controls"></div>'+
  1832. '<div class="mejs-clear"></div>'+
  1833. '</div>' +
  1834. '</div>')
  1835. .addClass(t.$media[0].className)
  1836. .insertBefore(t.$media);
  1837. // add classes for user and content
  1838. t.container.addClass(
  1839. (mf.isAndroid ? 'mejs-android ' : '') +
  1840. (mf.isiOS ? 'mejs-ios ' : '') +
  1841. (mf.isiPad ? 'mejs-ipad ' : '') +
  1842. (mf.isiPhone ? 'mejs-iphone ' : '') +
  1843. (t.isVideo ? 'mejs-video ' : 'mejs-audio ')
  1844. );
  1845. // move the <video/video> tag into the right spot
  1846. if (mf.isiOS) {
  1847. // sadly, you can't move nodes in iOS, so we have to destroy and recreate it!
  1848. var $newMedia = t.$media.clone();
  1849. t.container.find('.mejs-mediaelement').append($newMedia);
  1850. t.$media.remove();
  1851. t.$node = t.$media = $newMedia;
  1852. t.node = t.media = $newMedia[0]
  1853. } else {
  1854. // normal way of moving it into place (doesn't work on iOS)
  1855. t.container.find('.mejs-mediaelement').append(t.$media);
  1856. }
  1857. // find parts
  1858. t.controls = t.container.find('.mejs-controls');
  1859. t.layers = t.container.find('.mejs-layers');
  1860. // determine the size
  1861. /* size priority:
  1862. (1) videoWidth (forced),
  1863. (2) style="width;height;"
  1864. (3) width attribute,
  1865. (4) defaultVideoWidth (for unspecified cases)
  1866. */
  1867. var tagType = (t.isVideo ? 'video' : 'audio'),
  1868. capsTagName = tagType.substring(0,1).toUpperCase() + tagType.substring(1);
  1869. if (t.options[tagType + 'Width'] > 0 || t.options[tagType + 'Width'].toString().indexOf('%') > -1) {
  1870. t.width = t.options[tagType + 'Width'];
  1871. } else if (t.media.style.width !== '' && t.media.style.width !== null) {
  1872. t.width = t.media.style.width;
  1873. } else if (t.media.getAttribute('width') !== null) {
  1874. t.width = t.$media.attr('width');
  1875. } else {
  1876. t.width = t.options['default' + capsTagName + 'Width'];
  1877. }
  1878. if (t.options[tagType + 'Height'] > 0 || t.options[tagType + 'Height'].toString().indexOf('%') > -1) {
  1879. t.height = t.options[tagType + 'Height'];
  1880. } else if (t.media.style.height !== '' && t.media.style.height !== null) {
  1881. t.height = t.media.style.height;
  1882. } else if (t.$media[0].getAttribute('height') !== null) {
  1883. t.height = t.$media.attr('height');
  1884. } else {
  1885. t.height = t.options['default' + capsTagName + 'Height'];
  1886. }
  1887. // set the size, while we wait for the plugins to load below
  1888. t.setPlayerSize(t.width, t.height);
  1889. // create MediaElementShim
  1890. meOptions.pluginWidth = t.width;
  1891. meOptions.pluginHeight = t.height;
  1892. }
  1893. // create MediaElement shim
  1894. mejs.MediaElement(t.$media[0], meOptions);
  1895. if (typeof(t.container) != 'undefined' && t.controlsAreVisible){
  1896. // controls are shown when loaded
  1897. t.container.trigger('controlsshown');
  1898. }
  1899. },
  1900. showControls: function(doAnimation) {
  1901. var t = this;
  1902. doAnimation = typeof doAnimation == 'undefined' || doAnimation;
  1903. if (t.controlsAreVisible)
  1904. return;
  1905. if (doAnimation) {
  1906. t.controls
  1907. .css('visibility','visible')
  1908. .stop(true, true).fadeIn(200, function() {
  1909. t.controlsAreVisible = true;
  1910. t.container.trigger('controlsshown');
  1911. });
  1912. // any additional controls people might add and want to hide
  1913. t.container.find('.mejs-control')
  1914. .css('visibility','visible')
  1915. .stop(true, true).fadeIn(200, function() {t.controlsAreVisible = true;});
  1916. } else {
  1917. t.controls
  1918. .css('visibility','visible')
  1919. .css('display','block');
  1920. // any additional controls people might add and want to hide
  1921. t.container.find('.mejs-control')
  1922. .css('visibility','visible')
  1923. .css('display','block');
  1924. t.controlsAreVisible = true;
  1925. t.container.trigger('controlsshown');
  1926. }
  1927. t.setControlsSize();
  1928. },
  1929. hideControls: function(doAnimation) {
  1930. var t = this;
  1931. doAnimation = typeof doAnimation == 'undefined' || doAnimation;
  1932. if (!t.controlsAreVisible || t.options.alwaysShowControls)
  1933. return;
  1934. if (doAnimation) {
  1935. // fade out main controls
  1936. t.controls.stop(true, true).fadeOut(200, function() {
  1937. $(this)
  1938. .css('visibility','hidden')
  1939. .css('display','block');
  1940. t.controlsAreVisible = false;
  1941. t.container.trigger('controlshidden');
  1942. });
  1943. // any additional controls people might add and want to hide
  1944. t.container.find('.mejs-control').stop(true, true).fadeOut(200, function() {
  1945. $(this)
  1946. .css('visibility','hidden')
  1947. .css('display','block');
  1948. });
  1949. } else {
  1950. // hide main controls
  1951. t.controls
  1952. .css('visibility','hidden')
  1953. .css('display','block');
  1954. // hide others
  1955. t.container.find('.mejs-control')
  1956. .css('visibility','hidden')
  1957. .css('display','block');
  1958. t.controlsAreVisible = false;
  1959. t.container.trigger('controlshidden');
  1960. }
  1961. },
  1962. controlsTimer: null,
  1963. startControlsTimer: function(timeout) {
  1964. var t = this;
  1965. timeout = typeof timeout != 'undefined' ? timeout : 1500;
  1966. t.killControlsTimer('start');
  1967. t.controlsTimer = setTimeout(function() {
  1968. //
  1969. t.hideControls();
  1970. t.killControlsTimer('hide');
  1971. }, timeout);
  1972. },
  1973. killControlsTimer: function(src) {
  1974. var t = this;
  1975. if (t.controlsTimer !== null) {
  1976. clearTimeout(t.controlsTimer);
  1977. delete t.controlsTimer;
  1978. t.controlsTimer = null;
  1979. }
  1980. },
  1981. controlsEnabled: true,
  1982. disableControls: function() {
  1983. var t= this;
  1984. t.killControlsTimer();
  1985. t.hideControls(false);
  1986. this.controlsEnabled = false;
  1987. },
  1988. enableControls: function() {
  1989. var t= this;
  1990. t.showControls(false);
  1991. t.controlsEnabled = true;
  1992. },
  1993. // Sets up all controls and events
  1994. meReady: function(media, domNode) {
  1995. var t = this,
  1996. mf = mejs.MediaFeatures,
  1997. autoplayAttr = domNode.getAttribute('autoplay'),
  1998. autoplay = !(typeof autoplayAttr == 'undefined' || autoplayAttr === null || autoplayAttr === 'false'),
  1999. featureIndex,
  2000. feature;
  2001. // make sure it can't create itself again if a plugin reloads
  2002. if (t.created) {
  2003. return;
  2004. } else {
  2005. t.created = true;
  2006. }
  2007. t.media = media;
  2008. t.domNode = domNode;
  2009. if (!(mf.isAndroid && t.options.AndroidUseNativeControls) && !(mf.isiPad && t.options.iPadUseNativeControls) && !(mf.isiPhone && t.options.iPhoneUseNativeControls)) {
  2010. // two built in features
  2011. t.buildposter(t, t.controls, t.layers, t.media);
  2012. t.buildkeyboard(t, t.controls, t.layers, t.media);
  2013. t.buildoverlays(t, t.controls, t.layers, t.media);
  2014. // grab for use by features
  2015. t.findTracks();
  2016. // add user-defined features/controls
  2017. for (featureIndex in t.options.features) {
  2018. feature = t.options.features[featureIndex];
  2019. if (t['build' + feature]) {
  2020. try {
  2021. t['build' + feature](t, t.controls, t.layers, t.media);
  2022. } catch (e) {
  2023. // TODO: report control error
  2024. //throw e;
  2025. //
  2026. //
  2027. }
  2028. }
  2029. }
  2030. t.container.trigger('controlsready');
  2031. // reset all layers and controls
  2032. t.setPlayerSize(t.width, t.height);
  2033. t.setControlsSize();
  2034. // controls fade
  2035. if (t.isVideo) {
  2036. if (mejs.MediaFeatures.hasTouch) {
  2037. // for touch devices (iOS, Android)
  2038. // show/hide without animation on touch
  2039. t.$media.bind('touchstart', function() {
  2040. // toggle controls
  2041. if (t.controlsAreVisible) {
  2042. t.hideControls(false);
  2043. } else {
  2044. if (t.controlsEnabled) {
  2045. t.showControls(false);
  2046. }
  2047. }
  2048. });
  2049. } else {
  2050. // create callback here since it needs access to current
  2051. // MediaElement object
  2052. mejs.MediaElementPlayer.prototype.clickToPlayPauseCallback = function() {
  2053. //
  2054. if (t.options.clickToPlayPause) {
  2055. if (t.media.paused) {
  2056. t.play();
  2057. } else {
  2058. t.pause();
  2059. }
  2060. }
  2061. };
  2062. // click to play/pause
  2063. t.media.addEventListener('click', t.clickToPlayPauseCallback, false);
  2064. // show/hide controls
  2065. t.container
  2066. .bind('mouseenter mouseover', function () {
  2067. if (t.controlsEnabled) {
  2068. if (!t.options.alwaysShowControls) {
  2069. t.killControlsTimer('enter');
  2070. t.showControls();
  2071. t.startControlsTimer(2500);
  2072. }
  2073. }
  2074. })
  2075. .bind('mousemove', function() {
  2076. if (t.controlsEnabled) {
  2077. if (!t.controlsAreVisible) {
  2078. t.showControls();
  2079. }
  2080. //t.killControlsTimer('move');
  2081. if (!t.options.alwaysShowControls) {
  2082. t.startControlsTimer(2500);
  2083. }
  2084. }
  2085. })
  2086. .bind('mouseleave', function () {
  2087. if (t.controlsEnabled) {
  2088. if (!t.media.paused && !t.options.alwaysShowControls) {
  2089. t.startControlsTimer(1000);
  2090. }
  2091. }
  2092. });
  2093. }
  2094. if(t.options.hideVideoControlsOnLoad) {
  2095. t.hideControls(false);
  2096. }
  2097. // check for autoplay
  2098. if (autoplay && !t.options.alwaysShowControls) {
  2099. t.hideControls();
  2100. }
  2101. // resizer
  2102. if (t.options.enableAutosize) {
  2103. t.media.addEventListener('loadedmetadata', function(e) {
  2104. // if the <video height> was not set and the options.videoHeight was not set
  2105. // then resize to the real dimensions
  2106. if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) {
  2107. t.setPlayerSize(e.target.videoWidth, e.target.videoHeight);
  2108. t.setControlsSize();
  2109. t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight);
  2110. }
  2111. }, false);
  2112. }
  2113. }
  2114. // EVENTS
  2115. // FOCUS: when a video starts playing, it takes focus from other players (possibily pausing them)
  2116. media.addEventListener('play', function() {
  2117. var playerIndex;
  2118. // go through all other players
  2119. for (playerIndex in mejs.players) {
  2120. var p = mejs.players[playerIndex];
  2121. if (p.id != t.id && t.options.pauseOtherPlayers && !p.paused && !p.ended) {
  2122. p.pause();
  2123. }
  2124. p.hasFocus = false;
  2125. }
  2126. t.hasFocus = true;
  2127. },false);
  2128. // ended for all
  2129. t.media.addEventListener('ended', function (e) {
  2130. if(t.options.autoRewind) {
  2131. try{
  2132. t.media.setCurrentTime(0);
  2133. } catch (exp) {
  2134. }
  2135. }
  2136. t.media.pause();
  2137. if (t.setProgressRail) {
  2138. t.setProgressRail();
  2139. }
  2140. if (t.setCurrentRail) {
  2141. t.setCurrentRail();
  2142. }
  2143. if (t.options.loop) {
  2144. t.play();
  2145. } else if (!t.options.alwaysShowControls && t.controlsEnabled) {
  2146. t.showControls();
  2147. }
  2148. }, false);
  2149. // resize on the first play
  2150. t.media.addEventListener('loadedmetadata', function(e) {
  2151. if (t.updateDuration) {
  2152. t.updateDuration();
  2153. }
  2154. if (t.updateCurrent) {
  2155. t.updateCurrent();
  2156. }
  2157. if (!t.isFullScreen) {
  2158. t.setPlayerSize(t.width, t.height);
  2159. t.setControlsSize();
  2160. }
  2161. }, false);
  2162. // webkit has trouble doing this without a delay
  2163. setTimeout(function () {
  2164. t.setPlayerSize(t.width, t.height);
  2165. t.setControlsSize();
  2166. }, 50);
  2167. // adjust controls whenever window sizes (used to be in fullscreen only)
  2168. t.globalBind('resize', function() {
  2169. // don't resize for fullscreen mode
  2170. if ( !(t.isFullScreen || (mejs.MediaFeatures.hasTrueNativeFullScreen && document.webkitIsFullScreen)) ) {
  2171. t.setPlayerSize(t.width, t.height);
  2172. }
  2173. // always adjust controls
  2174. t.setControlsSize();
  2175. });
  2176. // TEMP: needs to be moved somewhere else
  2177. if (t.media.pluginType == 'youtube') {
  2178. t.container.find('.mejs-overlay-play').hide();
  2179. }
  2180. }
  2181. // force autoplay for HTML5
  2182. if (autoplay && media.pluginType == 'native') {
  2183. t.play();
  2184. }
  2185. if (t.options.success) {
  2186. if (typeof t.options.success == 'string') {
  2187. window[t.options.success](t.media, t.domNode, t);
  2188. } else {
  2189. t.options.success(t.media, t.domNode, t);
  2190. }
  2191. }
  2192. },
  2193. handleError: function(e) {
  2194. var t = this;
  2195. t.controls.hide();
  2196. // Tell user that the file cannot be played
  2197. if (t.options.error) {
  2198. t.options.error(e);
  2199. }
  2200. },
  2201. setPlayerSize: function(width,height) {
  2202. var t = this;
  2203. if (typeof width != 'undefined') {
  2204. t.width = width;
  2205. }
  2206. if (typeof height != 'undefined') {
  2207. t.height = height;
  2208. }
  2209. // detect 100% mode - use currentStyle for IE since css() doesn't return percentages
  2210. if (t.height.toString().indexOf('%') > 0 || t.$node.css('max-width') === '100%' || parseInt(t.$node.css('max-width').replace(/px/,''), 10) / t.$node.offsetParent().width() === 1 || (t.$node[0].currentStyle && t.$node[0].currentStyle.maxWidth === '100%')) {
  2211. // do we have the native dimensions yet?
  2212. var
  2213. nativeWidth = t.isVideo ? ((t.media.videoWidth && t.media.videoWidth > 0) ? t.media.videoWidth : t.options.defaultVideoWidth) : t.options.defaultAudioWidth,
  2214. nativeHeight = t.isVideo ? ((t.media.videoHeight && t.media.videoHeight > 0) ? t.media.videoHeight : t.options.defaultVideoHeight) : t.options.defaultAudioHeight,
  2215. parentWidth = t.container.parent().closest(':visible').width(),
  2216. newHeight = t.isVideo || !t.options.autosizeProgress ? parseInt(parentWidth * nativeHeight/nativeWidth, 10) : nativeHeight;
  2217. if (t.container.parent()[0].tagName.toLowerCase() === 'body') { // && t.container.siblings().count == 0) {
  2218. parentWidth = $(window).width();
  2219. newHeight = $(window).height();
  2220. }
  2221. if ( newHeight != 0 && parentWidth != 0 ) {
  2222. // set outer container size
  2223. t.container
  2224. .width(parentWidth)
  2225. .height(newHeight);
  2226. // set native <video> or <audio> and shims
  2227. t.$media.add(t.container.find('.mejs-shim'))
  2228. .width('100%')
  2229. .height('100%');
  2230. // if shim is ready, send the size to the embeded plugin
  2231. if (t.isVideo) {
  2232. if (t.media.setVideoSize) {
  2233. t.media.setVideoSize(parentWidth, newHeight);
  2234. }
  2235. }
  2236. // set the layers
  2237. t.layers.children('.mejs-layer')
  2238. .width('100%')
  2239. .height('100%');
  2240. }
  2241. } else {
  2242. t.container
  2243. .width(t.width)
  2244. .height(t.height);
  2245. t.layers.children('.mejs-layer')
  2246. .width(t.width)
  2247. .height(t.height);
  2248. }
  2249. // special case for big play button so it doesn't go over the controls area
  2250. var playLayer = t.layers.find('.mejs-overlay-play'),
  2251. playButton = playLayer.find('.mejs-overlay-button');
  2252. playLayer.height(t.container.height() - t.controls.height());
  2253. playButton.css('margin-top', '-' + (playButton.height()/2 - t.controls.height()/2).toString() + 'px' );
  2254. },
  2255. setControlsSize: function() {
  2256. var t = this,
  2257. usedWidth = 0,
  2258. railWidth = 0,
  2259. rail = t.controls.find('.mejs-time-rail'),
  2260. total = t.controls.find('.mejs-time-total'),
  2261. current = t.controls.find('.mejs-time-current'),
  2262. loaded = t.controls.find('.mejs-time-loaded'),
  2263. others = rail.siblings();
  2264. // allow the size to come from custom CSS
  2265. if (t.options && !t.options.autosizeProgress) {
  2266. // Also, frontends devs can be more flexible
  2267. // due the opportunity of absolute positioning.
  2268. railWidth = parseInt(rail.css('width'));
  2269. }
  2270. // attempt to autosize
  2271. if (railWidth === 0 || !railWidth) {
  2272. // find the size of all the other controls besides the rail
  2273. others.each(function() {
  2274. var $this = $(this);
  2275. if ($this.css('position') != 'absolute' && $this.is(':visible')) {
  2276. usedWidth += $(this).outerWidth(true);
  2277. }
  2278. });
  2279. // fit the rail into the remaining space
  2280. railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.width());
  2281. }
  2282. // outer area
  2283. rail.width(railWidth);
  2284. // dark space
  2285. total.width(railWidth - (total.outerWidth(true) - total.width()));
  2286. if (t.setProgressRail)
  2287. t.setProgressRail();
  2288. if (t.setCurrentRail)
  2289. t.setCurrentRail();
  2290. },
  2291. buildposter: function(player, controls, layers, media) {
  2292. var t = this,
  2293. poster =
  2294. $('<div class="mejs-poster mejs-layer">' +
  2295. '</div>')
  2296. .appendTo(layers),
  2297. posterUrl = player.$media.attr('poster');
  2298. // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster)
  2299. if (player.options.poster !== '') {
  2300. posterUrl = player.options.poster;
  2301. }
  2302. // second, try the real poster
  2303. if (posterUrl !== '' && posterUrl != null) {
  2304. t.setPoster(posterUrl);
  2305. } else {
  2306. poster.hide();
  2307. }
  2308. media.addEventListener('play',function() {
  2309. poster.hide();
  2310. }, false);
  2311. if(player.options.showPosterWhenEnded && player.options.autoRewind){
  2312. media.addEventListener('ended',function() {
  2313. poster.show();
  2314. }, false);
  2315. }
  2316. },
  2317. setPoster: function(url) {
  2318. var t = this,
  2319. posterDiv = t.container.find('.mejs-poster'),
  2320. posterImg = posterDiv.find('img');
  2321. if (posterImg.length == 0) {
  2322. posterImg = $('<img width="100%" height="100%" />').appendTo(posterDiv);
  2323. }
  2324. posterImg.attr('src', url);
  2325. posterDiv.css({'background-image' : 'url(' + url + ')'});
  2326. },
  2327. buildoverlays: function(player, controls, layers, media) {
  2328. var t = this;
  2329. if (!player.isVideo)
  2330. return;
  2331. var
  2332. loading =
  2333. $('<div class="mejs-overlay mejs-layer">'+
  2334. '<div class="mejs-overlay-loading"><span></span></div>'+
  2335. '</div>')
  2336. .hide() // start out hidden
  2337. .appendTo(layers),
  2338. error =
  2339. $('<div class="mejs-overlay mejs-layer">'+
  2340. '<div class="mejs-overlay-error"></div>'+
  2341. '</div>')
  2342. .hide() // start out hidden
  2343. .appendTo(layers),
  2344. // this needs to come last so it's on top
  2345. bigPlay =
  2346. $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+
  2347. '<div class="mejs-overlay-button"></div>'+
  2348. '</div>')
  2349. .appendTo(layers)
  2350. .bind('click touchstart', function() {
  2351. if (t.options.clickToPlayPause) {
  2352. if (media.paused) {
  2353. t.play();
  2354. }
  2355. }
  2356. });
  2357. /*
  2358. if (mejs.MediaFeatures.isiOS || mejs.MediaFeatures.isAndroid) {
  2359. bigPlay.remove();
  2360. loading.remove();
  2361. }
  2362. */
  2363. // show/hide big play button
  2364. media.addEventListener('play',function() {
  2365. bigPlay.hide();
  2366. loading.hide();
  2367. controls.find('.mejs-time-buffering').hide();
  2368. error.hide();
  2369. }, false);
  2370. media.addEventListener('playing', function() {
  2371. bigPlay.hide();
  2372. loading.hide();
  2373. controls.find('.mejs-time-buffering').hide();
  2374. error.hide();
  2375. }, false);
  2376. media.addEventListener('seeking', function() {
  2377. loading.show();
  2378. controls.find('.mejs-time-buffering').show();
  2379. }, false);
  2380. media.addEventListener('seeked', function() {
  2381. loading.hide();
  2382. controls.find('.mejs-time-buffering').hide();
  2383. }, false);
  2384. media.addEventListener('pause',function() {
  2385. if (!mejs.MediaFeatures.isiPhone) {
  2386. bigPlay.show();
  2387. }
  2388. }, false);
  2389. media.addEventListener('waiting', function() {
  2390. loading.show();
  2391. controls.find('.mejs-time-buffering').show();
  2392. }, false);
  2393. // show/hide loading
  2394. media.addEventListener('loadeddata',function() {
  2395. // for some reason Chrome is firing this event
  2396. //if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none')
  2397. // return;
  2398. loading.show();
  2399. controls.find('.mejs-time-buffering').show();
  2400. }, false);
  2401. media.addEventListener('canplay',function() {
  2402. loading.hide();
  2403. controls.find('.mejs-time-buffering').hide();
  2404. }, false);
  2405. // error handling
  2406. media.addEventListener('error',function() {
  2407. loading.hide();
  2408. controls.find('.mejs-time-buffering').hide();
  2409. error.show();
  2410. error.find('mejs-overlay-error').html("Error loading this resource");
  2411. }, false);
  2412. },
  2413. buildkeyboard: function(player, controls, layers, media) {
  2414. var t = this;
  2415. // listen for key presses
  2416. t.globalBind('keydown', function(e) {
  2417. if (player.hasFocus && player.options.enableKeyboard) {
  2418. // find a matching key
  2419. for (var i=0, il=player.options.keyActions.length; i<il; i++) {
  2420. var keyAction = player.options.keyActions[i];
  2421. for (var j=0, jl=keyAction.keys.length; j<jl; j++) {
  2422. if (e.keyCode == keyAction.keys[j]) {
  2423. e.preventDefault();
  2424. keyAction.action(player, media, e.keyCode);
  2425. return false;
  2426. }
  2427. }
  2428. }
  2429. }
  2430. return true;
  2431. });
  2432. // check if someone clicked outside a player region, then kill its focus
  2433. t.globalBind('click', function(event) {
  2434. if ($(event.target).closest('.mejs-container').length == 0) {
  2435. player.hasFocus = false;
  2436. }
  2437. });
  2438. },
  2439. findTracks: function() {
  2440. var t = this,
  2441. tracktags = t.$media.find('track');
  2442. // store for use by plugins
  2443. t.tracks = [];
  2444. tracktags.each(function(index, track) {
  2445. track = $(track);
  2446. t.tracks.push({
  2447. srclang: (track.attr('srclang')) ? track.attr('srclang').toLowerCase() : '',
  2448. src: track.attr('src'),
  2449. kind: track.attr('kind'),
  2450. label: track.attr('label') || '',
  2451. entries: [],
  2452. isLoaded: false
  2453. });
  2454. });
  2455. },
  2456. changeSkin: function(className) {
  2457. this.container[0].className = 'mejs-container ' + className;
  2458. this.setPlayerSize(this.width, this.height);
  2459. this.setControlsSize();
  2460. },
  2461. play: function() {
  2462. this.load();
  2463. this.media.play();
  2464. },
  2465. pause: function() {
  2466. try {
  2467. this.media.pause();
  2468. } catch (e) {}
  2469. },
  2470. load: function() {
  2471. if (!this.isLoaded) {
  2472. this.media.load();
  2473. }
  2474. this.isLoaded = true;
  2475. },
  2476. setMuted: function(muted) {
  2477. this.media.setMuted(muted);
  2478. },
  2479. setCurrentTime: function(time) {
  2480. this.media.setCurrentTime(time);
  2481. },
  2482. getCurrentTime: function() {
  2483. return this.media.currentTime;
  2484. },
  2485. setVolume: function(volume) {
  2486. this.media.setVolume(volume);
  2487. },
  2488. getVolume: function() {
  2489. return this.media.volume;
  2490. },
  2491. setSrc: function(src) {
  2492. this.media.setSrc(src);
  2493. },
  2494. remove: function() {
  2495. var t = this, featureIndex, feature;
  2496. // invoke features cleanup
  2497. for (featureIndex in t.options.features) {
  2498. feature = t.options.features[featureIndex];
  2499. if (t['clean' + feature]) {
  2500. try {
  2501. t['clean' + feature](t);
  2502. } catch (e) {
  2503. // TODO: report control error
  2504. //throw e;
  2505. //
  2506. //
  2507. }
  2508. }
  2509. }
  2510. // grab video and put it back in place
  2511. if (!t.isDynamic) {
  2512. t.$media.prop('controls', true);
  2513. // detach events from the video
  2514. // TODO: detach event listeners better than this;
  2515. // also detach ONLY the events attached by this plugin!
  2516. t.$node.clone().show().insertBefore(t.container);
  2517. t.$node.remove();
  2518. } else {
  2519. t.$node.insertBefore(t.container);
  2520. }
  2521. if (t.media.pluginType !== 'native') {
  2522. t.media.remove();
  2523. }
  2524. // Remove the player from the mejs.players object so that pauseOtherPlayers doesn't blow up when trying to pause a non existance flash api.
  2525. delete mejs.players[t.id];
  2526. t.container.remove();
  2527. t.globalUnbind();
  2528. delete t.node.player;
  2529. }
  2530. };
  2531. (function(){
  2532. var rwindow = /^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;
  2533. function splitEvents(events, id) {
  2534. // add player ID as an event namespace so it's easier to unbind them all later
  2535. var ret = {d: [], w: []};
  2536. $.each((events || '').split(' '), function(k, v){
  2537. var eventname = v + '.' + id;
  2538. if (eventname.indexOf('.') === 0) {
  2539. ret.d.push(eventname);
  2540. ret.w.push(eventname);
  2541. }
  2542. else {
  2543. ret[rwindow.test(v) ? 'w' : 'd'].push(eventname);
  2544. }
  2545. });
  2546. ret.d = ret.d.join(' ');
  2547. ret.w = ret.w.join(' ');
  2548. return ret;
  2549. }
  2550. mejs.MediaElementPlayer.prototype.globalBind = function(events, data, callback) {
  2551. var t = this;
  2552. events = splitEvents(events, t.id);
  2553. if (events.d) $(document).bind(events.d, data, callback);
  2554. if (events.w) $(window).bind(events.w, data, callback);
  2555. };
  2556. mejs.MediaElementPlayer.prototype.globalUnbind = function(events, callback) {
  2557. var t = this;
  2558. events = splitEvents(events, t.id);
  2559. if (events.d) $(document).unbind(events.d, callback);
  2560. if (events.w) $(window).unbind(events.w, callback);
  2561. };
  2562. })();
  2563. // turn into jQuery plugin
  2564. if (typeof jQuery != 'undefined') {
  2565. jQuery.fn.mediaelementplayer = function (options) {
  2566. if (options === false) {
  2567. this.each(function () {
  2568. var player = jQuery(this).data('mediaelementplayer');
  2569. if (player) {
  2570. player.remove();
  2571. }
  2572. jQuery(this).removeData('mediaelementplayer');
  2573. });
  2574. }
  2575. else {
  2576. this.each(function () {
  2577. jQuery(this).data('mediaelementplayer', new mejs.MediaElementPlayer(this, options));
  2578. });
  2579. }
  2580. return this;
  2581. };
  2582. }
  2583. $(document).ready(function() {
  2584. // auto enable using JSON attribute
  2585. $('.mejs-player').mediaelementplayer();
  2586. });
  2587. // push out to window
  2588. window.MediaElementPlayer = mejs.MediaElementPlayer;
  2589. })(mejs.$);
  2590. (function($) {
  2591. $.extend(mejs.MepDefaults, {
  2592. playpauseText: mejs.i18n.t('Play/Pause')
  2593. });
  2594. // PLAY/pause BUTTON
  2595. $.extend(MediaElementPlayer.prototype, {
  2596. buildplaypause: function(player, controls, layers, media) {
  2597. var
  2598. t = this,
  2599. play =
  2600. $('<div class="mejs-button mejs-playpause-button mejs-play" >' +
  2601. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.playpauseText + '" aria-label="' + t.options.playpauseText + '"></button>' +
  2602. '</div>')
  2603. .appendTo(controls)
  2604. .click(function(e) {
  2605. e.preventDefault();
  2606. if (media.paused) {
  2607. media.play();
  2608. } else {
  2609. media.pause();
  2610. }
  2611. return false;
  2612. });
  2613. media.addEventListener('play',function() {
  2614. play.removeClass('mejs-play').addClass('mejs-pause');
  2615. }, false);
  2616. media.addEventListener('playing',function() {
  2617. play.removeClass('mejs-play').addClass('mejs-pause');
  2618. }, false);
  2619. media.addEventListener('pause',function() {
  2620. play.removeClass('mejs-pause').addClass('mejs-play');
  2621. }, false);
  2622. media.addEventListener('paused',function() {
  2623. play.removeClass('mejs-pause').addClass('mejs-play');
  2624. }, false);
  2625. }
  2626. });
  2627. })(mejs.$);
  2628. (function($) {
  2629. $.extend(mejs.MepDefaults, {
  2630. stopText: 'Stop'
  2631. });
  2632. // STOP BUTTON
  2633. $.extend(MediaElementPlayer.prototype, {
  2634. buildstop: function(player, controls, layers, media) {
  2635. var t = this,
  2636. stop =
  2637. $('<div class="mejs-button mejs-stop-button mejs-stop">' +
  2638. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.stopText + '" aria-label="' + t.options.stopText + '"></button>' +
  2639. '</div>')
  2640. .appendTo(controls)
  2641. .click(function() {
  2642. if (!media.paused) {
  2643. media.pause();
  2644. }
  2645. if (media.currentTime > 0) {
  2646. media.setCurrentTime(0);
  2647. media.pause();
  2648. controls.find('.mejs-time-current').width('0px');
  2649. controls.find('.mejs-time-handle').css('left', '0px');
  2650. controls.find('.mejs-time-float-current').html( mejs.Utility.secondsToTimeCode(0) );
  2651. controls.find('.mejs-currenttime').html( mejs.Utility.secondsToTimeCode(0) );
  2652. layers.find('.mejs-poster').show();
  2653. }
  2654. });
  2655. }
  2656. });
  2657. })(mejs.$);
  2658. (function($) {
  2659. // progress/loaded bar
  2660. $.extend(MediaElementPlayer.prototype, {
  2661. buildprogress: function(player, controls, layers, media) {
  2662. $('<div class="mejs-time-rail">'+
  2663. '<span class="mejs-time-total">'+
  2664. '<span class="mejs-time-buffering"></span>'+
  2665. '<span class="mejs-time-loaded"></span>'+
  2666. '<span class="mejs-time-current"></span>'+
  2667. '<span class="mejs-time-handle"></span>'+
  2668. '<span class="mejs-time-float">' +
  2669. '<span class="mejs-time-float-current">00:00</span>' +
  2670. '<span class="mejs-time-float-corner"></span>' +
  2671. '</span>'+
  2672. '</span>'+
  2673. '</div>')
  2674. .appendTo(controls);
  2675. controls.find('.mejs-time-buffering').hide();
  2676. var
  2677. t = this,
  2678. total = controls.find('.mejs-time-total'),
  2679. loaded = controls.find('.mejs-time-loaded'),
  2680. current = controls.find('.mejs-time-current'),
  2681. handle = controls.find('.mejs-time-handle'),
  2682. timefloat = controls.find('.mejs-time-float'),
  2683. timefloatcurrent = controls.find('.mejs-time-float-current'),
  2684. handleMouseMove = function (e) {
  2685. // mouse position relative to the object
  2686. var x = e.pageX,
  2687. offset = total.offset(),
  2688. width = total.outerWidth(true),
  2689. percentage = 0,
  2690. newTime = 0,
  2691. pos = 0;
  2692. if (media.duration) {
  2693. if (x < offset.left) {
  2694. x = offset.left;
  2695. } else if (x > width + offset.left) {
  2696. x = width + offset.left;
  2697. }
  2698. pos = x - offset.left;
  2699. percentage = (pos / width);
  2700. newTime = (percentage <= 0.02) ? 0 : percentage * media.duration;
  2701. // seek to where the mouse is
  2702. if (mouseIsDown && newTime !== media.currentTime) {
  2703. media.setCurrentTime(newTime);
  2704. }
  2705. // position floating time box
  2706. if (!mejs.MediaFeatures.hasTouch) {
  2707. timefloat.css('left', pos);
  2708. timefloatcurrent.html( mejs.Utility.secondsToTimeCode(newTime) );
  2709. timefloat.show();
  2710. }
  2711. }
  2712. },
  2713. mouseIsDown = false,
  2714. mouseIsOver = false;
  2715. // handle clicks
  2716. //controls.find('.mejs-time-rail').delegate('span', 'click', handleMouseMove);
  2717. total
  2718. .bind('mousedown', function (e) {
  2719. // only handle left clicks
  2720. if (e.which === 1) {
  2721. mouseIsDown = true;
  2722. handleMouseMove(e);
  2723. t.globalBind('mousemove.dur', function(e) {
  2724. handleMouseMove(e);
  2725. });
  2726. t.globalBind('mouseup.dur', function (e) {
  2727. mouseIsDown = false;
  2728. timefloat.hide();
  2729. t.globalUnbind('.dur');
  2730. });
  2731. return false;
  2732. }
  2733. })
  2734. .bind('mouseenter', function(e) {
  2735. mouseIsOver = true;
  2736. t.globalBind('mousemove.dur', function(e) {
  2737. handleMouseMove(e);
  2738. });
  2739. if (!mejs.MediaFeatures.hasTouch) {
  2740. timefloat.show();
  2741. }
  2742. })
  2743. .bind('mouseleave',function(e) {
  2744. mouseIsOver = false;
  2745. if (!mouseIsDown) {
  2746. t.globalUnbind('.dur');
  2747. timefloat.hide();
  2748. }
  2749. });
  2750. // loading
  2751. media.addEventListener('progress', function (e) {
  2752. player.setProgressRail(e);
  2753. player.setCurrentRail(e);
  2754. }, false);
  2755. // current time
  2756. media.addEventListener('timeupdate', function(e) {
  2757. player.setProgressRail(e);
  2758. player.setCurrentRail(e);
  2759. }, false);
  2760. // store for later use
  2761. t.loaded = loaded;
  2762. t.total = total;
  2763. t.current = current;
  2764. t.handle = handle;
  2765. },
  2766. setProgressRail: function(e) {
  2767. var
  2768. t = this,
  2769. target = (e != undefined) ? e.target : t.media,
  2770. percent = null;
  2771. // newest HTML5 spec has buffered array (FF4, Webkit)
  2772. if (target && target.buffered && target.buffered.length > 0 && target.buffered.end && target.duration) {
  2773. // TODO: account for a real array with multiple values (only Firefox 4 has this so far)
  2774. percent = target.buffered.end(0) / target.duration;
  2775. }
  2776. // Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
  2777. // to be anything other than 0. If the byte count is available we use this instead.
  2778. // Browsers that support the else if do not seem to have the bufferedBytes value and
  2779. // should skip to there. Tested in Safari 5, Webkit head, FF3.6, Chrome 6, IE 7/8.
  2780. else if (target && target.bytesTotal != undefined && target.bytesTotal > 0 && target.bufferedBytes != undefined) {
  2781. percent = target.bufferedBytes / target.bytesTotal;
  2782. }
  2783. // Firefox 3 with an Ogg file seems to go this way
  2784. else if (e && e.lengthComputable && e.total != 0) {
  2785. percent = e.loaded/e.total;
  2786. }
  2787. // finally update the progress bar
  2788. if (percent !== null) {
  2789. percent = Math.min(1, Math.max(0, percent));
  2790. // update loaded bar
  2791. if (t.loaded && t.total) {
  2792. t.loaded.width(t.total.width() * percent);
  2793. }
  2794. }
  2795. },
  2796. setCurrentRail: function() {
  2797. var t = this;
  2798. if (t.media.currentTime != undefined && t.media.duration) {
  2799. // update bar and handle
  2800. if (t.total && t.handle) {
  2801. var
  2802. newWidth = Math.round(t.total.width() * t.media.currentTime / t.media.duration),
  2803. handlePos = newWidth - Math.round(t.handle.outerWidth(true) / 2);
  2804. t.current.width(newWidth);
  2805. t.handle.css('left', handlePos);
  2806. }
  2807. }
  2808. }
  2809. });
  2810. })(mejs.$);
  2811. (function($) {
  2812. // options
  2813. $.extend(mejs.MepDefaults, {
  2814. duration: -1,
  2815. timeAndDurationSeparator: '<span> | </span>'
  2816. });
  2817. // current and duration 00:00 / 00:00
  2818. $.extend(MediaElementPlayer.prototype, {
  2819. buildcurrent: function(player, controls, layers, media) {
  2820. var t = this;
  2821. $('<div class="mejs-time">'+
  2822. '<span class="mejs-currenttime">' + (player.options.alwaysShowHours ? '00:' : '')
  2823. + (player.options.showTimecodeFrameCount? '00:00:00':'00:00')+ '</span>'+
  2824. '</div>')
  2825. .appendTo(controls);
  2826. t.currenttime = t.controls.find('.mejs-currenttime');
  2827. media.addEventListener('timeupdate',function() {
  2828. player.updateCurrent();
  2829. }, false);
  2830. },
  2831. buildduration: function(player, controls, layers, media) {
  2832. var t = this;
  2833. if (controls.children().last().find('.mejs-currenttime').length > 0) {
  2834. $(t.options.timeAndDurationSeparator +
  2835. '<span class="mejs-duration">' +
  2836. (t.options.duration > 0 ?
  2837. mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
  2838. ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
  2839. ) +
  2840. '</span>')
  2841. .appendTo(controls.find('.mejs-time'));
  2842. } else {
  2843. // add class to current time
  2844. controls.find('.mejs-currenttime').parent().addClass('mejs-currenttime-container');
  2845. $('<div class="mejs-time mejs-duration-container">'+
  2846. '<span class="mejs-duration">' +
  2847. (t.options.duration > 0 ?
  2848. mejs.Utility.secondsToTimeCode(t.options.duration, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25) :
  2849. ((player.options.alwaysShowHours ? '00:' : '') + (player.options.showTimecodeFrameCount? '00:00:00':'00:00'))
  2850. ) +
  2851. '</span>' +
  2852. '</div>')
  2853. .appendTo(controls);
  2854. }
  2855. t.durationD = t.controls.find('.mejs-duration');
  2856. media.addEventListener('timeupdate',function() {
  2857. player.updateDuration();
  2858. }, false);
  2859. },
  2860. updateCurrent: function() {
  2861. var t = this;
  2862. if (t.currenttime) {
  2863. t.currenttime.html(mejs.Utility.secondsToTimeCode(t.media.currentTime, t.options.alwaysShowHours || t.media.duration > 3600, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
  2864. }
  2865. },
  2866. updateDuration: function() {
  2867. var t = this;
  2868. //Toggle the long video class if the video is longer than an hour.
  2869. t.container.toggleClass("mejs-long-video", t.media.duration > 3600);
  2870. if (t.durationD && (t.options.duration > 0 || t.media.duration)) {
  2871. t.durationD.html(mejs.Utility.secondsToTimeCode(t.options.duration > 0 ? t.options.duration : t.media.duration, t.options.alwaysShowHours, t.options.showTimecodeFrameCount, t.options.framesPerSecond || 25));
  2872. }
  2873. }
  2874. });
  2875. })(mejs.$);
  2876. (function($) {
  2877. $.extend(mejs.MepDefaults, {
  2878. muteText: mejs.i18n.t('Mute Toggle'),
  2879. hideVolumeOnTouchDevices: true,
  2880. audioVolume: 'horizontal',
  2881. videoVolume: 'vertical'
  2882. });
  2883. $.extend(MediaElementPlayer.prototype, {
  2884. buildvolume: function(player, controls, layers, media) {
  2885. // Android and iOS don't support volume controls
  2886. if (mejs.MediaFeatures.hasTouch && this.options.hideVolumeOnTouchDevices)
  2887. return;
  2888. var t = this,
  2889. mode = (t.isVideo) ? t.options.videoVolume : t.options.audioVolume,
  2890. mute = (mode == 'horizontal') ?
  2891. // horizontal version
  2892. $('<div class="mejs-button mejs-volume-button mejs-mute">'+
  2893. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '" aria-label="' + t.options.muteText + '"></button>'+
  2894. '</div>' +
  2895. '<div class="mejs-horizontal-volume-slider">'+ // outer background
  2896. '<div class="mejs-horizontal-volume-total"></div>'+ // line background
  2897. '<div class="mejs-horizontal-volume-current"></div>'+ // current volume
  2898. '<div class="mejs-horizontal-volume-handle"></div>'+ // handle
  2899. '</div>'
  2900. )
  2901. .appendTo(controls) :
  2902. // vertical version
  2903. $('<div class="mejs-button mejs-volume-button mejs-mute">'+
  2904. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.muteText + '" aria-label="' + t.options.muteText + '"></button>'+
  2905. '<div class="mejs-volume-slider">'+ // outer background
  2906. '<div class="mejs-volume-total"></div>'+ // line background
  2907. '<div class="mejs-volume-current"></div>'+ // current volume
  2908. '<div class="mejs-volume-handle"></div>'+ // handle
  2909. '</div>'+
  2910. '</div>')
  2911. .appendTo(controls),
  2912. volumeSlider = t.container.find('.mejs-volume-slider, .mejs-horizontal-volume-slider'),
  2913. volumeTotal = t.container.find('.mejs-volume-total, .mejs-horizontal-volume-total'),
  2914. volumeCurrent = t.container.find('.mejs-volume-current, .mejs-horizontal-volume-current'),
  2915. volumeHandle = t.container.find('.mejs-volume-handle, .mejs-horizontal-volume-handle'),
  2916. positionVolumeHandle = function(volume, secondTry) {
  2917. if (!volumeSlider.is(':visible') && typeof secondTry == 'undefined') {
  2918. volumeSlider.show();
  2919. positionVolumeHandle(volume, true);
  2920. volumeSlider.hide()
  2921. return;
  2922. }
  2923. // correct to 0-1
  2924. volume = Math.max(0,volume);
  2925. volume = Math.min(volume,1);
  2926. // ajust mute button style
  2927. if (volume == 0) {
  2928. mute.removeClass('mejs-mute').addClass('mejs-unmute');
  2929. } else {
  2930. mute.removeClass('mejs-unmute').addClass('mejs-mute');
  2931. }
  2932. // position slider
  2933. if (mode == 'vertical') {
  2934. var
  2935. // height of the full size volume slider background
  2936. totalHeight = volumeTotal.height(),
  2937. // top/left of full size volume slider background
  2938. totalPosition = volumeTotal.position(),
  2939. // the new top position based on the current volume
  2940. // 70% volume on 100px height == top:30px
  2941. newTop = totalHeight - (totalHeight * volume);
  2942. // handle
  2943. volumeHandle.css('top', Math.round(totalPosition.top + newTop - (volumeHandle.height() / 2)));
  2944. // show the current visibility
  2945. volumeCurrent.height(totalHeight - newTop );
  2946. volumeCurrent.css('top', totalPosition.top + newTop);
  2947. } else {
  2948. var
  2949. // height of the full size volume slider background
  2950. totalWidth = volumeTotal.width(),
  2951. // top/left of full size volume slider background
  2952. totalPosition = volumeTotal.position(),
  2953. // the new left position based on the current volume
  2954. newLeft = totalWidth * volume;
  2955. // handle
  2956. volumeHandle.css('left', Math.round(totalPosition.left + newLeft - (volumeHandle.width() / 2)));
  2957. // rezize the current part of the volume bar
  2958. volumeCurrent.width( Math.round(newLeft) );
  2959. }
  2960. },
  2961. handleVolumeMove = function(e) {
  2962. var volume = null,
  2963. totalOffset = volumeTotal.offset();
  2964. // calculate the new volume based on the moust position
  2965. if (mode == 'vertical') {
  2966. var
  2967. railHeight = volumeTotal.height(),
  2968. totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
  2969. newY = e.pageY - totalOffset.top;
  2970. volume = (railHeight - newY) / railHeight;
  2971. // the controls just hide themselves (usually when mouse moves too far up)
  2972. if (totalOffset.top == 0 || totalOffset.left == 0)
  2973. return;
  2974. } else {
  2975. var
  2976. railWidth = volumeTotal.width(),
  2977. newX = e.pageX - totalOffset.left;
  2978. volume = newX / railWidth;
  2979. }
  2980. // ensure the volume isn't outside 0-1
  2981. volume = Math.max(0,volume);
  2982. volume = Math.min(volume,1);
  2983. // position the slider and handle
  2984. positionVolumeHandle(volume);
  2985. // set the media object (this will trigger the volumechanged event)
  2986. if (volume == 0) {
  2987. media.setMuted(true);
  2988. } else {
  2989. media.setMuted(false);
  2990. }
  2991. media.setVolume(volume);
  2992. },
  2993. mouseIsDown = false,
  2994. mouseIsOver = false;
  2995. // SLIDER
  2996. mute
  2997. .hover(function() {
  2998. volumeSlider.show();
  2999. mouseIsOver = true;
  3000. }, function() {
  3001. mouseIsOver = false;
  3002. if (!mouseIsDown && mode == 'vertical') {
  3003. volumeSlider.hide();
  3004. }
  3005. });
  3006. volumeSlider
  3007. .bind('mouseover', function() {
  3008. mouseIsOver = true;
  3009. })
  3010. .bind('mousedown', function (e) {
  3011. handleVolumeMove(e);
  3012. t.globalBind('mousemove.vol', function(e) {
  3013. handleVolumeMove(e);
  3014. });
  3015. t.globalBind('mouseup.vol', function () {
  3016. mouseIsDown = false;
  3017. t.globalUnbind('.vol');
  3018. if (!mouseIsOver && mode == 'vertical') {
  3019. volumeSlider.hide();
  3020. }
  3021. });
  3022. mouseIsDown = true;
  3023. return false;
  3024. });
  3025. // MUTE button
  3026. mute.find('button').click(function() {
  3027. media.setMuted( !media.muted );
  3028. });
  3029. // listen for volume change events from other sources
  3030. media.addEventListener('volumechange', function(e) {
  3031. if (!mouseIsDown) {
  3032. if (media.muted) {
  3033. positionVolumeHandle(0);
  3034. mute.removeClass('mejs-mute').addClass('mejs-unmute');
  3035. } else {
  3036. positionVolumeHandle(media.volume);
  3037. mute.removeClass('mejs-unmute').addClass('mejs-mute');
  3038. }
  3039. }
  3040. }, false);
  3041. if (t.container.is(':visible')) {
  3042. // set initial volume
  3043. positionVolumeHandle(player.options.startVolume);
  3044. // mutes the media and sets the volume icon muted if the initial volume is set to 0
  3045. if (player.options.startVolume === 0) {
  3046. media.setMuted(true);
  3047. }
  3048. // shim gets the startvolume as a parameter, but we have to set it on the native <video> and <audio> elements
  3049. if (media.pluginType === 'native') {
  3050. media.setVolume(player.options.startVolume);
  3051. }
  3052. }
  3053. }
  3054. });
  3055. })(mejs.$);
  3056. (function($) {
  3057. $.extend(mejs.MepDefaults, {
  3058. usePluginFullScreen: true,
  3059. newWindowCallback: function() { return '';},
  3060. fullscreenText: mejs.i18n.t('Fullscreen')
  3061. });
  3062. $.extend(MediaElementPlayer.prototype, {
  3063. isFullScreen: false,
  3064. isNativeFullScreen: false,
  3065. isInIframe: false,
  3066. buildfullscreen: function(player, controls, layers, media) {
  3067. if (!player.isVideo)
  3068. return;
  3069. player.isInIframe = (window.location != window.parent.location);
  3070. // native events
  3071. if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
  3072. // chrome doesn't alays fire this in an iframe
  3073. var func = function(e) {
  3074. if (player.isFullScreen) {
  3075. if (mejs.MediaFeatures.isFullScreen()) {
  3076. player.isNativeFullScreen = true;
  3077. // reset the controls once we are fully in full screen
  3078. player.setControlsSize();
  3079. } else {
  3080. player.isNativeFullScreen = false;
  3081. // when a user presses ESC
  3082. // make sure to put the player back into place
  3083. player.exitFullScreen();
  3084. }
  3085. }
  3086. };
  3087. if (mejs.MediaFeatures.hasMozNativeFullScreen) {
  3088. player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
  3089. } else {
  3090. player.container.bind(mejs.MediaFeatures.fullScreenEventName, func);
  3091. }
  3092. }
  3093. var t = this,
  3094. normalHeight = 0,
  3095. normalWidth = 0,
  3096. container = player.container,
  3097. fullscreenBtn =
  3098. $('<div class="mejs-button mejs-fullscreen-button">' +
  3099. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' +
  3100. '</div>')
  3101. .appendTo(controls);
  3102. if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {
  3103. fullscreenBtn.click(function() {
  3104. var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;
  3105. if (isFullScreen) {
  3106. player.exitFullScreen();
  3107. } else {
  3108. player.enterFullScreen();
  3109. }
  3110. });
  3111. } else {
  3112. var hideTimeout = null,
  3113. supportsPointerEvents = (function() {
  3114. // TAKEN FROM MODERNIZR
  3115. var element = document.createElement('x'),
  3116. documentElement = document.documentElement,
  3117. getComputedStyle = window.getComputedStyle,
  3118. supports;
  3119. if(!('pointerEvents' in element.style)){
  3120. return false;
  3121. }
  3122. element.style.pointerEvents = 'auto';
  3123. element.style.pointerEvents = 'x';
  3124. documentElement.appendChild(element);
  3125. supports = getComputedStyle &&
  3126. getComputedStyle(element, '').pointerEvents === 'auto';
  3127. documentElement.removeChild(element);
  3128. return !!supports;
  3129. })();
  3130. //
  3131. if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(
  3132. // allows clicking through the fullscreen button and controls down directly to Flash
  3133. /*
  3134. When a user puts his mouse over the fullscreen button, the controls are disabled
  3135. So we put a div over the video and another one on iether side of the fullscreen button
  3136. that caputre mouse movement
  3137. and restore the controls once the mouse moves outside of the fullscreen button
  3138. */
  3139. var fullscreenIsDisabled = false,
  3140. restoreControls = function() {
  3141. if (fullscreenIsDisabled) {
  3142. // hide the hovers
  3143. for (var i in hoverDivs) {
  3144. hoverDivs[i].hide();
  3145. }
  3146. // restore the control bar
  3147. fullscreenBtn.css('pointer-events', '');
  3148. t.controls.css('pointer-events', '');
  3149. // prevent clicks from pausing video
  3150. t.media.removeEventListener('click', t.clickToPlayPauseCallback);
  3151. // store for later
  3152. fullscreenIsDisabled = false;
  3153. }
  3154. },
  3155. hoverDivs = {},
  3156. hoverDivNames = ['top', 'left', 'right', 'bottom'],
  3157. i, len,
  3158. positionHoverDivs = function() {
  3159. var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
  3160. fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
  3161. fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
  3162. fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
  3163. containerWidth = t.container.width(),
  3164. containerHeight = t.container.height();
  3165. for (i in hoverDivs) {
  3166. hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
  3167. }
  3168. // over video, but not controls
  3169. hoverDivs['top']
  3170. .width( containerWidth )
  3171. .height( fullScreenBtnOffsetTop );
  3172. // over controls, but not the fullscreen button
  3173. hoverDivs['left']
  3174. .width( fullScreenBtnOffsetLeft )
  3175. .height( fullScreenBtnHeight )
  3176. .css({top: fullScreenBtnOffsetTop});
  3177. // after the fullscreen button
  3178. hoverDivs['right']
  3179. .width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
  3180. .height( fullScreenBtnHeight )
  3181. .css({top: fullScreenBtnOffsetTop,
  3182. left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});
  3183. // under the fullscreen button
  3184. hoverDivs['bottom']
  3185. .width( containerWidth )
  3186. .height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
  3187. .css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
  3188. };
  3189. t.globalBind('resize', function() {
  3190. positionHoverDivs();
  3191. });
  3192. for (i = 0, len = hoverDivNames.length; i < len; i++) {
  3193. hoverDivs[hoverDivNames[i]] = $('<div class="mejs-fullscreen-hover" />').appendTo(t.container).mouseover(restoreControls).hide();
  3194. }
  3195. // on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
  3196. fullscreenBtn.on('mouseover',function() {
  3197. if (!t.isFullScreen) {
  3198. var buttonPos = fullscreenBtn.offset(),
  3199. containerPos = player.container.offset();
  3200. // move the button in Flash into place
  3201. media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);
  3202. // allows click through
  3203. fullscreenBtn.css('pointer-events', 'none');
  3204. t.controls.css('pointer-events', 'none');
  3205. // restore click-to-play
  3206. t.media.addEventListener('click', t.clickToPlayPauseCallback);
  3207. // show the divs that will restore things
  3208. for (i in hoverDivs) {
  3209. hoverDivs[i].show();
  3210. }
  3211. positionHoverDivs();
  3212. fullscreenIsDisabled = true;
  3213. }
  3214. });
  3215. // restore controls anytime the user enters or leaves fullscreen
  3216. media.addEventListener('fullscreenchange', function(e) {
  3217. t.isFullScreen = !t.isFullScreen;
  3218. // don't allow plugin click to pause video - messes with
  3219. // plugin's controls
  3220. if (t.isFullScreen) {
  3221. t.media.removeEventListener('click', t.clickToPlayPauseCallback);
  3222. } else {
  3223. t.media.addEventListener('click', t.clickToPlayPauseCallback);
  3224. }
  3225. restoreControls();
  3226. });
  3227. // the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
  3228. // so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
  3229. t.globalBind('mousemove', function(e) {
  3230. // if the mouse is anywhere but the fullsceen button, then restore it all
  3231. if (fullscreenIsDisabled) {
  3232. var fullscreenBtnPos = fullscreenBtn.offset();
  3233. if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
  3234. e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
  3235. ) {
  3236. fullscreenBtn.css('pointer-events', '');
  3237. t.controls.css('pointer-events', '');
  3238. fullscreenIsDisabled = false;
  3239. }
  3240. }
  3241. });
  3242. } else {
  3243. // the hover state will show the fullscreen button in Flash to hover up and click
  3244. fullscreenBtn
  3245. .on('mouseover', function() {
  3246. if (hideTimeout !== null) {
  3247. clearTimeout(hideTimeout);
  3248. delete hideTimeout;
  3249. }
  3250. var buttonPos = fullscreenBtn.offset(),
  3251. containerPos = player.container.offset();
  3252. media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);
  3253. })
  3254. .on('mouseout', function() {
  3255. if (hideTimeout !== null) {
  3256. clearTimeout(hideTimeout);
  3257. delete hideTimeout;
  3258. }
  3259. hideTimeout = setTimeout(function() {
  3260. media.hideFullscreenButton();
  3261. }, 1500);
  3262. });
  3263. }
  3264. }
  3265. player.fullscreenBtn = fullscreenBtn;
  3266. t.globalBind('keydown',function (e) {
  3267. if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
  3268. player.exitFullScreen();
  3269. }
  3270. });
  3271. },
  3272. cleanfullscreen: function(player) {
  3273. player.exitFullScreen();
  3274. },
  3275. containerSizeTimeout: null,
  3276. enterFullScreen: function() {
  3277. var t = this;
  3278. // firefox+flash can't adjust plugin sizes without resetting :(
  3279. if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
  3280. //t.media.setFullscreen(true);
  3281. //player.isFullScreen = true;
  3282. return;
  3283. }
  3284. // set it to not show scroll bars so 100% will work
  3285. $(document.documentElement).addClass('mejs-fullscreen');
  3286. // store sizing
  3287. normalHeight = t.container.height();
  3288. normalWidth = t.container.width();
  3289. // attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
  3290. if (t.media.pluginType === 'native') {
  3291. if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
  3292. mejs.MediaFeatures.requestFullScreen(t.container[0]);
  3293. //return;
  3294. if (t.isInIframe) {
  3295. // sometimes exiting from fullscreen doesn't work
  3296. // notably in Chrome <iframe>. Fixed in version 17
  3297. setTimeout(function checkFullscreen() {
  3298. if (t.isNativeFullScreen) {
  3299. // check if the video is suddenly not really fullscreen
  3300. if ($(window).width() !== screen.width) {
  3301. // manually exit
  3302. t.exitFullScreen();
  3303. } else {
  3304. // test again
  3305. setTimeout(checkFullscreen, 500);
  3306. }
  3307. }
  3308. }, 500);
  3309. }
  3310. } else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
  3311. t.media.webkitEnterFullscreen();
  3312. return;
  3313. }
  3314. }
  3315. // check for iframe launch
  3316. if (t.isInIframe) {
  3317. var url = t.options.newWindowCallback(this);
  3318. if (url !== '') {
  3319. // launch immediately
  3320. if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
  3321. t.pause();
  3322. window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
  3323. return;
  3324. } else {
  3325. setTimeout(function() {
  3326. if (!t.isNativeFullScreen) {
  3327. t.pause();
  3328. window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
  3329. }
  3330. }, 250);
  3331. }
  3332. }
  3333. }
  3334. // full window code
  3335. // make full size
  3336. t.container
  3337. .addClass('mejs-container-fullscreen')
  3338. .width('100%')
  3339. .height('100%');
  3340. //.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});
  3341. // Only needed for safari 5.1 native full screen, can cause display issues elsewhere
  3342. // Actually, it seems to be needed for IE8, too
  3343. //if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
  3344. t.containerSizeTimeout = setTimeout(function() {
  3345. t.container.css({width: '100%', height: '100%'});
  3346. t.setControlsSize();
  3347. }, 500);
  3348. //}
  3349. if (t.media.pluginType === 'native') {
  3350. t.$media
  3351. .width('100%')
  3352. .height('100%');
  3353. } else {
  3354. t.container.find('.mejs-shim')
  3355. .width('100%')
  3356. .height('100%');
  3357. //if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
  3358. t.media.setVideoSize($(window).width(),$(window).height());
  3359. //}
  3360. }
  3361. t.layers.children('div')
  3362. .width('100%')
  3363. .height('100%');
  3364. if (t.fullscreenBtn) {
  3365. t.fullscreenBtn
  3366. .removeClass('mejs-fullscreen')
  3367. .addClass('mejs-unfullscreen');
  3368. }
  3369. t.setControlsSize();
  3370. t.isFullScreen = true;
  3371. },
  3372. exitFullScreen: function() {
  3373. var t = this;
  3374. // Prevent container from attempting to stretch a second time
  3375. clearTimeout(t.containerSizeTimeout);
  3376. // firefox can't adjust plugins
  3377. if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
  3378. t.media.setFullscreen(false);
  3379. //player.isFullScreen = false;
  3380. return;
  3381. }
  3382. // come outo of native fullscreen
  3383. if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
  3384. mejs.MediaFeatures.cancelFullScreen();
  3385. }
  3386. // restore scroll bars to document
  3387. $(document.documentElement).removeClass('mejs-fullscreen');
  3388. t.container
  3389. .removeClass('mejs-container-fullscreen')
  3390. .width(normalWidth)
  3391. .height(normalHeight);
  3392. //.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});
  3393. if (t.media.pluginType === 'native') {
  3394. t.$media
  3395. .width(normalWidth)
  3396. .height(normalHeight);
  3397. } else {
  3398. t.container.find('.mejs-shim')
  3399. .width(normalWidth)
  3400. .height(normalHeight);
  3401. t.media.setVideoSize(normalWidth, normalHeight);
  3402. }
  3403. t.layers.children('div')
  3404. .width(normalWidth)
  3405. .height(normalHeight);
  3406. t.fullscreenBtn
  3407. .removeClass('mejs-unfullscreen')
  3408. .addClass('mejs-fullscreen');
  3409. t.setControlsSize();
  3410. t.isFullScreen = false;
  3411. }
  3412. });
  3413. })(mejs.$);
  3414. (function($) {
  3415. // add extra default options
  3416. $.extend(mejs.MepDefaults, {
  3417. // this will automatically turn on a <track>
  3418. startLanguage: '',
  3419. tracksText: mejs.i18n.t('Captions/Subtitles'),
  3420. // option to remove the [cc] button when no <track kind="subtitles"> are present
  3421. hideCaptionsButtonWhenEmpty: true,
  3422. // If true and we only have one track, change captions to popup
  3423. toggleCaptionsButtonWhenOnlyOne: false,
  3424. // #id or .class
  3425. slidesSelector: ''
  3426. });
  3427. $.extend(MediaElementPlayer.prototype, {
  3428. hasChapters: false,
  3429. buildtracks: function(player, controls, layers, media) {
  3430. if (player.tracks.length == 0)
  3431. return;
  3432. var t = this,
  3433. i,
  3434. options = '';
  3435. if (t.domNode.textTracks) { // if browser will do native captions, prefer mejs captions, loop through tracks and hide
  3436. for (var i = t.domNode.textTracks.length - 1; i >= 0; i--) {
  3437. t.domNode.textTracks[i].mode = "hidden";
  3438. }
  3439. }
  3440. player.chapters =
  3441. $('<div class="mejs-chapters mejs-layer"></div>')
  3442. .prependTo(layers).hide();
  3443. player.captions =
  3444. $('<div class="mejs-captions-layer mejs-layer"><div class="mejs-captions-position mejs-captions-position-hover"><span class="mejs-captions-text"></span></div></div>')
  3445. .prependTo(layers).hide();
  3446. player.captionsText = player.captions.find('.mejs-captions-text');
  3447. player.captionsButton =
  3448. $('<div class="mejs-button mejs-captions-button">'+
  3449. '<button type="button" aria-controls="' + t.id + '" title="' + t.options.tracksText + '" aria-label="' + t.options.tracksText + '"></button>'+
  3450. '<div class="mejs-captions-selector">'+
  3451. '<ul>'+
  3452. '<li>'+
  3453. '<input type="radio" name="' + player.id + '_captions" id="' + player.id + '_captions_none" value="none" checked="checked" />' +
  3454. '<label for="' + player.id + '_captions_none">' + mejs.i18n.t('None') +'</label>'+
  3455. '</li>' +
  3456. '</ul>'+
  3457. '</div>'+
  3458. '</div>')
  3459. .appendTo(controls);
  3460. var subtitleCount = 0;
  3461. for (i=0; i<player.tracks.length; i++) {
  3462. if (player.tracks[i].kind == 'subtitles') {
  3463. subtitleCount++;
  3464. }
  3465. }
  3466. // if only one language then just make the button a toggle
  3467. if (t.options.toggleCaptionsButtonWhenOnlyOne && subtitleCount == 1){
  3468. // click
  3469. player.captionsButton.on('click',function() {
  3470. if (player.selectedTrack == null) {
  3471. var lang = player.tracks[0].srclang;
  3472. } else {
  3473. var lang = 'none';
  3474. }
  3475. player.setTrack(lang);
  3476. });
  3477. } else {
  3478. // hover
  3479. player.captionsButton.hover(function() {
  3480. $(this).find('.mejs-captions-selector').css('visibility','visible');
  3481. }, function() {
  3482. $(this).find('.mejs-captions-selector').css('visibility','hidden');
  3483. })
  3484. // handle clicks to the language radio buttons
  3485. .on('click','input[type=radio]',function() {
  3486. lang = this.value;
  3487. player.setTrack(lang);
  3488. });
  3489. }
  3490. if (!player.options.alwaysShowControls) {
  3491. // move with controls
  3492. player.container
  3493. .bind('controlsshown', function () {
  3494. // push captions above controls
  3495. player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
  3496. })
  3497. .bind('controlshidden', function () {
  3498. if (!media.paused) {
  3499. // move back to normal place
  3500. player.container.find('.mejs-captions-position').removeClass('mejs-captions-position-hover');
  3501. }
  3502. });
  3503. } else {
  3504. player.container.find('.mejs-captions-position').addClass('mejs-captions-position-hover');
  3505. }
  3506. player.trackToLoad = -1;
  3507. player.selectedTrack = null;
  3508. player.isLoadingTrack = false;
  3509. // add to list
  3510. for (i=0; i<player.tracks.length; i++) {
  3511. if (player.tracks[i].kind == 'subtitles') {
  3512. player.addTrackButton(player.tracks[i].srclang, player.tracks[i].label);
  3513. }
  3514. }
  3515. // start loading tracks
  3516. player.loadNextTrack();
  3517. media.addEventListener('timeupdate',function(e) {
  3518. player.displayCaptions();
  3519. }, false);
  3520. if (player.options.slidesSelector != '') {
  3521. player.slidesContainer = $(player.options.slidesSelector);
  3522. media.addEventListener('timeupdate',function(e) {
  3523. player.displaySlides();
  3524. }, false);
  3525. }
  3526. media.addEventListener('loadedmetadata', function(e) {
  3527. player.displayChapters();
  3528. }, false);
  3529. player.container.hover(
  3530. function () {
  3531. // chapters
  3532. if (player.hasChapters) {
  3533. player.chapters.css('visibility','visible');
  3534. player.chapters.fadeIn(200).height(player.chapters.find('.mejs-chapter').outerHeight());
  3535. }
  3536. },
  3537. function () {
  3538. if (player.hasChapters && !media.paused) {
  3539. player.chapters.fadeOut(200, function() {
  3540. $(this).css('visibility','hidden');
  3541. $(this).css('display','block');
  3542. });
  3543. }
  3544. });
  3545. // check for autoplay
  3546. if (player.node.getAttribute('autoplay') !== null) {
  3547. player.chapters.css('visibility','hidden');
  3548. }
  3549. },
  3550. setTrack: function(lang){
  3551. var t = this,
  3552. i;
  3553. if (lang == 'none') {
  3554. t.selectedTrack = null;
  3555. t.captionsButton.removeClass('mejs-captions-enabled');
  3556. } else {
  3557. for (i=0; i<t.tracks.length; i++) {
  3558. if (t.tracks[i].srclang == lang) {
  3559. if (t.selectedTrack == null)
  3560. t.captionsButton.addClass('mejs-captions-enabled');
  3561. t.selectedTrack = t.tracks[i];
  3562. t.captions.attr('lang', t.selectedTrack.srclang);
  3563. t.displayCaptions();
  3564. break;
  3565. }
  3566. }
  3567. }
  3568. },
  3569. loadNextTrack: function() {
  3570. var t = this;
  3571. t.trackToLoad++;
  3572. if (t.trackToLoad < t.tracks.length) {
  3573. t.isLoadingTrack = true;
  3574. t.loadTrack(t.trackToLoad);
  3575. } else {
  3576. // add done?
  3577. t.isLoadingTrack = false;
  3578. t.checkForTracks();
  3579. }
  3580. },
  3581. loadTrack: function(index){
  3582. var
  3583. t = this,
  3584. track = t.tracks[index],
  3585. after = function() {
  3586. track.isLoaded = true;
  3587. // create button
  3588. //t.addTrackButton(track.srclang);
  3589. t.enableTrackButton(track.srclang, track.label);
  3590. t.loadNextTrack();
  3591. };
  3592. $.ajax({
  3593. url: track.src,
  3594. dataType: "text",
  3595. success: function(d) {
  3596. // parse the loaded file
  3597. if (typeof d == "string" && (/<tt\s+xml/ig).exec(d)) {
  3598. track.entries = mejs.TrackFormatParser.dfxp.parse(d);
  3599. } else {
  3600. track.entries = mejs.TrackFormatParser.webvvt.parse(d);
  3601. }
  3602. after();
  3603. if (track.kind == 'chapters') {
  3604. t.media.addEventListener('play', function(e) {
  3605. if (t.media.duration > 0) {
  3606. t.displayChapters(track);
  3607. }
  3608. }, false);
  3609. }
  3610. if (track.kind == 'slides') {
  3611. t.setupSlides(track);
  3612. }
  3613. },
  3614. error: function() {
  3615. t.loadNextTrack();
  3616. }
  3617. });
  3618. },
  3619. enableTrackButton: function(lang, label) {
  3620. var t = this;
  3621. if (label === '') {
  3622. label = mejs.language.codes[lang] || lang;
  3623. }
  3624. t.captionsButton
  3625. .find('input[value=' + lang + ']')
  3626. .prop('disabled',false)
  3627. .siblings('label')
  3628. .html( label );
  3629. // auto select
  3630. if (t.options.startLanguage == lang) {
  3631. $('#' + t.id + '_captions_' + lang).click();
  3632. }
  3633. t.adjustLanguageBox();
  3634. },
  3635. addTrackButton: function(lang, label) {
  3636. var t = this;
  3637. if (label === '') {
  3638. label = mejs.language.codes[lang] || lang;
  3639. }
  3640. t.captionsButton.find('ul').append(
  3641. $('<li>'+
  3642. '<input type="radio" name="' + t.id + '_captions" id="' + t.id + '_captions_' + lang + '" value="' + lang + '" disabled="disabled" />' +
  3643. '<label for="' + t.id + '_captions_' + lang + '">' + label + ' (loading)' + '</label>'+
  3644. '</li>')
  3645. );
  3646. t.adjustLanguageBox();
  3647. // remove this from the dropdownlist (if it exists)
  3648. t.container.find('.mejs-captions-translations option[value=' + lang + ']').remove();
  3649. },
  3650. adjustLanguageBox:function() {
  3651. var t = this;
  3652. // adjust the size of the outer box
  3653. t.captionsButton.find('.mejs-captions-selector').height(
  3654. t.captionsButton.find('.mejs-captions-selector ul').outerHeight(true) +
  3655. t.captionsButton.find('.mejs-captions-translations').outerHeight(true)
  3656. );
  3657. },
  3658. checkForTracks: function() {
  3659. var
  3660. t = this,
  3661. hasSubtitles = false;
  3662. // check if any subtitles
  3663. if (t.options.hideCaptionsButtonWhenEmpty) {
  3664. for (i=0; i<t.tracks.length; i++) {
  3665. if (t.tracks[i].kind == 'subtitles') {
  3666. hasSubtitles = true;
  3667. break;
  3668. }
  3669. }
  3670. if (!hasSubtitles) {
  3671. t.captionsButton.hide();
  3672. t.setControlsSize();
  3673. }
  3674. }
  3675. },
  3676. displayCaptions: function() {
  3677. if (typeof this.tracks == 'undefined')
  3678. return;
  3679. var
  3680. t = this,
  3681. i,
  3682. track = t.selectedTrack;
  3683. if (track != null && track.isLoaded) {
  3684. for (i=0; i<track.entries.times.length; i++) {
  3685. if (t.media.currentTime >= track.entries.times[i].start && t.media.currentTime <= track.entries.times[i].stop){
  3686. t.captionsText.html(track.entries.text[i]);
  3687. t.captions.show().height(0);
  3688. return; // exit out if one is visible;
  3689. }
  3690. }
  3691. t.captions.hide();
  3692. } else {
  3693. t.captions.hide();
  3694. }
  3695. },
  3696. setupSlides: function(track) {
  3697. var t = this;
  3698. t.slides = track;
  3699. t.slides.entries.imgs = [t.slides.entries.text.length];
  3700. t.showSlide(0);
  3701. },
  3702. showSlide: function(index) {
  3703. if (typeof this.tracks == 'undefined' || typeof this.slidesContainer == 'undefined') {
  3704. return;
  3705. }
  3706. var t = this,
  3707. url = t.slides.entries.text[index],
  3708. img = t.slides.entries.imgs[index];
  3709. if (typeof img == 'undefined' || typeof img.fadeIn == 'undefined') {
  3710. t.slides.entries.imgs[index] = img = $('<img src="' + url + '">')
  3711. .on('load', function() {
  3712. img.appendTo(t.slidesContainer)
  3713. .hide()
  3714. .fadeIn()
  3715. .siblings(':visible')
  3716. .fadeOut();
  3717. });
  3718. } else {
  3719. if (!img.is(':visible') && !img.is(':animated')) {
  3720. //
  3721. img.fadeIn()
  3722. .siblings(':visible')
  3723. .fadeOut();
  3724. }
  3725. }
  3726. },
  3727. displaySlides: function() {
  3728. if (typeof this.slides == 'undefined')
  3729. return;
  3730. var
  3731. t = this,
  3732. slides = t.slides,
  3733. i;
  3734. for (i=0; i<slides.entries.times.length; i++) {
  3735. if (t.media.currentTime >= slides.entries.times[i].start && t.media.currentTime <= slides.entries.times[i].stop){
  3736. t.showSlide(i);
  3737. return; // exit out if one is visible;
  3738. }
  3739. }
  3740. },
  3741. displayChapters: function() {
  3742. var
  3743. t = this,
  3744. i;
  3745. for (i=0; i<t.tracks.length; i++) {
  3746. if (t.tracks[i].kind == 'chapters' && t.tracks[i].isLoaded) {
  3747. t.drawChapters(t.tracks[i]);
  3748. t.hasChapters = true;
  3749. break;
  3750. }
  3751. }
  3752. },
  3753. drawChapters: function(chapters) {
  3754. var
  3755. t = this,
  3756. i,
  3757. dur,
  3758. //width,
  3759. //left,
  3760. percent = 0,
  3761. usedPercent = 0;
  3762. t.chapters.empty();
  3763. for (i=0; i<chapters.entries.times.length; i++) {
  3764. dur = chapters.entries.times[i].stop - chapters.entries.times[i].start;
  3765. percent = Math.floor(dur / t.media.duration * 100);
  3766. if (percent + usedPercent > 100 || // too large
  3767. i == chapters.entries.times.length-1 && percent + usedPercent < 100) // not going to fill it in
  3768. {
  3769. percent = 100 - usedPercent;
  3770. }
  3771. //width = Math.floor(t.width * dur / t.media.duration);
  3772. //left = Math.floor(t.width * chapters.entries.times[i].start / t.media.duration);
  3773. //if (left + width > t.width) {
  3774. // width = t.width - left;
  3775. //}
  3776. t.chapters.append( $(
  3777. '<div class="mejs-chapter" rel="' + chapters.entries.times[i].start + '" style="left: ' + usedPercent.toString() + '%;width: ' + percent.toString() + '%;">' +
  3778. '<div class="mejs-chapter-block' + ((i==chapters.entries.times.length-1) ? ' mejs-chapter-block-last' : '') + '">' +
  3779. '<span class="ch-title">' + chapters.entries.text[i] + '</span>' +
  3780. '<span class="ch-time">' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].start) + '&ndash;' + mejs.Utility.secondsToTimeCode(chapters.entries.times[i].stop) + '</span>' +
  3781. '</div>' +
  3782. '</div>'));
  3783. usedPercent += percent;
  3784. }
  3785. t.chapters.find('div.mejs-chapter').click(function() {
  3786. t.media.setCurrentTime( parseFloat( $(this).attr('rel') ) );
  3787. if (t.media.paused) {
  3788. t.media.play();
  3789. }
  3790. });
  3791. t.chapters.show();
  3792. }
  3793. });
  3794. mejs.language = {
  3795. codes: {
  3796. af:'Afrikaans',
  3797. sq:'Albanian',
  3798. ar:'Arabic',
  3799. be:'Belarusian',
  3800. bg:'Bulgarian',
  3801. ca:'Catalan',
  3802. zh:'Chinese',
  3803. 'zh-cn':'Chinese Simplified',
  3804. 'zh-tw':'Chinese Traditional',
  3805. hr:'Croatian',
  3806. cs:'Czech',
  3807. da:'Danish',
  3808. nl:'Dutch',
  3809. en:'English',
  3810. et:'Estonian',
  3811. tl:'Filipino',
  3812. fi:'Finnish',
  3813. fr:'French',
  3814. gl:'Galician',
  3815. de:'German',
  3816. el:'Greek',
  3817. ht:'Haitian Creole',
  3818. iw:'Hebrew',
  3819. hi:'Hindi',
  3820. hu:'Hungarian',
  3821. is:'Icelandic',
  3822. id:'Indonesian',
  3823. ga:'Irish',
  3824. it:'Italian',
  3825. ja:'Japanese',
  3826. ko:'Korean',
  3827. lv:'Latvian',
  3828. lt:'Lithuanian',
  3829. mk:'Macedonian',
  3830. ms:'Malay',
  3831. mt:'Maltese',
  3832. no:'Norwegian',
  3833. fa:'Persian',
  3834. pl:'Polish',
  3835. pt:'Portuguese',
  3836. //'pt-pt':'Portuguese (Portugal)',
  3837. ro:'Romanian',
  3838. ru:'Russian',
  3839. sr:'Serbian',
  3840. sk:'Slovak',
  3841. sl:'Slovenian',
  3842. es:'Spanish',
  3843. sw:'Swahili',
  3844. sv:'Swedish',
  3845. tl:'Tagalog',
  3846. th:'Thai',
  3847. tr:'Turkish',
  3848. uk:'Ukrainian',
  3849. vi:'Vietnamese',
  3850. cy:'Welsh',
  3851. yi:'Yiddish'
  3852. }
  3853. };
  3854. /*
  3855. Parses WebVVT format which should be formatted as
  3856. ================================
  3857. WEBVTT
  3858. 1
  3859. 00:00:01,1 --> 00:00:05,000
  3860. A line of text
  3861. 2
  3862. 00:01:15,1 --> 00:02:05,000
  3863. A second line of text
  3864. ===============================
  3865. Adapted from: http://www.delphiki.com/html5/playr
  3866. */
  3867. mejs.TrackFormatParser = {
  3868. webvvt: {
  3869. // match start "chapter-" (or anythingelse)
  3870. pattern_identifier: /^([a-zA-z]+-)?[0-9]+$/,
  3871. pattern_timecode: /^([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ([0-9]{2}:[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,
  3872. parse: function(trackText) {
  3873. var
  3874. i = 0,
  3875. lines = mejs.TrackFormatParser.split2(trackText, /\r?\n/),
  3876. entries = {text:[], times:[]},
  3877. timecode,
  3878. text;
  3879. for(; i<lines.length; i++) {
  3880. // check for the line number
  3881. if (this.pattern_identifier.exec(lines[i])){
  3882. // skip to the next line where the start --> end time code should be
  3883. i++;
  3884. timecode = this.pattern_timecode.exec(lines[i]);
  3885. if (timecode && i<lines.length){
  3886. i++;
  3887. // grab all the (possibly multi-line) text that follows
  3888. text = lines[i];
  3889. i++;
  3890. while(lines[i] !== '' && i<lines.length){
  3891. text = text + '\n' + lines[i];
  3892. i++;
  3893. }
  3894. text = $.trim(text).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
  3895. // Text is in a different array so I can use .join
  3896. entries.text.push(text);
  3897. entries.times.push(
  3898. {
  3899. start: (mejs.Utility.convertSMPTEtoSeconds(timecode[1]) == 0) ? 0.200 : mejs.Utility.convertSMPTEtoSeconds(timecode[1]),
  3900. stop: mejs.Utility.convertSMPTEtoSeconds(timecode[3]),
  3901. settings: timecode[5]
  3902. });
  3903. }
  3904. }
  3905. }
  3906. return entries;
  3907. }
  3908. },
  3909. // Thanks to Justin Capella: https://github.com/johndyer/mediaelement/pull/420
  3910. dfxp: {
  3911. parse: function(trackText) {
  3912. trackText = $(trackText).filter("tt");
  3913. var
  3914. i = 0,
  3915. container = trackText.children("div").eq(0),
  3916. lines = container.find("p"),
  3917. styleNode = trackText.find("#" + container.attr("style")),
  3918. styles,
  3919. begin,
  3920. end,
  3921. text,
  3922. entries = {text:[], times:[]};
  3923. if (styleNode.length) {
  3924. var attributes = styleNode.removeAttr("id").get(0).attributes;
  3925. if (attributes.length) {
  3926. styles = {};
  3927. for (i = 0; i < attributes.length; i++) {
  3928. styles[attributes[i].name.split(":")[1]] = attributes[i].value;
  3929. }
  3930. }
  3931. }
  3932. for(i = 0; i<lines.length; i++) {
  3933. var style;
  3934. var _temp_times = {
  3935. start: null,
  3936. stop: null,
  3937. style: null
  3938. };
  3939. if (lines.eq(i).attr("begin")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("begin"));
  3940. if (!_temp_times.start && lines.eq(i-1).attr("end")) _temp_times.start = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i-1).attr("end"));
  3941. if (lines.eq(i).attr("end")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i).attr("end"));
  3942. if (!_temp_times.stop && lines.eq(i+1).attr("begin")) _temp_times.stop = mejs.Utility.convertSMPTEtoSeconds(lines.eq(i+1).attr("begin"));
  3943. if (styles) {
  3944. style = "";
  3945. for (var _style in styles) {
  3946. style += _style + ":" + styles[_style] + ";";
  3947. }
  3948. }
  3949. if (style) _temp_times.style = style;
  3950. if (_temp_times.start == 0) _temp_times.start = 0.200;
  3951. entries.times.push(_temp_times);
  3952. text = $.trim(lines.eq(i).html()).replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, "<a href='$1' target='_blank'>$1</a>");
  3953. entries.text.push(text);
  3954. if (entries.times.start == 0) entries.times.start = 2;
  3955. }
  3956. return entries;
  3957. }
  3958. },
  3959. split2: function (text, regex) {
  3960. // normal version for compliant browsers
  3961. // see below for IE fix
  3962. return text.split(regex);
  3963. }
  3964. };
  3965. // test for browsers with bad String.split method.
  3966. if ('x\n\ny'.split(/\n/gi).length != 3) {
  3967. // add super slow IE8 and below version
  3968. mejs.TrackFormatParser.split2 = function(text, regex) {
  3969. var
  3970. parts = [],
  3971. chunk = '',
  3972. i;
  3973. for (i=0; i<text.length; i++) {
  3974. chunk += text.substring(i,i+1);
  3975. if (regex.test(chunk)) {
  3976. parts.push(chunk.replace(regex, ''));
  3977. chunk = '';
  3978. }
  3979. }
  3980. parts.push(chunk);
  3981. return parts;
  3982. }
  3983. }
  3984. })(mejs.$);
  3985. /*
  3986. * ContextMenu Plugin
  3987. *
  3988. *
  3989. */
  3990. (function($) {
  3991. $.extend(mejs.MepDefaults,
  3992. { 'contextMenuItems': [
  3993. // demo of a fullscreen option
  3994. {
  3995. render: function(player) {
  3996. // check for fullscreen plugin
  3997. if (typeof player.enterFullScreen == 'undefined')
  3998. return null;
  3999. if (player.isFullScreen) {
  4000. return mejs.i18n.t('Turn off Fullscreen');
  4001. } else {
  4002. return mejs.i18n.t('Go Fullscreen');
  4003. }
  4004. },
  4005. click: function(player) {
  4006. if (player.isFullScreen) {
  4007. player.exitFullScreen();
  4008. } else {
  4009. player.enterFullScreen();
  4010. }
  4011. }
  4012. }
  4013. ,
  4014. // demo of a mute/unmute button
  4015. {
  4016. render: function(player) {
  4017. if (player.media.muted) {
  4018. return mejs.i18n.t('Unmute');
  4019. } else {
  4020. return mejs.i18n.t('Mute');
  4021. }
  4022. },
  4023. click: function(player) {
  4024. if (player.media.muted) {
  4025. player.setMuted(false);
  4026. } else {
  4027. player.setMuted(true);
  4028. }
  4029. }
  4030. },
  4031. // separator
  4032. {
  4033. isSeparator: true
  4034. }
  4035. ,
  4036. // demo of simple download video
  4037. {
  4038. render: function(player) {
  4039. return mejs.i18n.t('Download Video');
  4040. },
  4041. click: function(player) {
  4042. window.location.href = player.media.currentSrc;
  4043. }
  4044. }
  4045. ]}
  4046. );
  4047. $.extend(MediaElementPlayer.prototype, {
  4048. buildcontextmenu: function(player, controls, layers, media) {
  4049. // create context menu
  4050. player.contextMenu = $('<div class="mejs-contextmenu"></div>')
  4051. .appendTo($('body'))
  4052. .hide();
  4053. // create events for showing context menu
  4054. player.container.bind('contextmenu', function(e) {
  4055. if (player.isContextMenuEnabled) {
  4056. e.preventDefault();
  4057. player.renderContextMenu(e.clientX-1, e.clientY-1);
  4058. return false;
  4059. }
  4060. });
  4061. player.container.bind('click', function() {
  4062. player.contextMenu.hide();
  4063. });
  4064. player.contextMenu.bind('mouseleave', function() {
  4065. //
  4066. player.startContextMenuTimer();
  4067. });
  4068. },
  4069. cleancontextmenu: function(player) {
  4070. player.contextMenu.remove();
  4071. },
  4072. isContextMenuEnabled: true,
  4073. enableContextMenu: function() {
  4074. this.isContextMenuEnabled = true;
  4075. },
  4076. disableContextMenu: function() {
  4077. this.isContextMenuEnabled = false;
  4078. },
  4079. contextMenuTimeout: null,
  4080. startContextMenuTimer: function() {
  4081. //
  4082. var t = this;
  4083. t.killContextMenuTimer();
  4084. t.contextMenuTimer = setTimeout(function() {
  4085. t.hideContextMenu();
  4086. t.killContextMenuTimer();
  4087. }, 750);
  4088. },
  4089. killContextMenuTimer: function() {
  4090. var timer = this.contextMenuTimer;
  4091. //
  4092. if (timer != null) {
  4093. clearTimeout(timer);
  4094. delete timer;
  4095. timer = null;
  4096. }
  4097. },
  4098. hideContextMenu: function() {
  4099. this.contextMenu.hide();
  4100. },
  4101. renderContextMenu: function(x,y) {
  4102. // alway re-render the items so that things like "turn fullscreen on" and "turn fullscreen off" are always written correctly
  4103. var t = this,
  4104. html = '',
  4105. items = t.options.contextMenuItems;
  4106. for (var i=0, il=items.length; i<il; i++) {
  4107. if (items[i].isSeparator) {
  4108. html += '<div class="mejs-contextmenu-separator"></div>';
  4109. } else {
  4110. var rendered = items[i].render(t);
  4111. // render can return null if the item doesn't need to be used at the moment
  4112. if (rendered != null) {
  4113. html += '<div class="mejs-contextmenu-item" data-itemindex="' + i + '" id="element-' + (Math.random()*1000000) + '">' + rendered + '</div>';
  4114. }
  4115. }
  4116. }
  4117. // position and show the context menu
  4118. t.contextMenu
  4119. .empty()
  4120. .append($(html))
  4121. .css({top:y, left:x})
  4122. .show();
  4123. // bind events
  4124. t.contextMenu.find('.mejs-contextmenu-item').each(function() {
  4125. // which one is this?
  4126. var $dom = $(this),
  4127. itemIndex = parseInt( $dom.data('itemindex'), 10 ),
  4128. item = t.options.contextMenuItems[itemIndex];
  4129. // bind extra functionality?
  4130. if (typeof item.show != 'undefined')
  4131. item.show( $dom , t);
  4132. // bind click action
  4133. $dom.click(function() {
  4134. // perform click action
  4135. if (typeof item.click != 'undefined')
  4136. item.click(t);
  4137. // close
  4138. t.contextMenu.hide();
  4139. });
  4140. });
  4141. // stop the controls from hiding
  4142. setTimeout(function() {
  4143. t.killControlsTimer('rev3');
  4144. }, 100);
  4145. }
  4146. });
  4147. })(mejs.$);
  4148. /**
  4149. * Postroll plugin
  4150. */
  4151. (function($) {
  4152. $.extend(mejs.MepDefaults, {
  4153. postrollCloseText: mejs.i18n.t('Close')
  4154. });
  4155. // Postroll
  4156. $.extend(MediaElementPlayer.prototype, {
  4157. buildpostroll: function(player, controls, layers, media) {
  4158. var
  4159. t = this,
  4160. postrollLink = t.container.find('link[rel="postroll"]').attr('href');
  4161. if (typeof postrollLink !== 'undefined') {
  4162. player.postroll =
  4163. $('<div class="mejs-postroll-layer mejs-layer"><a class="mejs-postroll-close" onclick="$(this).parent().hide();return false;">' + t.options.postrollCloseText + '</a><div class="mejs-postroll-layer-content"></div></div>').prependTo(layers).hide();
  4164. t.media.addEventListener('ended', function (e) {
  4165. $.ajax({
  4166. dataType: 'html',
  4167. url: postrollLink,
  4168. success: function (data, textStatus) {
  4169. layers.find('.mejs-postroll-layer-content').html(data);
  4170. }
  4171. });
  4172. player.postroll.show();
  4173. }, false);
  4174. }
  4175. }
  4176. });
  4177. })(mejs.$);