mediaelement-and-player.js 160 KB

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