mediaelement-and-player.js 162 KB

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