mediaelement-and-player.js 163 KB

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