mediaelement-and-player.js 185 KB

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