browserify-test.js 1.1 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174
  1. (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. /**
  3. * @file add-text-track-data.js
  4. */
  5. 'use strict';
  6. Object.defineProperty(exports, '__esModule', {
  7. value: true
  8. });
  9. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10. var _globalWindow = require('global/window');
  11. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  12. var _videoJs = require('video.js');
  13. var _videoJs2 = _interopRequireDefault(_videoJs);
  14. /**
  15. * Define properties on a cue for backwards compatability,
  16. * but warn the user that the way that they are using it
  17. * is depricated and will be removed at a later date.
  18. *
  19. * @param {Cue} cue the cue to add the properties on
  20. * @private
  21. */
  22. var deprecateOldCue = function deprecateOldCue(cue) {
  23. Object.defineProperties(cue.frame, {
  24. id: {
  25. get: function get() {
  26. _videoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
  27. return cue.value.key;
  28. }
  29. },
  30. value: {
  31. get: function get() {
  32. _videoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
  33. return cue.value.data;
  34. }
  35. },
  36. privateData: {
  37. get: function get() {
  38. _videoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
  39. return cue.value.data;
  40. }
  41. }
  42. });
  43. };
  44. var durationOfVideo = function durationOfVideo(duration) {
  45. var dur = undefined;
  46. if (isNaN(duration) || Math.abs(duration) === Infinity) {
  47. dur = Number.MAX_VALUE;
  48. } else {
  49. dur = duration;
  50. }
  51. return dur;
  52. };
  53. /**
  54. * Add text track data to a source handler given the captions and
  55. * metadata from the buffer.
  56. *
  57. * @param {Object} sourceHandler the flash or virtual source buffer
  58. * @param {Array} captionArray an array of caption data
  59. * @param {Array} metadataArray an array of meta data
  60. * @private
  61. */
  62. var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
  63. var Cue = _globalWindow2['default'].WebKitDataCue || _globalWindow2['default'].VTTCue;
  64. if (captionArray) {
  65. captionArray.forEach(function (caption) {
  66. var track = caption.stream;
  67. this.inbandTextTracks_[track].addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
  68. }, sourceHandler);
  69. }
  70. if (metadataArray) {
  71. (function () {
  72. var videoDuration = durationOfVideo(sourceHandler.mediaSource_.duration);
  73. metadataArray.forEach(function (metadata) {
  74. var time = metadata.cueTime + this.timestampOffset;
  75. metadata.frames.forEach(function (frame) {
  76. var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');
  77. cue.frame = frame;
  78. cue.value = frame;
  79. deprecateOldCue(cue);
  80. this.metadataTrack_.addCue(cue);
  81. }, this);
  82. }, sourceHandler);
  83. // Updating the metadeta cues so that
  84. // the endTime of each cue is the startTime of the next cue
  85. // the endTime of last cue is the duration of the video
  86. if (sourceHandler.metadataTrack_ && sourceHandler.metadataTrack_.cues && sourceHandler.metadataTrack_.cues.length) {
  87. (function () {
  88. var cues = sourceHandler.metadataTrack_.cues;
  89. var cuesArray = [];
  90. // Create a copy of the TextTrackCueList...
  91. // ...disregarding cues with a falsey value
  92. for (var i = 0; i < cues.length; i++) {
  93. if (cues[i]) {
  94. cuesArray.push(cues[i]);
  95. }
  96. }
  97. // Group cues by their startTime value
  98. var cuesGroupedByStartTime = cuesArray.reduce(function (obj, cue) {
  99. var timeSlot = obj[cue.startTime] || [];
  100. timeSlot.push(cue);
  101. obj[cue.startTime] = timeSlot;
  102. return obj;
  103. }, {});
  104. // Sort startTimes by ascending order
  105. var sortedStartTimes = Object.keys(cuesGroupedByStartTime).sort(function (a, b) {
  106. return Number(a) - Number(b);
  107. });
  108. // Map each cue group's endTime to the next group's startTime
  109. sortedStartTimes.forEach(function (startTime, idx) {
  110. var cueGroup = cuesGroupedByStartTime[startTime];
  111. var nextTime = Number(sortedStartTimes[idx + 1]) || videoDuration;
  112. // Map each cue's endTime the next group's startTime
  113. cueGroup.forEach(function (cue) {
  114. cue.endTime = nextTime;
  115. });
  116. });
  117. })();
  118. }
  119. })();
  120. }
  121. };
  122. exports['default'] = {
  123. addTextTrackData: addTextTrackData,
  124. durationOfVideo: durationOfVideo
  125. };
  126. module.exports = exports['default'];
  127. },{"global/window":16,"video.js":135}],2:[function(require,module,exports){
  128. /**
  129. * @file codec-utils.js
  130. */
  131. /**
  132. * Check if a codec string refers to an audio codec.
  133. *
  134. * @param {String} codec codec string to check
  135. * @return {Boolean} if this is an audio codec
  136. * @private
  137. */
  138. 'use strict';
  139. Object.defineProperty(exports, '__esModule', {
  140. value: true
  141. });
  142. var isAudioCodec = function isAudioCodec(codec) {
  143. return (/mp4a\.\d+.\d+/i.test(codec)
  144. );
  145. };
  146. /**
  147. * Check if a codec string refers to a video codec.
  148. *
  149. * @param {String} codec codec string to check
  150. * @return {Boolean} if this is a video codec
  151. * @private
  152. */
  153. var isVideoCodec = function isVideoCodec(codec) {
  154. return (/avc1\.[\da-f]+/i.test(codec)
  155. );
  156. };
  157. /**
  158. * Parse a content type header into a type and parameters
  159. * object
  160. *
  161. * @param {String} type the content type header
  162. * @return {Object} the parsed content-type
  163. * @private
  164. */
  165. var parseContentType = function parseContentType(type) {
  166. var object = { type: '', parameters: {} };
  167. var parameters = type.trim().split(';');
  168. // first parameter should always be content-type
  169. object.type = parameters.shift().trim();
  170. parameters.forEach(function (parameter) {
  171. var pair = parameter.trim().split('=');
  172. if (pair.length > 1) {
  173. var _name = pair[0].replace(/"/g, '').trim();
  174. var value = pair[1].replace(/"/g, '').trim();
  175. object.parameters[_name] = value;
  176. }
  177. });
  178. return object;
  179. };
  180. /**
  181. * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
  182. * `avc1.<hhhhhh>`
  183. *
  184. * @param {Array} codecs an array of codec strings to fix
  185. * @return {Array} the translated codec array
  186. * @private
  187. */
  188. var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
  189. return codecs.map(function (codec) {
  190. return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
  191. var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
  192. var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);
  193. return 'avc1.' + profileHex + '00' + avcLevelHex;
  194. });
  195. });
  196. };
  197. exports['default'] = {
  198. isAudioCodec: isAudioCodec,
  199. parseContentType: parseContentType,
  200. isVideoCodec: isVideoCodec,
  201. translateLegacyCodecs: translateLegacyCodecs
  202. };
  203. module.exports = exports['default'];
  204. },{}],3:[function(require,module,exports){
  205. /**
  206. * @file create-text-tracks-if-necessary.js
  207. */
  208. /**
  209. * Create text tracks on video.js if they exist on a segment.
  210. *
  211. * @param {Object} sourceBuffer the VSB or FSB
  212. * @param {Object} mediaSource the HTML or Flash media source
  213. * @param {Object} segment the segment that may contain the text track
  214. * @private
  215. */
  216. 'use strict';
  217. Object.defineProperty(exports, '__esModule', {
  218. value: true
  219. });
  220. var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
  221. var player = mediaSource.player_;
  222. // create an in-band caption track if one is present in the segment
  223. if (segment.captions && segment.captions.length) {
  224. if (!sourceBuffer.inbandTextTracks_) {
  225. sourceBuffer.inbandTextTracks_ = {};
  226. }
  227. for (var trackId in segment.captionStreams) {
  228. if (!sourceBuffer.inbandTextTracks_[trackId]) {
  229. player.tech_.trigger({ type: 'usage', name: 'hls-608' });
  230. var track = player.textTracks().getTrackById(trackId);
  231. if (track) {
  232. // Resuse an existing track with a CC# id because this was
  233. // very likely created by videojs-contrib-hls from information
  234. // in the m3u8 for us to use
  235. sourceBuffer.inbandTextTracks_[trackId] = track;
  236. } else {
  237. // Otherwise, create a track with the default `CC#` label and
  238. // without a language
  239. sourceBuffer.inbandTextTracks_[trackId] = player.addRemoteTextTrack({
  240. kind: 'captions',
  241. id: trackId,
  242. label: trackId
  243. }, false).track;
  244. }
  245. }
  246. }
  247. }
  248. if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
  249. sourceBuffer.metadataTrack_ = player.addRemoteTextTrack({
  250. kind: 'metadata',
  251. label: 'Timed Metadata'
  252. }, false).track;
  253. sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
  254. }
  255. };
  256. exports['default'] = createTextTracksIfNecessary;
  257. module.exports = exports['default'];
  258. },{}],4:[function(require,module,exports){
  259. /**
  260. * @file flash-constants.js
  261. */
  262. /**
  263. * The maximum size in bytes for append operations to the video.js
  264. * SWF. Calling through to Flash blocks and can be expensive so
  265. * we chunk data and pass through 4KB at a time, yielding to the
  266. * browser between chunks. This gives a theoretical maximum rate of
  267. * 1MB/s into Flash. Any higher and we begin to drop frames and UI
  268. * responsiveness suffers.
  269. *
  270. * @private
  271. */
  272. "use strict";
  273. Object.defineProperty(exports, "__esModule", {
  274. value: true
  275. });
  276. var flashConstants = {
  277. // times in milliseconds
  278. TIME_BETWEEN_CHUNKS: 1,
  279. BYTES_PER_CHUNK: 1024 * 32
  280. };
  281. exports["default"] = flashConstants;
  282. module.exports = exports["default"];
  283. },{}],5:[function(require,module,exports){
  284. /**
  285. * @file flash-media-source.js
  286. */
  287. 'use strict';
  288. Object.defineProperty(exports, '__esModule', {
  289. value: true
  290. });
  291. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  292. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  293. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  294. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  295. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  296. var _globalDocument = require('global/document');
  297. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  298. var _videoJs = require('video.js');
  299. var _videoJs2 = _interopRequireDefault(_videoJs);
  300. var _flashSourceBuffer = require('./flash-source-buffer');
  301. var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);
  302. var _flashConstants = require('./flash-constants');
  303. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  304. var _codecUtils = require('./codec-utils');
  305. /**
  306. * A flash implmentation of HTML MediaSources and a polyfill
  307. * for browsers that don't support native or HTML MediaSources..
  308. *
  309. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  310. * @class FlashMediaSource
  311. * @extends videojs.EventTarget
  312. */
  313. var FlashMediaSource = (function (_videojs$EventTarget) {
  314. _inherits(FlashMediaSource, _videojs$EventTarget);
  315. function FlashMediaSource() {
  316. var _this = this;
  317. _classCallCheck(this, FlashMediaSource);
  318. _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
  319. this.sourceBuffers = [];
  320. this.readyState = 'closed';
  321. this.on(['sourceopen', 'webkitsourceopen'], function (event) {
  322. // find the swf where we will push media data
  323. _this.swfObj = _globalDocument2['default'].getElementById(event.swfId);
  324. _this.player_ = (0, _videoJs2['default'])(_this.swfObj.parentNode);
  325. _this.tech_ = _this.swfObj.tech;
  326. _this.readyState = 'open';
  327. _this.tech_.on('seeking', function () {
  328. var i = _this.sourceBuffers.length;
  329. while (i--) {
  330. _this.sourceBuffers[i].abort();
  331. }
  332. });
  333. // trigger load events
  334. if (_this.swfObj) {
  335. _this.swfObj.vjs_load();
  336. }
  337. });
  338. }
  339. /**
  340. * Set or return the presentation duration.
  341. *
  342. * @param {Double} value the duration of the media in seconds
  343. * @param {Double} the current presentation duration
  344. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  345. */
  346. /**
  347. * We have this function so that the html and flash interfaces
  348. * are the same.
  349. *
  350. * @private
  351. */
  352. _createClass(FlashMediaSource, [{
  353. key: 'addSeekableRange_',
  354. value: function addSeekableRange_() {}
  355. // intentional no-op
  356. /**
  357. * Create a new flash source buffer and add it to our flash media source.
  358. *
  359. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  360. * @param {String} type the content-type of the source
  361. * @return {Object} the flash source buffer
  362. */
  363. }, {
  364. key: 'addSourceBuffer',
  365. value: function addSourceBuffer(type) {
  366. var parsedType = (0, _codecUtils.parseContentType)(type);
  367. var sourceBuffer = undefined;
  368. // if this is an FLV type, we'll push data to flash
  369. if (parsedType.type === 'video/mp2t' || parsedType.type === 'audio/mp2t') {
  370. // Flash source buffers
  371. sourceBuffer = new _flashSourceBuffer2['default'](this);
  372. } else {
  373. throw new Error('NotSupportedError (Video.js)');
  374. }
  375. this.sourceBuffers.push(sourceBuffer);
  376. return sourceBuffer;
  377. }
  378. /**
  379. * Signals the end of the stream.
  380. *
  381. * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
  382. * @param {String=} error Signals that a playback error
  383. * has occurred. If specified, it must be either "network" or
  384. * "decode".
  385. */
  386. }, {
  387. key: 'endOfStream',
  388. value: function endOfStream(error) {
  389. if (error === 'network') {
  390. // MEDIA_ERR_NETWORK
  391. this.tech_.error(2);
  392. } else if (error === 'decode') {
  393. // MEDIA_ERR_DECODE
  394. this.tech_.error(3);
  395. }
  396. if (this.readyState !== 'ended') {
  397. this.readyState = 'ended';
  398. this.swfObj.vjs_endOfStream();
  399. }
  400. }
  401. }]);
  402. return FlashMediaSource;
  403. })(_videoJs2['default'].EventTarget);
  404. exports['default'] = FlashMediaSource;
  405. try {
  406. Object.defineProperty(FlashMediaSource.prototype, 'duration', {
  407. /**
  408. * Return the presentation duration.
  409. *
  410. * @return {Double} the duration of the media in seconds
  411. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  412. */
  413. get: function get() {
  414. if (!this.swfObj) {
  415. return NaN;
  416. }
  417. // get the current duration from the SWF
  418. return this.swfObj.vjs_getProperty('duration');
  419. },
  420. /**
  421. * Set the presentation duration.
  422. *
  423. * @param {Double} value the duration of the media in seconds
  424. * @return {Double} the duration of the media in seconds
  425. * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  426. */
  427. set: function set(value) {
  428. var i = undefined;
  429. var oldDuration = this.swfObj.vjs_getProperty('duration');
  430. this.swfObj.vjs_setProperty('duration', value);
  431. if (value < oldDuration) {
  432. // In MSE, this triggers the range removal algorithm which causes
  433. // an update to occur
  434. for (i = 0; i < this.sourceBuffers.length; i++) {
  435. this.sourceBuffers[i].remove(value, oldDuration);
  436. }
  437. }
  438. return value;
  439. }
  440. });
  441. } catch (e) {
  442. // IE8 throws if defineProperty is called on a non-DOM node. We
  443. // don't support IE8 but we shouldn't throw an error if loaded
  444. // there.
  445. FlashMediaSource.prototype.duration = NaN;
  446. }
  447. for (var property in _flashConstants2['default']) {
  448. FlashMediaSource[property] = _flashConstants2['default'][property];
  449. }
  450. module.exports = exports['default'];
  451. },{"./codec-utils":2,"./flash-constants":4,"./flash-source-buffer":6,"global/document":15,"video.js":135}],6:[function(require,module,exports){
  452. /**
  453. * @file flash-source-buffer.js
  454. */
  455. 'use strict';
  456. Object.defineProperty(exports, '__esModule', {
  457. value: true
  458. });
  459. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  460. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  461. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  462. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  463. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  464. var _globalWindow = require('global/window');
  465. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  466. var _videoJs = require('video.js');
  467. var _videoJs2 = _interopRequireDefault(_videoJs);
  468. var _muxJsLibFlv = require('mux.js/lib/flv');
  469. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  470. var _removeCuesFromTrack = require('./remove-cues-from-track');
  471. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  472. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  473. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  474. var _addTextTrackData = require('./add-text-track-data');
  475. var _flashTransmuxerWorker = require('./flash-transmuxer-worker');
  476. var _flashTransmuxerWorker2 = _interopRequireDefault(_flashTransmuxerWorker);
  477. var _webwackify = require('webwackify');
  478. var _webwackify2 = _interopRequireDefault(_webwackify);
  479. var _flashConstants = require('./flash-constants');
  480. var _flashConstants2 = _interopRequireDefault(_flashConstants);
  481. var resolveFlashTransmuxWorker = function resolveFlashTransmuxWorker() {
  482. var result = undefined;
  483. try {
  484. result = require.resolve('./flash-transmuxer-worker');
  485. } catch (e) {
  486. // no result
  487. }
  488. return result;
  489. };
  490. /**
  491. * A wrapper around the setTimeout function that uses
  492. * the flash constant time between ticks value.
  493. *
  494. * @param {Function} func the function callback to run
  495. * @private
  496. */
  497. var scheduleTick = function scheduleTick(func) {
  498. // Chrome doesn't invoke requestAnimationFrame callbacks
  499. // in background tabs, so use setTimeout.
  500. _globalWindow2['default'].setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
  501. };
  502. /**
  503. * Generates a random string of max length 6
  504. *
  505. * @return {String} the randomly generated string
  506. * @function generateRandomString
  507. * @private
  508. */
  509. var generateRandomString = function generateRandomString() {
  510. return Math.random().toString(36).slice(2, 8);
  511. };
  512. /**
  513. * Round a number to a specified number of places much like
  514. * toFixed but return a number instead of a string representation.
  515. *
  516. * @param {Number} num A number
  517. * @param {Number} places The number of decimal places which to
  518. * round
  519. * @private
  520. */
  521. var toDecimalPlaces = function toDecimalPlaces(num, places) {
  522. if (typeof places !== 'number' || places < 0) {
  523. places = 0;
  524. }
  525. var scale = Math.pow(10, places);
  526. return Math.round(num * scale) / scale;
  527. };
  528. /**
  529. * A SourceBuffer implementation for Flash rather than HTML.
  530. *
  531. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  532. * @param {Object} mediaSource the flash media source
  533. * @class FlashSourceBuffer
  534. * @extends videojs.EventTarget
  535. */
  536. var FlashSourceBuffer = (function (_videojs$EventTarget) {
  537. _inherits(FlashSourceBuffer, _videojs$EventTarget);
  538. function FlashSourceBuffer(mediaSource) {
  539. var _this = this;
  540. _classCallCheck(this, FlashSourceBuffer);
  541. _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
  542. var encodedHeader = undefined;
  543. // Start off using the globally defined value but refine
  544. // as we append data into flash
  545. this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;
  546. // byte arrays queued to be appended
  547. this.buffer_ = [];
  548. // the total number of queued bytes
  549. this.bufferSize_ = 0;
  550. // to be able to determine the correct position to seek to, we
  551. // need to retain information about the mapping between the
  552. // media timeline and PTS values
  553. this.basePtsOffset_ = NaN;
  554. this.mediaSource_ = mediaSource;
  555. this.audioBufferEnd_ = NaN;
  556. this.videoBufferEnd_ = NaN;
  557. // indicates whether the asynchronous continuation of an operation
  558. // is still being processed
  559. // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
  560. this.updating = false;
  561. this.timestampOffset_ = 0;
  562. encodedHeader = _globalWindow2['default'].btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(_muxJsLibFlv2['default'].getFlvHeader())));
  563. // create function names with added randomness for the global callbacks flash will use
  564. // to get data from javascript into the swf. Random strings are added as a safety
  565. // measure for pages with multiple players since these functions will be global
  566. // instead of per instance. When making a call to the swf, the browser generates a
  567. // try catch code snippet, but just takes the function name and writes out an unquoted
  568. // call to that function. If the player id has any special characters, this will result
  569. // in an error, so safePlayerId replaces all special characters to '_'
  570. var safePlayerId = this.mediaSource_.player_.id().replace(/[^a-zA-Z0-9]/g, '_');
  571. this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
  572. this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();
  573. _globalWindow2['default'][this.flashEncodedHeaderName_] = function () {
  574. delete _globalWindow2['default'][_this.flashEncodedHeaderName_];
  575. return encodedHeader;
  576. };
  577. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);
  578. this.transmuxer_ = (0, _webwackify2['default'])(_flashTransmuxerWorker2['default'], resolveFlashTransmuxWorker());
  579. this.transmuxer_.postMessage({ action: 'init', options: {} });
  580. this.transmuxer_.onmessage = function (event) {
  581. if (event.data.action === 'data') {
  582. _this.receiveBuffer_(event.data.segment);
  583. }
  584. };
  585. this.one('updateend', function () {
  586. _this.mediaSource_.tech_.trigger('loadedmetadata');
  587. });
  588. Object.defineProperty(this, 'timestampOffset', {
  589. get: function get() {
  590. return this.timestampOffset_;
  591. },
  592. set: function set(val) {
  593. if (typeof val === 'number' && val >= 0) {
  594. this.timestampOffset_ = val;
  595. // We have to tell flash to expect a discontinuity
  596. this.mediaSource_.swfObj.vjs_discontinuity();
  597. // the media <-> PTS mapping must be re-established after
  598. // the discontinuity
  599. this.basePtsOffset_ = NaN;
  600. this.audioBufferEnd_ = NaN;
  601. this.videoBufferEnd_ = NaN;
  602. this.transmuxer_.postMessage({ action: 'reset' });
  603. }
  604. }
  605. });
  606. Object.defineProperty(this, 'buffered', {
  607. get: function get() {
  608. if (!this.mediaSource_ || !this.mediaSource_.swfObj || !('vjs_getProperty' in this.mediaSource_.swfObj)) {
  609. return _videoJs2['default'].createTimeRange();
  610. }
  611. var buffered = this.mediaSource_.swfObj.vjs_getProperty('buffered');
  612. if (buffered && buffered.length) {
  613. buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
  614. buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
  615. }
  616. return _videoJs2['default'].createTimeRanges(buffered);
  617. }
  618. });
  619. // On a seek we remove all text track data since flash has no concept
  620. // of a buffered-range and everything else is reset on seek
  621. this.mediaSource_.player_.on('seeked', function () {
  622. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
  623. if (_this.inbandTextTracks_) {
  624. for (var track in _this.inbandTextTracks_) {
  625. (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTracks_[track]);
  626. }
  627. }
  628. });
  629. var onHlsReset = this.onHlsReset_.bind(this);
  630. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  631. // resets its state and flushes the buffer
  632. this.mediaSource_.player_.tech_.on('hls-reset', onHlsReset);
  633. this.mediaSource_.player_.tech_.hls.on('dispose', function () {
  634. _this.transmuxer_.terminate();
  635. _this.mediaSource_.player_.tech_.off('hls-reset', onHlsReset);
  636. });
  637. }
  638. /**
  639. * Append bytes to the sourcebuffers buffer, in this case we
  640. * have to append it to swf object.
  641. *
  642. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  643. * @param {Array} bytes
  644. */
  645. _createClass(FlashSourceBuffer, [{
  646. key: 'appendBuffer',
  647. value: function appendBuffer(bytes) {
  648. var error = undefined;
  649. if (this.updating) {
  650. error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
  651. error.name = 'InvalidStateError';
  652. error.code = 11;
  653. throw error;
  654. }
  655. this.updating = true;
  656. this.mediaSource_.readyState = 'open';
  657. this.trigger({ type: 'update' });
  658. this.transmuxer_.postMessage({
  659. action: 'push',
  660. data: bytes.buffer,
  661. byteOffset: bytes.byteOffset,
  662. byteLength: bytes.byteLength
  663. }, [bytes.buffer]);
  664. this.transmuxer_.postMessage({ action: 'flush' });
  665. }
  666. /**
  667. * Reset the parser and remove any data queued to be sent to the SWF.
  668. *
  669. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  670. */
  671. }, {
  672. key: 'abort',
  673. value: function abort() {
  674. this.buffer_ = [];
  675. this.bufferSize_ = 0;
  676. this.mediaSource_.swfObj.vjs_abort();
  677. // report any outstanding updates have ended
  678. if (this.updating) {
  679. this.updating = false;
  680. this.trigger({ type: 'updateend' });
  681. }
  682. }
  683. /**
  684. * Flash cannot remove ranges already buffered in the NetStream
  685. * but seeking clears the buffer entirely. For most purposes,
  686. * having this operation act as a no-op is acceptable.
  687. *
  688. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  689. * @param {Double} start start of the section to remove
  690. * @param {Double} end end of the section to remove
  691. */
  692. }, {
  693. key: 'remove',
  694. value: function remove(start, end) {
  695. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  696. if (this.inbandTextTracks_) {
  697. for (var track in this.inbandTextTracks_) {
  698. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  699. }
  700. }
  701. this.trigger({ type: 'update' });
  702. this.trigger({ type: 'updateend' });
  703. }
  704. /**
  705. * Receive a buffer from the flv.
  706. *
  707. * @param {Object} segment
  708. * @private
  709. */
  710. }, {
  711. key: 'receiveBuffer_',
  712. value: function receiveBuffer_(segment) {
  713. var _this2 = this;
  714. // create an in-band caption track if one is present in the segment
  715. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  716. (0, _addTextTrackData.addTextTrackData)(this, segment.captions, segment.metadata);
  717. // Do this asynchronously since convertTagsToData_ can be time consuming
  718. scheduleTick(function () {
  719. var flvBytes = _this2.convertTagsToData_(segment);
  720. if (_this2.buffer_.length === 0) {
  721. scheduleTick(_this2.processBuffer_.bind(_this2));
  722. }
  723. if (flvBytes) {
  724. _this2.buffer_.push(flvBytes);
  725. _this2.bufferSize_ += flvBytes.byteLength;
  726. }
  727. });
  728. }
  729. /**
  730. * Append a portion of the current buffer to the SWF.
  731. *
  732. * @private
  733. */
  734. }, {
  735. key: 'processBuffer_',
  736. value: function processBuffer_() {
  737. var _this3 = this;
  738. var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;
  739. if (!this.buffer_.length) {
  740. if (this.updating !== false) {
  741. this.updating = false;
  742. this.trigger({ type: 'updateend' });
  743. }
  744. // do nothing if the buffer is empty
  745. return;
  746. }
  747. // concatenate appends up to the max append size
  748. var chunk = this.buffer_[0].subarray(0, chunkSize);
  749. // requeue any bytes that won't make it this round
  750. if (chunk.byteLength < chunkSize || this.buffer_[0].byteLength === chunkSize) {
  751. this.buffer_.shift();
  752. } else {
  753. this.buffer_[0] = this.buffer_[0].subarray(chunkSize);
  754. }
  755. this.bufferSize_ -= chunk.byteLength;
  756. // base64 encode the bytes
  757. var binary = [];
  758. var length = chunk.byteLength;
  759. for (var i = 0; i < length; i++) {
  760. binary.push(String.fromCharCode(chunk[i]));
  761. }
  762. var b64str = _globalWindow2['default'].btoa(binary.join(''));
  763. _globalWindow2['default'][this.flashEncodedDataName_] = function () {
  764. // schedule another processBuffer to process any left over data or to
  765. // trigger updateend
  766. scheduleTick(_this3.processBuffer_.bind(_this3));
  767. delete _globalWindow2['default'][_this3.flashEncodedDataName_];
  768. return b64str;
  769. };
  770. // Notify the swf that segment data is ready to be appended
  771. this.mediaSource_.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
  772. }
  773. /**
  774. * Turns an array of flv tags into a Uint8Array representing the
  775. * flv data. Also removes any tags that are before the current
  776. * time so that playback begins at or slightly after the right
  777. * place on a seek
  778. *
  779. * @private
  780. * @param {Object} segmentData object of segment data
  781. */
  782. }, {
  783. key: 'convertTagsToData_',
  784. value: function convertTagsToData_(segmentData) {
  785. var segmentByteLength = 0;
  786. var tech = this.mediaSource_.tech_;
  787. var videoTargetPts = 0;
  788. var segment = undefined;
  789. var videoTags = segmentData.tags.videoTags;
  790. var audioTags = segmentData.tags.audioTags;
  791. // Establish the media timeline to PTS translation if we don't
  792. // have one already
  793. if (isNaN(this.basePtsOffset_) && (videoTags.length || audioTags.length)) {
  794. // We know there is at least one video or audio tag, but since we may not have both,
  795. // we use pts: Infinity for the missing tag. The will force the following Math.min
  796. // call will to use the proper pts value since it will always be less than Infinity
  797. var firstVideoTag = videoTags[0] || { pts: Infinity };
  798. var firstAudioTag = audioTags[0] || { pts: Infinity };
  799. this.basePtsOffset_ = Math.min(firstAudioTag.pts, firstVideoTag.pts);
  800. }
  801. if (tech.seeking()) {
  802. // Do not use previously saved buffer end values while seeking since buffer
  803. // is cleared on all seeks
  804. this.videoBufferEnd_ = NaN;
  805. this.audioBufferEnd_ = NaN;
  806. }
  807. if (isNaN(this.videoBufferEnd_)) {
  808. if (tech.buffered().length) {
  809. videoTargetPts = tech.buffered().end(0) - this.timestampOffset;
  810. }
  811. // Trim to currentTime if seeking
  812. if (tech.seeking()) {
  813. videoTargetPts = Math.max(videoTargetPts, tech.currentTime() - this.timestampOffset);
  814. }
  815. // PTS values are represented in milliseconds
  816. videoTargetPts *= 1e3;
  817. videoTargetPts += this.basePtsOffset_;
  818. } else {
  819. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  820. // could append an overlapping segment, in which case there is a high likelyhood
  821. // a tag could have a matching pts to videoBufferEnd_, which would cause
  822. // that tag to get appended by the tag.pts >= targetPts check below even though it
  823. // is a duplicate of what was previously appended
  824. videoTargetPts = this.videoBufferEnd_ + 0.1;
  825. }
  826. // filter complete GOPs with a presentation time less than the seek target/end of buffer
  827. var currentIndex = videoTags.length;
  828. // if the last tag is beyond videoTargetPts, then do not search the list for a GOP
  829. // since our videoTargetPts lies in a future segment
  830. if (currentIndex && videoTags[currentIndex - 1].pts >= videoTargetPts) {
  831. // Start by walking backwards from the end of the list until we reach a tag that
  832. // is equal to or less than videoTargetPts
  833. while (--currentIndex) {
  834. var currentTag = videoTags[currentIndex];
  835. if (currentTag.pts > videoTargetPts) {
  836. continue;
  837. }
  838. // if we see a keyFrame or metadata tag once we've gone below videoTargetPts,
  839. // exit the loop as this is the start of the GOP that we want to append
  840. if (currentTag.keyFrame || currentTag.metaDataTag) {
  841. break;
  842. }
  843. }
  844. // We need to check if there are any metadata tags that come before currentIndex
  845. // as those will be metadata tags associated with the GOP we are appending
  846. // There could be 0 to 2 metadata tags that come before the currentIndex depending
  847. // on what videoTargetPts is and whether the transmuxer prepended metadata tags to this
  848. // key frame
  849. while (currentIndex) {
  850. var nextTag = videoTags[currentIndex - 1];
  851. if (!nextTag.metaDataTag) {
  852. break;
  853. }
  854. currentIndex--;
  855. }
  856. }
  857. var filteredVideoTags = videoTags.slice(currentIndex);
  858. var audioTargetPts = undefined;
  859. if (isNaN(this.audioBufferEnd_)) {
  860. audioTargetPts = videoTargetPts;
  861. } else {
  862. // Add a fudge factor of 0.1 to the last video pts appended since a rendition change
  863. // could append an overlapping segment, in which case there is a high likelyhood
  864. // a tag could have a matching pts to videoBufferEnd_, which would cause
  865. // that tag to get appended by the tag.pts >= targetPts check below even though it
  866. // is a duplicate of what was previously appended
  867. audioTargetPts = this.audioBufferEnd_ + 0.1;
  868. }
  869. if (filteredVideoTags.length) {
  870. // If targetPts intersects a GOP and we appended the tags for the GOP that came
  871. // before targetPts, we want to make sure to trim audio tags at the pts
  872. // of the first video tag to avoid brief moments of silence
  873. audioTargetPts = Math.min(audioTargetPts, filteredVideoTags[0].pts);
  874. }
  875. // skip tags with a presentation time less than the seek target/end of buffer
  876. currentIndex = 0;
  877. while (currentIndex < audioTags.length) {
  878. if (audioTags[currentIndex].pts >= audioTargetPts) {
  879. break;
  880. }
  881. currentIndex++;
  882. }
  883. var filteredAudioTags = audioTags.slice(currentIndex);
  884. // update the audio and video buffer ends
  885. if (filteredAudioTags.length) {
  886. this.audioBufferEnd_ = filteredAudioTags[filteredAudioTags.length - 1].pts;
  887. }
  888. if (filteredVideoTags.length) {
  889. this.videoBufferEnd_ = filteredVideoTags[filteredVideoTags.length - 1].pts;
  890. }
  891. var tags = this.getOrderedTags_(filteredVideoTags, filteredAudioTags);
  892. if (tags.length === 0) {
  893. return;
  894. }
  895. // If we are appending data that comes before our target pts, we want to tell
  896. // the swf to adjust its notion of current time to account for the extra tags
  897. // we are appending to complete the GOP that intersects with targetPts
  898. if (tags[0].pts < videoTargetPts && tech.seeking()) {
  899. var fudgeFactor = 1 / 30;
  900. var currentTime = tech.currentTime();
  901. var diff = (videoTargetPts - tags[0].pts) / 1e3;
  902. var adjustedTime = currentTime - diff;
  903. if (adjustedTime < fudgeFactor) {
  904. adjustedTime = 0;
  905. }
  906. try {
  907. this.mediaSource_.swfObj.vjs_adjustCurrentTime(adjustedTime);
  908. } catch (e) {
  909. // no-op for backwards compatability of swf. If adjustCurrentTime fails,
  910. // the swf may incorrectly report currentTime and buffered ranges
  911. // but should not affect playback over than the time displayed on the
  912. // progress bar is inaccurate
  913. }
  914. }
  915. // concatenate the bytes into a single segment
  916. for (var i = 0; i < tags.length; i++) {
  917. segmentByteLength += tags[i].bytes.byteLength;
  918. }
  919. segment = new Uint8Array(segmentByteLength);
  920. for (var i = 0, j = 0; i < tags.length; i++) {
  921. segment.set(tags[i].bytes, j);
  922. j += tags[i].bytes.byteLength;
  923. }
  924. return segment;
  925. }
  926. /**
  927. * Assemble the FLV tags in decoder order.
  928. *
  929. * @private
  930. * @param {Array} videoTags list of video tags
  931. * @param {Array} audioTags list of audio tags
  932. */
  933. }, {
  934. key: 'getOrderedTags_',
  935. value: function getOrderedTags_(videoTags, audioTags) {
  936. var tag = undefined;
  937. var tags = [];
  938. while (videoTags.length || audioTags.length) {
  939. if (!videoTags.length) {
  940. // only audio tags remain
  941. tag = audioTags.shift();
  942. } else if (!audioTags.length) {
  943. // only video tags remain
  944. tag = videoTags.shift();
  945. } else if (audioTags[0].dts < videoTags[0].dts) {
  946. // audio should be decoded next
  947. tag = audioTags.shift();
  948. } else {
  949. // video should be decoded next
  950. tag = videoTags.shift();
  951. }
  952. tags.push(tag);
  953. }
  954. return tags;
  955. }
  956. }, {
  957. key: 'onHlsReset_',
  958. value: function onHlsReset_() {
  959. this.transmuxer_.postMessage({ action: 'resetCaptions' });
  960. }
  961. }]);
  962. return FlashSourceBuffer;
  963. })(_videoJs2['default'].EventTarget);
  964. exports['default'] = FlashSourceBuffer;
  965. module.exports = exports['default'];
  966. },{"./add-text-track-data":1,"./create-text-tracks-if-necessary":3,"./flash-constants":4,"./flash-transmuxer-worker":7,"./remove-cues-from-track":9,"global/window":16,"mux.js/lib/flv":25,"video.js":135,"webwackify":142}],7:[function(require,module,exports){
  967. /**
  968. * @file flash-transmuxer-worker.js
  969. */
  970. 'use strict';
  971. Object.defineProperty(exports, '__esModule', {
  972. value: true
  973. });
  974. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  975. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  976. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  977. var _globalWindow = require('global/window');
  978. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  979. var _muxJsLibFlv = require('mux.js/lib/flv');
  980. var _muxJsLibFlv2 = _interopRequireDefault(_muxJsLibFlv);
  981. /**
  982. * Re-emits transmuxer events by converting them into messages to the
  983. * world outside the worker.
  984. *
  985. * @param {Object} transmuxer the transmuxer to wire events on
  986. * @private
  987. */
  988. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  989. transmuxer.on('data', function (segment) {
  990. _globalWindow2['default'].postMessage({
  991. action: 'data',
  992. segment: segment
  993. });
  994. });
  995. transmuxer.on('done', function (data) {
  996. _globalWindow2['default'].postMessage({ action: 'done' });
  997. });
  998. };
  999. /**
  1000. * All incoming messages route through this hash. If no function exists
  1001. * to handle an incoming message, then we ignore the message.
  1002. *
  1003. * @class MessageHandlers
  1004. * @param {Object} options the options to initialize with
  1005. */
  1006. var MessageHandlers = (function () {
  1007. function MessageHandlers(options) {
  1008. _classCallCheck(this, MessageHandlers);
  1009. this.options = options || {};
  1010. this.init();
  1011. }
  1012. /**
  1013. * Our web wroker interface so that things can talk to mux.js
  1014. * that will be running in a web worker. The scope is passed to this by
  1015. * webworkify.
  1016. *
  1017. * @param {Object} self the scope for the web worker
  1018. */
  1019. /**
  1020. * initialize our web worker and wire all the events.
  1021. */
  1022. _createClass(MessageHandlers, [{
  1023. key: 'init',
  1024. value: function init() {
  1025. if (this.transmuxer) {
  1026. this.transmuxer.dispose();
  1027. }
  1028. this.transmuxer = new _muxJsLibFlv2['default'].Transmuxer(this.options);
  1029. wireTransmuxerEvents(this.transmuxer);
  1030. }
  1031. /**
  1032. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  1033. * processing.
  1034. *
  1035. * @param {ArrayBuffer} data data to push into the muxer
  1036. */
  1037. }, {
  1038. key: 'push',
  1039. value: function push(data) {
  1040. // Cast array buffer to correct type for transmuxer
  1041. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  1042. this.transmuxer.push(segment);
  1043. }
  1044. /**
  1045. * Recreate the transmuxer so that the next segment added via `push`
  1046. * start with a fresh transmuxer.
  1047. */
  1048. }, {
  1049. key: 'reset',
  1050. value: function reset() {
  1051. this.init();
  1052. }
  1053. /**
  1054. * Forces the pipeline to finish processing the last segment and emit its
  1055. * results.
  1056. */
  1057. }, {
  1058. key: 'flush',
  1059. value: function flush() {
  1060. this.transmuxer.flush();
  1061. }
  1062. }, {
  1063. key: 'resetCaptions',
  1064. value: function resetCaptions() {
  1065. this.transmuxer.resetCaptions();
  1066. }
  1067. }]);
  1068. return MessageHandlers;
  1069. })();
  1070. var FlashTransmuxerWorker = function FlashTransmuxerWorker(self) {
  1071. self.onmessage = function (event) {
  1072. if (event.data.action === 'init' && event.data.options) {
  1073. this.messageHandlers = new MessageHandlers(event.data.options);
  1074. return;
  1075. }
  1076. if (!this.messageHandlers) {
  1077. this.messageHandlers = new MessageHandlers();
  1078. }
  1079. if (event.data && event.data.action && event.data.action !== 'init') {
  1080. if (this.messageHandlers[event.data.action]) {
  1081. this.messageHandlers[event.data.action](event.data);
  1082. }
  1083. }
  1084. };
  1085. };
  1086. exports['default'] = function (self) {
  1087. return new FlashTransmuxerWorker(self);
  1088. };
  1089. module.exports = exports['default'];
  1090. },{"global/window":16,"mux.js/lib/flv":25}],8:[function(require,module,exports){
  1091. /**
  1092. * @file html-media-source.js
  1093. */
  1094. 'use strict';
  1095. Object.defineProperty(exports, '__esModule', {
  1096. value: true
  1097. });
  1098. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  1099. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  1100. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1101. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1102. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  1103. var _globalWindow = require('global/window');
  1104. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  1105. var _globalDocument = require('global/document');
  1106. var _globalDocument2 = _interopRequireDefault(_globalDocument);
  1107. var _videoJs = require('video.js');
  1108. var _videoJs2 = _interopRequireDefault(_videoJs);
  1109. var _virtualSourceBuffer = require('./virtual-source-buffer');
  1110. var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);
  1111. var _addTextTrackData = require('./add-text-track-data');
  1112. var _codecUtils = require('./codec-utils');
  1113. /**
  1114. * Our MediaSource implementation in HTML, mimics native
  1115. * MediaSource where/if possible.
  1116. *
  1117. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
  1118. * @class HtmlMediaSource
  1119. * @extends videojs.EventTarget
  1120. */
  1121. var HtmlMediaSource = (function (_videojs$EventTarget) {
  1122. _inherits(HtmlMediaSource, _videojs$EventTarget);
  1123. function HtmlMediaSource() {
  1124. var _this = this;
  1125. _classCallCheck(this, HtmlMediaSource);
  1126. _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
  1127. var property = undefined;
  1128. this.nativeMediaSource_ = new _globalWindow2['default'].MediaSource();
  1129. // delegate to the native MediaSource's methods by default
  1130. for (property in this.nativeMediaSource_) {
  1131. if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
  1132. this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
  1133. }
  1134. }
  1135. // emulate `duration` and `seekable` until seeking can be
  1136. // handled uniformly for live streams
  1137. // see https://github.com/w3c/media-source/issues/5
  1138. this.duration_ = NaN;
  1139. Object.defineProperty(this, 'duration', {
  1140. get: function get() {
  1141. if (this.duration_ === Infinity) {
  1142. return this.duration_;
  1143. }
  1144. return this.nativeMediaSource_.duration;
  1145. },
  1146. set: function set(duration) {
  1147. this.duration_ = duration;
  1148. if (duration !== Infinity) {
  1149. this.nativeMediaSource_.duration = duration;
  1150. return;
  1151. }
  1152. }
  1153. });
  1154. Object.defineProperty(this, 'seekable', {
  1155. get: function get() {
  1156. if (this.duration_ === Infinity) {
  1157. return _videoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
  1158. }
  1159. return this.nativeMediaSource_.seekable;
  1160. }
  1161. });
  1162. Object.defineProperty(this, 'readyState', {
  1163. get: function get() {
  1164. return this.nativeMediaSource_.readyState;
  1165. }
  1166. });
  1167. Object.defineProperty(this, 'activeSourceBuffers', {
  1168. get: function get() {
  1169. return this.activeSourceBuffers_;
  1170. }
  1171. });
  1172. // the list of virtual and native SourceBuffers created by this
  1173. // MediaSource
  1174. this.sourceBuffers = [];
  1175. this.activeSourceBuffers_ = [];
  1176. /**
  1177. * update the list of active source buffers based upon various
  1178. * imformation from HLS and video.js
  1179. *
  1180. * @private
  1181. */
  1182. this.updateActiveSourceBuffers_ = function () {
  1183. // Retain the reference but empty the array
  1184. _this.activeSourceBuffers_.length = 0;
  1185. // If there is only one source buffer, then it will always be active and audio will
  1186. // be disabled based on the codec of the source buffer
  1187. if (_this.sourceBuffers.length === 1) {
  1188. var sourceBuffer = _this.sourceBuffers[0];
  1189. sourceBuffer.appendAudioInitSegment_ = true;
  1190. sourceBuffer.audioDisabled_ = !sourceBuffer.audioCodec_;
  1191. _this.activeSourceBuffers_.push(sourceBuffer);
  1192. return;
  1193. }
  1194. // There are 2 source buffers, a combined (possibly video only) source buffer and
  1195. // and an audio only source buffer.
  1196. // By default, the audio in the combined virtual source buffer is enabled
  1197. // and the audio-only source buffer (if it exists) is disabled.
  1198. var disableCombined = false;
  1199. var disableAudioOnly = true;
  1200. // TODO: maybe we can store the sourcebuffers on the track objects?
  1201. // safari may do something like this
  1202. for (var i = 0; i < _this.player_.audioTracks().length; i++) {
  1203. var track = _this.player_.audioTracks()[i];
  1204. if (track.enabled && track.kind !== 'main') {
  1205. // The enabled track is an alternate audio track so disable the audio in
  1206. // the combined source buffer and enable the audio-only source buffer.
  1207. disableCombined = true;
  1208. disableAudioOnly = false;
  1209. break;
  1210. }
  1211. }
  1212. _this.sourceBuffers.forEach(function (sourceBuffer) {
  1213. /* eslinst-disable */
  1214. // TODO once codecs are required, we can switch to using the codecs to determine
  1215. // what stream is the video stream, rather than relying on videoTracks
  1216. /* eslinst-enable */
  1217. sourceBuffer.appendAudioInitSegment_ = true;
  1218. if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  1219. // combined
  1220. sourceBuffer.audioDisabled_ = disableCombined;
  1221. } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
  1222. // If the "combined" source buffer is video only, then we do not want
  1223. // disable the audio-only source buffer (this is mostly for demuxed
  1224. // audio and video hls)
  1225. sourceBuffer.audioDisabled_ = true;
  1226. disableAudioOnly = false;
  1227. } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
  1228. // audio only
  1229. sourceBuffer.audioDisabled_ = disableAudioOnly;
  1230. if (disableAudioOnly) {
  1231. return;
  1232. }
  1233. }
  1234. _this.activeSourceBuffers_.push(sourceBuffer);
  1235. });
  1236. };
  1237. this.onPlayerMediachange_ = function () {
  1238. _this.sourceBuffers.forEach(function (sourceBuffer) {
  1239. sourceBuffer.appendAudioInitSegment_ = true;
  1240. });
  1241. };
  1242. this.onHlsReset_ = function () {
  1243. _this.sourceBuffers.forEach(function (sourceBuffer) {
  1244. if (sourceBuffer.transmuxer_) {
  1245. sourceBuffer.transmuxer_.postMessage({ action: 'resetCaptions' });
  1246. }
  1247. });
  1248. };
  1249. this.onHlsSegmentTimeMapping_ = function (event) {
  1250. _this.sourceBuffers.forEach(function (buffer) {
  1251. return buffer.timeMapping_ = event.mapping;
  1252. });
  1253. };
  1254. // Re-emit MediaSource events on the polyfill
  1255. ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
  1256. this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
  1257. }, this);
  1258. // capture the associated player when the MediaSource is
  1259. // successfully attached
  1260. this.on('sourceopen', function (event) {
  1261. // Get the player this MediaSource is attached to
  1262. var video = _globalDocument2['default'].querySelector('[src="' + _this.url_ + '"]');
  1263. if (!video) {
  1264. return;
  1265. }
  1266. _this.player_ = (0, _videoJs2['default'])(video.parentNode);
  1267. // hls-reset is fired by videojs.Hls on to the tech after the main SegmentLoader
  1268. // resets its state and flushes the buffer
  1269. _this.player_.tech_.on('hls-reset', _this.onHlsReset_);
  1270. // hls-segment-time-mapping is fired by videojs.Hls on to the tech after the main
  1271. // SegmentLoader inspects an MTS segment and has an accurate stream to display
  1272. // time mapping
  1273. _this.player_.tech_.on('hls-segment-time-mapping', _this.onHlsSegmentTimeMapping_);
  1274. if (_this.player_.audioTracks && _this.player_.audioTracks()) {
  1275. _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
  1276. _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
  1277. _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
  1278. }
  1279. _this.player_.on('mediachange', _this.onPlayerMediachange_);
  1280. });
  1281. this.on('sourceended', function (event) {
  1282. var duration = (0, _addTextTrackData.durationOfVideo)(_this.duration);
  1283. for (var i = 0; i < _this.sourceBuffers.length; i++) {
  1284. var sourcebuffer = _this.sourceBuffers[i];
  1285. var cues = sourcebuffer.metadataTrack_ && sourcebuffer.metadataTrack_.cues;
  1286. if (cues && cues.length) {
  1287. cues[cues.length - 1].endTime = duration;
  1288. }
  1289. }
  1290. });
  1291. // explicitly terminate any WebWorkers that were created
  1292. // by SourceHandlers
  1293. this.on('sourceclose', function (event) {
  1294. this.sourceBuffers.forEach(function (sourceBuffer) {
  1295. if (sourceBuffer.transmuxer_) {
  1296. sourceBuffer.transmuxer_.terminate();
  1297. }
  1298. });
  1299. this.sourceBuffers.length = 0;
  1300. if (!this.player_) {
  1301. return;
  1302. }
  1303. if (this.player_.audioTracks && this.player_.audioTracks()) {
  1304. this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
  1305. this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
  1306. this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
  1307. }
  1308. // We can only change this if the player hasn't been disposed of yet
  1309. // because `off` eventually tries to use the el_ property. If it has
  1310. // been disposed of, then don't worry about it because there are no
  1311. // event handlers left to unbind anyway
  1312. if (this.player_.el_) {
  1313. this.player_.off('mediachange', this.onPlayerMediachange_);
  1314. this.player_.tech_.off('hls-reset', this.onHlsReset_);
  1315. this.player_.tech_.off('hls-segment-time-mapping', this.onHlsSegmentTimeMapping_);
  1316. }
  1317. });
  1318. }
  1319. /**
  1320. * Add a range that that can now be seeked to.
  1321. *
  1322. * @param {Double} start where to start the addition
  1323. * @param {Double} end where to end the addition
  1324. * @private
  1325. */
  1326. _createClass(HtmlMediaSource, [{
  1327. key: 'addSeekableRange_',
  1328. value: function addSeekableRange_(start, end) {
  1329. var error = undefined;
  1330. if (this.duration !== Infinity) {
  1331. error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
  1332. error.name = 'InvalidStateError';
  1333. error.code = 11;
  1334. throw error;
  1335. }
  1336. if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
  1337. this.nativeMediaSource_.duration = end;
  1338. }
  1339. }
  1340. /**
  1341. * Add a source buffer to the media source.
  1342. *
  1343. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
  1344. * @param {String} type the content-type of the content
  1345. * @return {Object} the created source buffer
  1346. */
  1347. }, {
  1348. key: 'addSourceBuffer',
  1349. value: function addSourceBuffer(type) {
  1350. var buffer = undefined;
  1351. var parsedType = (0, _codecUtils.parseContentType)(type);
  1352. // Create a VirtualSourceBuffer to transmux MPEG-2 transport
  1353. // stream segments into fragmented MP4s
  1354. if (/^(video|audio)\/mp2t$/i.test(parsedType.type)) {
  1355. var codecs = [];
  1356. if (parsedType.parameters && parsedType.parameters.codecs) {
  1357. codecs = parsedType.parameters.codecs.split(',');
  1358. codecs = (0, _codecUtils.translateLegacyCodecs)(codecs);
  1359. codecs = codecs.filter(function (codec) {
  1360. return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
  1361. });
  1362. }
  1363. if (codecs.length === 0) {
  1364. codecs = ['avc1.4d400d', 'mp4a.40.2'];
  1365. }
  1366. buffer = new _virtualSourceBuffer2['default'](this, codecs);
  1367. if (this.sourceBuffers.length !== 0) {
  1368. // If another VirtualSourceBuffer already exists, then we are creating a
  1369. // SourceBuffer for an alternate audio track and therefore we know that
  1370. // the source has both an audio and video track.
  1371. // That means we should trigger the manual creation of the real
  1372. // SourceBuffers instead of waiting for the transmuxer to return data
  1373. this.sourceBuffers[0].createRealSourceBuffers_();
  1374. buffer.createRealSourceBuffers_();
  1375. // Automatically disable the audio on the first source buffer if
  1376. // a second source buffer is ever created
  1377. this.sourceBuffers[0].audioDisabled_ = true;
  1378. }
  1379. } else {
  1380. // delegate to the native implementation
  1381. buffer = this.nativeMediaSource_.addSourceBuffer(type);
  1382. }
  1383. this.sourceBuffers.push(buffer);
  1384. return buffer;
  1385. }
  1386. }]);
  1387. return HtmlMediaSource;
  1388. })(_videoJs2['default'].EventTarget);
  1389. exports['default'] = HtmlMediaSource;
  1390. module.exports = exports['default'];
  1391. },{"./add-text-track-data":1,"./codec-utils":2,"./virtual-source-buffer":12,"global/document":15,"global/window":16,"video.js":135}],9:[function(require,module,exports){
  1392. /**
  1393. * @file remove-cues-from-track.js
  1394. */
  1395. /**
  1396. * Remove cues from a track on video.js.
  1397. *
  1398. * @param {Double} start start of where we should remove the cue
  1399. * @param {Double} end end of where the we should remove the cue
  1400. * @param {Object} track the text track to remove the cues from
  1401. * @private
  1402. */
  1403. "use strict";
  1404. Object.defineProperty(exports, "__esModule", {
  1405. value: true
  1406. });
  1407. var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
  1408. var i = undefined;
  1409. var cue = undefined;
  1410. if (!track) {
  1411. return;
  1412. }
  1413. if (!track.cues) {
  1414. return;
  1415. }
  1416. i = track.cues.length;
  1417. while (i--) {
  1418. cue = track.cues[i];
  1419. // Remove any overlapping cue
  1420. if (cue.startTime <= end && cue.endTime >= start) {
  1421. track.removeCue(cue);
  1422. }
  1423. }
  1424. };
  1425. exports["default"] = removeCuesFromTrack;
  1426. module.exports = exports["default"];
  1427. },{}],10:[function(require,module,exports){
  1428. /**
  1429. * @file transmuxer-worker.js
  1430. */
  1431. /**
  1432. * videojs-contrib-media-sources
  1433. *
  1434. * Copyright (c) 2015 Brightcove
  1435. * All rights reserved.
  1436. *
  1437. * Handles communication between the browser-world and the mux.js
  1438. * transmuxer running inside of a WebWorker by exposing a simple
  1439. * message-based interface to a Transmuxer object.
  1440. */
  1441. 'use strict';
  1442. Object.defineProperty(exports, '__esModule', {
  1443. value: true
  1444. });
  1445. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  1446. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1447. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1448. var _globalWindow = require('global/window');
  1449. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  1450. var _muxJsLibMp4 = require('mux.js/lib/mp4');
  1451. var _muxJsLibMp42 = _interopRequireDefault(_muxJsLibMp4);
  1452. /**
  1453. * Re-emits transmuxer events by converting them into messages to the
  1454. * world outside the worker.
  1455. *
  1456. * @param {Object} transmuxer the transmuxer to wire events on
  1457. * @private
  1458. */
  1459. var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  1460. transmuxer.on('data', function (segment) {
  1461. // transfer ownership of the underlying ArrayBuffer
  1462. // instead of doing a copy to save memory
  1463. // ArrayBuffers are transferable but generic TypedArrays are not
  1464. // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
  1465. var initArray = segment.initSegment;
  1466. segment.initSegment = {
  1467. data: initArray.buffer,
  1468. byteOffset: initArray.byteOffset,
  1469. byteLength: initArray.byteLength
  1470. };
  1471. var typedArray = segment.data;
  1472. segment.data = typedArray.buffer;
  1473. _globalWindow2['default'].postMessage({
  1474. action: 'data',
  1475. segment: segment,
  1476. byteOffset: typedArray.byteOffset,
  1477. byteLength: typedArray.byteLength
  1478. }, [segment.data]);
  1479. });
  1480. if (transmuxer.captionStream) {
  1481. transmuxer.captionStream.on('data', function (caption) {
  1482. _globalWindow2['default'].postMessage({
  1483. action: 'caption',
  1484. data: caption
  1485. });
  1486. });
  1487. }
  1488. transmuxer.on('done', function (data) {
  1489. _globalWindow2['default'].postMessage({ action: 'done' });
  1490. });
  1491. transmuxer.on('gopInfo', function (gopInfo) {
  1492. _globalWindow2['default'].postMessage({
  1493. action: 'gopInfo',
  1494. gopInfo: gopInfo
  1495. });
  1496. });
  1497. };
  1498. /**
  1499. * All incoming messages route through this hash. If no function exists
  1500. * to handle an incoming message, then we ignore the message.
  1501. *
  1502. * @class MessageHandlers
  1503. * @param {Object} options the options to initialize with
  1504. */
  1505. var MessageHandlers = (function () {
  1506. function MessageHandlers(options) {
  1507. _classCallCheck(this, MessageHandlers);
  1508. this.options = options || {};
  1509. this.init();
  1510. }
  1511. /**
  1512. * Our web wroker interface so that things can talk to mux.js
  1513. * that will be running in a web worker. the scope is passed to this by
  1514. * webworkify.
  1515. *
  1516. * @param {Object} self the scope for the web worker
  1517. */
  1518. /**
  1519. * initialize our web worker and wire all the events.
  1520. */
  1521. _createClass(MessageHandlers, [{
  1522. key: 'init',
  1523. value: function init() {
  1524. if (this.transmuxer) {
  1525. this.transmuxer.dispose();
  1526. }
  1527. this.transmuxer = new _muxJsLibMp42['default'].Transmuxer(this.options);
  1528. wireTransmuxerEvents(this.transmuxer);
  1529. }
  1530. /**
  1531. * Adds data (a ts segment) to the start of the transmuxer pipeline for
  1532. * processing.
  1533. *
  1534. * @param {ArrayBuffer} data data to push into the muxer
  1535. */
  1536. }, {
  1537. key: 'push',
  1538. value: function push(data) {
  1539. // Cast array buffer to correct type for transmuxer
  1540. var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);
  1541. this.transmuxer.push(segment);
  1542. }
  1543. /**
  1544. * Recreate the transmuxer so that the next segment added via `push`
  1545. * start with a fresh transmuxer.
  1546. */
  1547. }, {
  1548. key: 'reset',
  1549. value: function reset() {
  1550. this.init();
  1551. }
  1552. /**
  1553. * Set the value that will be used as the `baseMediaDecodeTime` time for the
  1554. * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
  1555. * set relative to the first based on the PTS values.
  1556. *
  1557. * @param {Object} data used to set the timestamp offset in the muxer
  1558. */
  1559. }, {
  1560. key: 'setTimestampOffset',
  1561. value: function setTimestampOffset(data) {
  1562. var timestampOffset = data.timestampOffset || 0;
  1563. this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
  1564. }
  1565. }, {
  1566. key: 'setAudioAppendStart',
  1567. value: function setAudioAppendStart(data) {
  1568. this.transmuxer.setAudioAppendStart(Math.ceil(data.appendStart * 90000));
  1569. }
  1570. /**
  1571. * Forces the pipeline to finish processing the last segment and emit it's
  1572. * results.
  1573. *
  1574. * @param {Object} data event data, not really used
  1575. */
  1576. }, {
  1577. key: 'flush',
  1578. value: function flush(data) {
  1579. this.transmuxer.flush();
  1580. }
  1581. }, {
  1582. key: 'resetCaptions',
  1583. value: function resetCaptions() {
  1584. this.transmuxer.resetCaptions();
  1585. }
  1586. }, {
  1587. key: 'alignGopsWith',
  1588. value: function alignGopsWith(data) {
  1589. this.transmuxer.alignGopsWith(data.gopsToAlignWith.slice());
  1590. }
  1591. }]);
  1592. return MessageHandlers;
  1593. })();
  1594. var TransmuxerWorker = function TransmuxerWorker(self) {
  1595. self.onmessage = function (event) {
  1596. if (event.data.action === 'init' && event.data.options) {
  1597. this.messageHandlers = new MessageHandlers(event.data.options);
  1598. return;
  1599. }
  1600. if (!this.messageHandlers) {
  1601. this.messageHandlers = new MessageHandlers();
  1602. }
  1603. if (event.data && event.data.action && event.data.action !== 'init') {
  1604. if (this.messageHandlers[event.data.action]) {
  1605. this.messageHandlers[event.data.action](event.data);
  1606. }
  1607. }
  1608. };
  1609. };
  1610. exports['default'] = function (self) {
  1611. return new TransmuxerWorker(self);
  1612. };
  1613. module.exports = exports['default'];
  1614. },{"global/window":16,"mux.js/lib/mp4":33}],11:[function(require,module,exports){
  1615. /**
  1616. * @file videojs-contrib-media-sources.js
  1617. */
  1618. 'use strict';
  1619. Object.defineProperty(exports, '__esModule', {
  1620. value: true
  1621. });
  1622. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1623. var _globalWindow = require('global/window');
  1624. var _globalWindow2 = _interopRequireDefault(_globalWindow);
  1625. var _flashMediaSource = require('./flash-media-source');
  1626. var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);
  1627. var _htmlMediaSource = require('./html-media-source');
  1628. var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);
  1629. var _videoJs = require('video.js');
  1630. var _videoJs2 = _interopRequireDefault(_videoJs);
  1631. var urlCount = 0;
  1632. // ------------
  1633. // Media Source
  1634. // ------------
  1635. var defaults = {
  1636. // how to determine the MediaSource implementation to use. There
  1637. // are three available modes:
  1638. // - auto: use native MediaSources where available and Flash
  1639. // everywhere else
  1640. // - html5: always use native MediaSources
  1641. // - flash: always use the Flash MediaSource polyfill
  1642. mode: 'auto'
  1643. };
  1644. // store references to the media sources so they can be connected
  1645. // to a video element (a swf object)
  1646. // TODO: can we store this somewhere local to this module?
  1647. _videoJs2['default'].mediaSources = {};
  1648. /**
  1649. * Provide a method for a swf object to notify JS that a
  1650. * media source is now open.
  1651. *
  1652. * @param {String} msObjectURL string referencing the MSE Object URL
  1653. * @param {String} swfId the swf id
  1654. */
  1655. var open = function open(msObjectURL, swfId) {
  1656. var mediaSource = _videoJs2['default'].mediaSources[msObjectURL];
  1657. if (mediaSource) {
  1658. mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
  1659. } else {
  1660. throw new Error('Media Source not found (Video.js)');
  1661. }
  1662. };
  1663. /**
  1664. * Check to see if the native MediaSource object exists and supports
  1665. * an MP4 container with both H.264 video and AAC-LC audio.
  1666. *
  1667. * @return {Boolean} if native media sources are supported
  1668. */
  1669. var supportsNativeMediaSources = function supportsNativeMediaSources() {
  1670. return !!_globalWindow2['default'].MediaSource && !!_globalWindow2['default'].MediaSource.isTypeSupported && _globalWindow2['default'].MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
  1671. };
  1672. /**
  1673. * An emulation of the MediaSource API so that we can support
  1674. * native and non-native functionality such as flash and
  1675. * video/mp2t videos. returns an instance of HtmlMediaSource or
  1676. * FlashMediaSource depending on what is supported and what options
  1677. * are passed in.
  1678. *
  1679. * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
  1680. * @param {Object} options options to use during setup.
  1681. */
  1682. var MediaSource = function MediaSource(options) {
  1683. var settings = _videoJs2['default'].mergeOptions(defaults, options);
  1684. this.MediaSource = {
  1685. open: open,
  1686. supportsNativeMediaSources: supportsNativeMediaSources
  1687. };
  1688. // determine whether HTML MediaSources should be used
  1689. if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
  1690. return new _htmlMediaSource2['default']();
  1691. } else if (_videoJs2['default'].getTech('Flash')) {
  1692. return new _flashMediaSource2['default']();
  1693. }
  1694. throw new Error('Cannot use Flash or Html5 to create a MediaSource for this video');
  1695. };
  1696. exports.MediaSource = MediaSource;
  1697. MediaSource.open = open;
  1698. MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;
  1699. /**
  1700. * A wrapper around the native URL for our MSE object
  1701. * implementation, this object is exposed under videojs.URL
  1702. *
  1703. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
  1704. */
  1705. var URL = {
  1706. /**
  1707. * A wrapper around the native createObjectURL for our objects.
  1708. * This function maps a native or emulated mediaSource to a blob
  1709. * url so that it can be loaded into video.js
  1710. *
  1711. * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
  1712. * @param {MediaSource} object the object to create a blob url to
  1713. */
  1714. createObjectURL: function createObjectURL(object) {
  1715. var objectUrlPrefix = 'blob:vjs-media-source/';
  1716. var url = undefined;
  1717. // use the native MediaSource to generate an object URL
  1718. if (object instanceof _htmlMediaSource2['default']) {
  1719. url = _globalWindow2['default'].URL.createObjectURL(object.nativeMediaSource_);
  1720. object.url_ = url;
  1721. return url;
  1722. }
  1723. // if the object isn't an emulated MediaSource, delegate to the
  1724. // native implementation
  1725. if (!(object instanceof _flashMediaSource2['default'])) {
  1726. url = _globalWindow2['default'].URL.createObjectURL(object);
  1727. object.url_ = url;
  1728. return url;
  1729. }
  1730. // build a URL that can be used to map back to the emulated
  1731. // MediaSource
  1732. url = objectUrlPrefix + urlCount;
  1733. urlCount++;
  1734. // setup the mapping back to object
  1735. _videoJs2['default'].mediaSources[url] = object;
  1736. return url;
  1737. }
  1738. };
  1739. exports.URL = URL;
  1740. _videoJs2['default'].MediaSource = MediaSource;
  1741. _videoJs2['default'].URL = URL;
  1742. },{"./flash-media-source":5,"./html-media-source":8,"global/window":16,"video.js":135}],12:[function(require,module,exports){
  1743. /**
  1744. * @file virtual-source-buffer.js
  1745. */
  1746. 'use strict';
  1747. Object.defineProperty(exports, '__esModule', {
  1748. value: true
  1749. });
  1750. var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
  1751. var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
  1752. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  1753. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
  1754. function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  1755. var _videoJs = require('video.js');
  1756. var _videoJs2 = _interopRequireDefault(_videoJs);
  1757. var _createTextTracksIfNecessary = require('./create-text-tracks-if-necessary');
  1758. var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);
  1759. var _removeCuesFromTrack = require('./remove-cues-from-track');
  1760. var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);
  1761. var _addTextTrackData = require('./add-text-track-data');
  1762. var _webwackify = require('webwackify');
  1763. var _webwackify2 = _interopRequireDefault(_webwackify);
  1764. var _transmuxerWorker = require('./transmuxer-worker');
  1765. var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);
  1766. var _codecUtils = require('./codec-utils');
  1767. var resolveTransmuxWorker = function resolveTransmuxWorker() {
  1768. var result = undefined;
  1769. try {
  1770. result = require.resolve('./transmuxer-worker');
  1771. } catch (e) {
  1772. // no result
  1773. }
  1774. return result;
  1775. };
  1776. // We create a wrapper around the SourceBuffer so that we can manage the
  1777. // state of the `updating` property manually. We have to do this because
  1778. // Firefox changes `updating` to false long before triggering `updateend`
  1779. // events and that was causing strange problems in videojs-contrib-hls
  1780. var makeWrappedSourceBuffer = function makeWrappedSourceBuffer(mediaSource, mimeType) {
  1781. var sourceBuffer = mediaSource.addSourceBuffer(mimeType);
  1782. var wrapper = Object.create(null);
  1783. wrapper.updating = false;
  1784. wrapper.realBuffer_ = sourceBuffer;
  1785. var _loop = function (key) {
  1786. if (typeof sourceBuffer[key] === 'function') {
  1787. wrapper[key] = function () {
  1788. return sourceBuffer[key].apply(sourceBuffer, arguments);
  1789. };
  1790. } else if (typeof wrapper[key] === 'undefined') {
  1791. Object.defineProperty(wrapper, key, {
  1792. get: function get() {
  1793. return sourceBuffer[key];
  1794. },
  1795. set: function set(v) {
  1796. return sourceBuffer[key] = v;
  1797. }
  1798. });
  1799. }
  1800. };
  1801. for (var key in sourceBuffer) {
  1802. _loop(key);
  1803. }
  1804. return wrapper;
  1805. };
  1806. /**
  1807. * Returns a list of gops in the buffer that have a pts value of 3 seconds or more in
  1808. * front of current time.
  1809. *
  1810. * @param {Array} buffer
  1811. * The current buffer of gop information
  1812. * @param {Player} player
  1813. * The player instance
  1814. * @param {Double} mapping
  1815. * Offset to map display time to stream presentation time
  1816. * @return {Array}
  1817. * List of gops considered safe to append over
  1818. */
  1819. var gopsSafeToAlignWith = function gopsSafeToAlignWith(buffer, player, mapping) {
  1820. if (!player || !buffer.length) {
  1821. return [];
  1822. }
  1823. // pts value for current time + 3 seconds to give a bit more wiggle room
  1824. var currentTimePts = Math.ceil((player.currentTime() - mapping + 3) * 90000);
  1825. var i = undefined;
  1826. for (i = 0; i < buffer.length; i++) {
  1827. if (buffer[i].pts > currentTimePts) {
  1828. break;
  1829. }
  1830. }
  1831. return buffer.slice(i);
  1832. };
  1833. exports.gopsSafeToAlignWith = gopsSafeToAlignWith;
  1834. /**
  1835. * Appends gop information (timing and byteLength) received by the transmuxer for the
  1836. * gops appended in the last call to appendBuffer
  1837. *
  1838. * @param {Array} buffer
  1839. * The current buffer of gop information
  1840. * @param {Array} gops
  1841. * List of new gop information
  1842. * @param {boolean} replace
  1843. * If true, replace the buffer with the new gop information. If false, append the
  1844. * new gop information to the buffer in the right location of time.
  1845. * @return {Array}
  1846. * Updated list of gop information
  1847. */
  1848. var updateGopBuffer = function updateGopBuffer(buffer, gops, replace) {
  1849. if (!gops.length) {
  1850. return buffer;
  1851. }
  1852. if (replace) {
  1853. // If we are in safe append mode, then completely overwrite the gop buffer
  1854. // with the most recent appeneded data. This will make sure that when appending
  1855. // future segments, we only try to align with gops that are both ahead of current
  1856. // time and in the last segment appended.
  1857. return gops.slice();
  1858. }
  1859. var start = gops[0].pts;
  1860. var i = 0;
  1861. for (i; i < buffer.length; i++) {
  1862. if (buffer[i].pts >= start) {
  1863. break;
  1864. }
  1865. }
  1866. return buffer.slice(0, i).concat(gops);
  1867. };
  1868. exports.updateGopBuffer = updateGopBuffer;
  1869. /**
  1870. * Removes gop information in buffer that overlaps with provided start and end
  1871. *
  1872. * @param {Array} buffer
  1873. * The current buffer of gop information
  1874. * @param {Double} start
  1875. * position to start the remove at
  1876. * @param {Double} end
  1877. * position to end the remove at
  1878. * @param {Double} mapping
  1879. * Offset to map display time to stream presentation time
  1880. */
  1881. var removeGopBuffer = function removeGopBuffer(buffer, start, end, mapping) {
  1882. var startPts = Math.ceil((start - mapping) * 90000);
  1883. var endPts = Math.ceil((end - mapping) * 90000);
  1884. var updatedBuffer = buffer.slice();
  1885. var i = buffer.length;
  1886. while (i--) {
  1887. if (buffer[i].pts <= endPts) {
  1888. break;
  1889. }
  1890. }
  1891. if (i === -1) {
  1892. // no removal because end of remove range is before start of buffer
  1893. return updatedBuffer;
  1894. }
  1895. var j = i + 1;
  1896. while (j--) {
  1897. if (buffer[j].pts <= startPts) {
  1898. break;
  1899. }
  1900. }
  1901. // clamp remove range start to 0 index
  1902. j = Math.max(j, 0);
  1903. updatedBuffer.splice(j, i - j + 1);
  1904. return updatedBuffer;
  1905. };
  1906. exports.removeGopBuffer = removeGopBuffer;
  1907. /**
  1908. * VirtualSourceBuffers exist so that we can transmux non native formats
  1909. * into a native format, but keep the same api as a native source buffer.
  1910. * It creates a transmuxer, that works in its own thread (a web worker) and
  1911. * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
  1912. * then send all of that data to the naive sourcebuffer so that it is
  1913. * indestinguishable from a natively supported format.
  1914. *
  1915. * @param {HtmlMediaSource} mediaSource the parent mediaSource
  1916. * @param {Array} codecs array of codecs that we will be dealing with
  1917. * @class VirtualSourceBuffer
  1918. * @extends video.js.EventTarget
  1919. */
  1920. var VirtualSourceBuffer = (function (_videojs$EventTarget) {
  1921. _inherits(VirtualSourceBuffer, _videojs$EventTarget);
  1922. function VirtualSourceBuffer(mediaSource, codecs) {
  1923. var _this = this;
  1924. _classCallCheck(this, VirtualSourceBuffer);
  1925. _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _videoJs2['default'].EventTarget);
  1926. this.timestampOffset_ = 0;
  1927. this.pendingBuffers_ = [];
  1928. this.bufferUpdating_ = false;
  1929. this.mediaSource_ = mediaSource;
  1930. this.codecs_ = codecs;
  1931. this.audioCodec_ = null;
  1932. this.videoCodec_ = null;
  1933. this.audioDisabled_ = false;
  1934. this.appendAudioInitSegment_ = true;
  1935. this.gopBuffer_ = [];
  1936. this.timeMapping_ = 0;
  1937. this.safeAppend_ = _videoJs2['default'].browser.IE_VERSION >= 11;
  1938. var options = {
  1939. remux: false,
  1940. alignGopsAtEnd: this.safeAppend_
  1941. };
  1942. this.codecs_.forEach(function (codec) {
  1943. if ((0, _codecUtils.isAudioCodec)(codec)) {
  1944. _this.audioCodec_ = codec;
  1945. } else if ((0, _codecUtils.isVideoCodec)(codec)) {
  1946. _this.videoCodec_ = codec;
  1947. }
  1948. });
  1949. // append muxed segments to their respective native buffers as
  1950. // soon as they are available
  1951. this.transmuxer_ = (0, _webwackify2['default'])(_transmuxerWorker2['default'], resolveTransmuxWorker());
  1952. this.transmuxer_.postMessage({ action: 'init', options: options });
  1953. this.transmuxer_.onmessage = function (event) {
  1954. if (event.data.action === 'data') {
  1955. return _this.data_(event);
  1956. }
  1957. if (event.data.action === 'done') {
  1958. return _this.done_(event);
  1959. }
  1960. if (event.data.action === 'gopInfo') {
  1961. return _this.appendGopInfo_(event);
  1962. }
  1963. };
  1964. // this timestampOffset is a property with the side-effect of resetting
  1965. // baseMediaDecodeTime in the transmuxer on the setter
  1966. Object.defineProperty(this, 'timestampOffset', {
  1967. get: function get() {
  1968. return this.timestampOffset_;
  1969. },
  1970. set: function set(val) {
  1971. if (typeof val === 'number' && val >= 0) {
  1972. this.timestampOffset_ = val;
  1973. this.appendAudioInitSegment_ = true;
  1974. // reset gop buffer on timestampoffset as this signals a change in timeline
  1975. this.gopBuffer_.length = 0;
  1976. this.timeMapping_ = 0;
  1977. // We have to tell the transmuxer to set the baseMediaDecodeTime to
  1978. // the desired timestampOffset for the next segment
  1979. this.transmuxer_.postMessage({
  1980. action: 'setTimestampOffset',
  1981. timestampOffset: val
  1982. });
  1983. }
  1984. }
  1985. });
  1986. // setting the append window affects both source buffers
  1987. Object.defineProperty(this, 'appendWindowStart', {
  1988. get: function get() {
  1989. return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
  1990. },
  1991. set: function set(start) {
  1992. if (this.videoBuffer_) {
  1993. this.videoBuffer_.appendWindowStart = start;
  1994. }
  1995. if (this.audioBuffer_) {
  1996. this.audioBuffer_.appendWindowStart = start;
  1997. }
  1998. }
  1999. });
  2000. // this buffer is "updating" if either of its native buffers are
  2001. Object.defineProperty(this, 'updating', {
  2002. get: function get() {
  2003. return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
  2004. }
  2005. });
  2006. // the buffered property is the intersection of the buffered
  2007. // ranges of the native source buffers
  2008. Object.defineProperty(this, 'buffered', {
  2009. get: function get() {
  2010. var start = null;
  2011. var end = null;
  2012. var arity = 0;
  2013. var extents = [];
  2014. var ranges = [];
  2015. // neither buffer has been created yet
  2016. if (!this.videoBuffer_ && !this.audioBuffer_) {
  2017. return _videoJs2['default'].createTimeRange();
  2018. }
  2019. // only one buffer is configured
  2020. if (!this.videoBuffer_) {
  2021. return this.audioBuffer_.buffered;
  2022. }
  2023. if (!this.audioBuffer_) {
  2024. return this.videoBuffer_.buffered;
  2025. }
  2026. // both buffers are configured
  2027. if (this.audioDisabled_) {
  2028. return this.videoBuffer_.buffered;
  2029. }
  2030. // both buffers are empty
  2031. if (this.videoBuffer_.buffered.length === 0 && this.audioBuffer_.buffered.length === 0) {
  2032. return _videoJs2['default'].createTimeRange();
  2033. }
  2034. // Handle the case where we have both buffers and create an
  2035. // intersection of the two
  2036. var videoBuffered = this.videoBuffer_.buffered;
  2037. var audioBuffered = this.audioBuffer_.buffered;
  2038. var count = videoBuffered.length;
  2039. // A) Gather up all start and end times
  2040. while (count--) {
  2041. extents.push({ time: videoBuffered.start(count), type: 'start' });
  2042. extents.push({ time: videoBuffered.end(count), type: 'end' });
  2043. }
  2044. count = audioBuffered.length;
  2045. while (count--) {
  2046. extents.push({ time: audioBuffered.start(count), type: 'start' });
  2047. extents.push({ time: audioBuffered.end(count), type: 'end' });
  2048. }
  2049. // B) Sort them by time
  2050. extents.sort(function (a, b) {
  2051. return a.time - b.time;
  2052. });
  2053. // C) Go along one by one incrementing arity for start and decrementing
  2054. // arity for ends
  2055. for (count = 0; count < extents.length; count++) {
  2056. if (extents[count].type === 'start') {
  2057. arity++;
  2058. // D) If arity is ever incremented to 2 we are entering an
  2059. // overlapping range
  2060. if (arity === 2) {
  2061. start = extents[count].time;
  2062. }
  2063. } else if (extents[count].type === 'end') {
  2064. arity--;
  2065. // E) If arity is ever decremented to 1 we leaving an
  2066. // overlapping range
  2067. if (arity === 1) {
  2068. end = extents[count].time;
  2069. }
  2070. }
  2071. // F) Record overlapping ranges
  2072. if (start !== null && end !== null) {
  2073. ranges.push([start, end]);
  2074. start = null;
  2075. end = null;
  2076. }
  2077. }
  2078. return _videoJs2['default'].createTimeRanges(ranges);
  2079. }
  2080. });
  2081. }
  2082. /**
  2083. * When we get a data event from the transmuxer
  2084. * we call this function and handle the data that
  2085. * was sent to us
  2086. *
  2087. * @private
  2088. * @param {Event} event the data event from the transmuxer
  2089. */
  2090. _createClass(VirtualSourceBuffer, [{
  2091. key: 'data_',
  2092. value: function data_(event) {
  2093. var segment = event.data.segment;
  2094. // Cast ArrayBuffer to TypedArray
  2095. segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);
  2096. segment.initSegment = new Uint8Array(segment.initSegment.data, segment.initSegment.byteOffset, segment.initSegment.byteLength);
  2097. (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);
  2098. // Add the segments to the pendingBuffers array
  2099. this.pendingBuffers_.push(segment);
  2100. return;
  2101. }
  2102. /**
  2103. * When we get a done event from the transmuxer
  2104. * we call this function and we process all
  2105. * of the pending data that we have been saving in the
  2106. * data_ function
  2107. *
  2108. * @private
  2109. * @param {Event} event the done event from the transmuxer
  2110. */
  2111. }, {
  2112. key: 'done_',
  2113. value: function done_(event) {
  2114. // Don't process and append data if the mediaSource is closed
  2115. if (this.mediaSource_.readyState === 'closed') {
  2116. this.pendingBuffers_.length = 0;
  2117. return;
  2118. }
  2119. // All buffers should have been flushed from the muxer
  2120. // start processing anything we have received
  2121. this.processPendingSegments_();
  2122. return;
  2123. }
  2124. /**
  2125. * Create our internal native audio/video source buffers and add
  2126. * event handlers to them with the following conditions:
  2127. * 1. they do not already exist on the mediaSource
  2128. * 2. this VSB has a codec for them
  2129. *
  2130. * @private
  2131. */
  2132. }, {
  2133. key: 'createRealSourceBuffers_',
  2134. value: function createRealSourceBuffers_() {
  2135. var _this2 = this;
  2136. var types = ['audio', 'video'];
  2137. types.forEach(function (type) {
  2138. // Don't create a SourceBuffer of this type if we don't have a
  2139. // codec for it
  2140. if (!_this2[type + 'Codec_']) {
  2141. return;
  2142. }
  2143. // Do nothing if a SourceBuffer of this type already exists
  2144. if (_this2[type + 'Buffer_']) {
  2145. return;
  2146. }
  2147. var buffer = null;
  2148. // If the mediasource already has a SourceBuffer for the codec
  2149. // use that
  2150. if (_this2.mediaSource_[type + 'Buffer_']) {
  2151. buffer = _this2.mediaSource_[type + 'Buffer_'];
  2152. // In multiple audio track cases, the audio source buffer is disabled
  2153. // on the main VirtualSourceBuffer by the HTMLMediaSource much earlier
  2154. // than createRealSourceBuffers_ is called to create the second
  2155. // VirtualSourceBuffer because that happens as a side-effect of
  2156. // videojs-contrib-hls starting the audioSegmentLoader. As a result,
  2157. // the audioBuffer is essentially "ownerless" and no one will toggle
  2158. // the `updating` state back to false once the `updateend` event is received
  2159. //
  2160. // Setting `updating` to false manually will work around this
  2161. // situation and allow work to continue
  2162. buffer.updating = false;
  2163. } else {
  2164. var codecProperty = type + 'Codec_';
  2165. var mimeType = type + '/mp4;codecs="' + _this2[codecProperty] + '"';
  2166. buffer = makeWrappedSourceBuffer(_this2.mediaSource_.nativeMediaSource_, mimeType);
  2167. _this2.mediaSource_[type + 'Buffer_'] = buffer;
  2168. }
  2169. _this2[type + 'Buffer_'] = buffer;
  2170. // Wire up the events to the SourceBuffer
  2171. ['update', 'updatestart', 'updateend'].forEach(function (event) {
  2172. buffer.addEventListener(event, function () {
  2173. // if audio is disabled
  2174. if (type === 'audio' && _this2.audioDisabled_) {
  2175. return;
  2176. }
  2177. if (event === 'updateend') {
  2178. _this2[type + 'Buffer_'].updating = false;
  2179. }
  2180. var shouldTrigger = types.every(function (t) {
  2181. // skip checking audio's updating status if audio
  2182. // is not enabled
  2183. if (t === 'audio' && _this2.audioDisabled_) {
  2184. return true;
  2185. }
  2186. // if the other type if updating we don't trigger
  2187. if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
  2188. return false;
  2189. }
  2190. return true;
  2191. });
  2192. if (shouldTrigger) {
  2193. return _this2.trigger(event);
  2194. }
  2195. });
  2196. });
  2197. });
  2198. }
  2199. /**
  2200. * Emulate the native mediasource function, but our function will
  2201. * send all of the proposed segments to the transmuxer so that we
  2202. * can transmux them before we append them to our internal
  2203. * native source buffers in the correct format.
  2204. *
  2205. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
  2206. * @param {Uint8Array} segment the segment to append to the buffer
  2207. */
  2208. }, {
  2209. key: 'appendBuffer',
  2210. value: function appendBuffer(segment) {
  2211. // Start the internal "updating" state
  2212. this.bufferUpdating_ = true;
  2213. if (this.audioBuffer_ && this.audioBuffer_.buffered.length) {
  2214. var audioBuffered = this.audioBuffer_.buffered;
  2215. this.transmuxer_.postMessage({
  2216. action: 'setAudioAppendStart',
  2217. appendStart: audioBuffered.end(audioBuffered.length - 1)
  2218. });
  2219. }
  2220. if (this.videoBuffer_) {
  2221. this.transmuxer_.postMessage({
  2222. action: 'alignGopsWith',
  2223. gopsToAlignWith: gopsSafeToAlignWith(this.gopBuffer_, this.mediaSource_.player_, this.timeMapping_)
  2224. });
  2225. }
  2226. this.transmuxer_.postMessage({
  2227. action: 'push',
  2228. // Send the typed-array of data as an ArrayBuffer so that
  2229. // it can be sent as a "Transferable" and avoid the costly
  2230. // memory copy
  2231. data: segment.buffer,
  2232. // To recreate the original typed-array, we need information
  2233. // about what portion of the ArrayBuffer it was a view into
  2234. byteOffset: segment.byteOffset,
  2235. byteLength: segment.byteLength
  2236. }, [segment.buffer]);
  2237. this.transmuxer_.postMessage({ action: 'flush' });
  2238. }
  2239. /**
  2240. * Appends gop information (timing and byteLength) received by the transmuxer for the
  2241. * gops appended in the last call to appendBuffer
  2242. *
  2243. * @param {Event} event
  2244. * The gopInfo event from the transmuxer
  2245. * @param {Array} event.data.gopInfo
  2246. * List of gop info to append
  2247. */
  2248. }, {
  2249. key: 'appendGopInfo_',
  2250. value: function appendGopInfo_(event) {
  2251. this.gopBuffer_ = updateGopBuffer(this.gopBuffer_, event.data.gopInfo, this.safeAppend_);
  2252. }
  2253. /**
  2254. * Emulate the native mediasource function and remove parts
  2255. * of the buffer from any of our internal buffers that exist
  2256. *
  2257. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
  2258. * @param {Double} start position to start the remove at
  2259. * @param {Double} end position to end the remove at
  2260. */
  2261. }, {
  2262. key: 'remove',
  2263. value: function remove(start, end) {
  2264. if (this.videoBuffer_) {
  2265. this.videoBuffer_.updating = true;
  2266. this.videoBuffer_.remove(start, end);
  2267. this.gopBuffer_ = removeGopBuffer(this.gopBuffer_, start, end, this.timeMapping_);
  2268. }
  2269. if (!this.audioDisabled_ && this.audioBuffer_) {
  2270. this.audioBuffer_.updating = true;
  2271. this.audioBuffer_.remove(start, end);
  2272. }
  2273. // Remove Metadata Cues (id3)
  2274. (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
  2275. // Remove Any Captions
  2276. if (this.inbandTextTracks_) {
  2277. for (var track in this.inbandTextTracks_) {
  2278. (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTracks_[track]);
  2279. }
  2280. }
  2281. }
  2282. /**
  2283. * Process any segments that the muxer has output
  2284. * Concatenate segments together based on type and append them into
  2285. * their respective sourceBuffers
  2286. *
  2287. * @private
  2288. */
  2289. }, {
  2290. key: 'processPendingSegments_',
  2291. value: function processPendingSegments_() {
  2292. var sortedSegments = {
  2293. video: {
  2294. segments: [],
  2295. bytes: 0
  2296. },
  2297. audio: {
  2298. segments: [],
  2299. bytes: 0
  2300. },
  2301. captions: [],
  2302. metadata: []
  2303. };
  2304. // Sort segments into separate video/audio arrays and
  2305. // keep track of their total byte lengths
  2306. sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
  2307. var type = segment.type;
  2308. var data = segment.data;
  2309. var initSegment = segment.initSegment;
  2310. segmentObj[type].segments.push(data);
  2311. segmentObj[type].bytes += data.byteLength;
  2312. segmentObj[type].initSegment = initSegment;
  2313. // Gather any captions into a single array
  2314. if (segment.captions) {
  2315. segmentObj.captions = segmentObj.captions.concat(segment.captions);
  2316. }
  2317. if (segment.info) {
  2318. segmentObj[type].info = segment.info;
  2319. }
  2320. // Gather any metadata into a single array
  2321. if (segment.metadata) {
  2322. segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
  2323. }
  2324. return segmentObj;
  2325. }, sortedSegments);
  2326. // Create the real source buffers if they don't exist by now since we
  2327. // finally are sure what tracks are contained in the source
  2328. if (!this.videoBuffer_ && !this.audioBuffer_) {
  2329. // Remove any codecs that may have been specified by default but
  2330. // are no longer applicable now
  2331. if (sortedSegments.video.bytes === 0) {
  2332. this.videoCodec_ = null;
  2333. }
  2334. if (sortedSegments.audio.bytes === 0) {
  2335. this.audioCodec_ = null;
  2336. }
  2337. this.createRealSourceBuffers_();
  2338. }
  2339. if (sortedSegments.audio.info) {
  2340. this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
  2341. }
  2342. if (sortedSegments.video.info) {
  2343. this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
  2344. }
  2345. if (this.appendAudioInitSegment_) {
  2346. if (!this.audioDisabled_ && this.audioBuffer_) {
  2347. sortedSegments.audio.segments.unshift(sortedSegments.audio.initSegment);
  2348. sortedSegments.audio.bytes += sortedSegments.audio.initSegment.byteLength;
  2349. }
  2350. this.appendAudioInitSegment_ = false;
  2351. }
  2352. var triggerUpdateend = false;
  2353. // Merge multiple video and audio segments into one and append
  2354. if (this.videoBuffer_ && sortedSegments.video.bytes) {
  2355. sortedSegments.video.segments.unshift(sortedSegments.video.initSegment);
  2356. sortedSegments.video.bytes += sortedSegments.video.initSegment.byteLength;
  2357. this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
  2358. // TODO: are video tracks the only ones with text tracks?
  2359. (0, _addTextTrackData.addTextTrackData)(this, sortedSegments.captions, sortedSegments.metadata);
  2360. } else if (this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
  2361. // The transmuxer did not return any bytes of video, meaning it was all trimmed
  2362. // for gop alignment. Since we have a video buffer and audio is disabled, updateend
  2363. // will never be triggered by this source buffer, which will cause contrib-hls
  2364. // to be stuck forever waiting for updateend. If audio is not disabled, updateend
  2365. // will be triggered by the audio buffer, which will be sent upwards since the video
  2366. // buffer will not be in an updating state.
  2367. triggerUpdateend = true;
  2368. }
  2369. if (!this.audioDisabled_ && this.audioBuffer_) {
  2370. this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
  2371. }
  2372. this.pendingBuffers_.length = 0;
  2373. if (triggerUpdateend) {
  2374. this.trigger('updateend');
  2375. }
  2376. // We are no longer in the internal "updating" state
  2377. this.bufferUpdating_ = false;
  2378. }
  2379. /**
  2380. * Combine all segments into a single Uint8Array and then append them
  2381. * to the destination buffer
  2382. *
  2383. * @param {Object} segmentObj
  2384. * @param {SourceBuffer} destinationBuffer native source buffer to append data to
  2385. * @private
  2386. */
  2387. }, {
  2388. key: 'concatAndAppendSegments_',
  2389. value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
  2390. var offset = 0;
  2391. var tempBuffer = undefined;
  2392. if (segmentObj.bytes) {
  2393. tempBuffer = new Uint8Array(segmentObj.bytes);
  2394. // Combine the individual segments into one large typed-array
  2395. segmentObj.segments.forEach(function (segment) {
  2396. tempBuffer.set(segment, offset);
  2397. offset += segment.byteLength;
  2398. });
  2399. try {
  2400. destinationBuffer.updating = true;
  2401. destinationBuffer.appendBuffer(tempBuffer);
  2402. } catch (error) {
  2403. if (this.mediaSource_.player_) {
  2404. this.mediaSource_.player_.error({
  2405. code: -3,
  2406. type: 'APPEND_BUFFER_ERR',
  2407. message: error.message,
  2408. originalError: error
  2409. });
  2410. }
  2411. }
  2412. }
  2413. }
  2414. /**
  2415. * Emulate the native mediasource function. abort any soureBuffer
  2416. * actions and throw out any un-appended data.
  2417. *
  2418. * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
  2419. */
  2420. }, {
  2421. key: 'abort',
  2422. value: function abort() {
  2423. if (this.videoBuffer_) {
  2424. this.videoBuffer_.abort();
  2425. }
  2426. if (!this.audioDisabled_ && this.audioBuffer_) {
  2427. this.audioBuffer_.abort();
  2428. }
  2429. if (this.transmuxer_) {
  2430. this.transmuxer_.postMessage({ action: 'reset' });
  2431. }
  2432. this.pendingBuffers_.length = 0;
  2433. this.bufferUpdating_ = false;
  2434. }
  2435. }]);
  2436. return VirtualSourceBuffer;
  2437. })(_videoJs2['default'].EventTarget);
  2438. exports['default'] = VirtualSourceBuffer;
  2439. },{"./add-text-track-data":1,"./codec-utils":2,"./create-text-tracks-if-necessary":3,"./remove-cues-from-track":9,"./transmuxer-worker":10,"video.js":135,"webwackify":142}],13:[function(require,module,exports){
  2440. },{}],14:[function(require,module,exports){
  2441. var isFunction = require('is-function')
  2442. module.exports = forEach
  2443. var toString = Object.prototype.toString
  2444. var hasOwnProperty = Object.prototype.hasOwnProperty
  2445. function forEach(list, iterator, context) {
  2446. if (!isFunction(iterator)) {
  2447. throw new TypeError('iterator must be a function')
  2448. }
  2449. if (arguments.length < 3) {
  2450. context = this
  2451. }
  2452. if (toString.call(list) === '[object Array]')
  2453. forEachArray(list, iterator, context)
  2454. else if (typeof list === 'string')
  2455. forEachString(list, iterator, context)
  2456. else
  2457. forEachObject(list, iterator, context)
  2458. }
  2459. function forEachArray(array, iterator, context) {
  2460. for (var i = 0, len = array.length; i < len; i++) {
  2461. if (hasOwnProperty.call(array, i)) {
  2462. iterator.call(context, array[i], i, array)
  2463. }
  2464. }
  2465. }
  2466. function forEachString(string, iterator, context) {
  2467. for (var i = 0, len = string.length; i < len; i++) {
  2468. // no such thing as a sparse string.
  2469. iterator.call(context, string.charAt(i), i, string)
  2470. }
  2471. }
  2472. function forEachObject(object, iterator, context) {
  2473. for (var k in object) {
  2474. if (hasOwnProperty.call(object, k)) {
  2475. iterator.call(context, object[k], k, object)
  2476. }
  2477. }
  2478. }
  2479. },{"is-function":17}],15:[function(require,module,exports){
  2480. (function (global){
  2481. var topLevel = typeof global !== 'undefined' ? global :
  2482. typeof window !== 'undefined' ? window : {}
  2483. var minDoc = require('min-document');
  2484. var doccy;
  2485. if (typeof document !== 'undefined') {
  2486. doccy = document;
  2487. } else {
  2488. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  2489. if (!doccy) {
  2490. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  2491. }
  2492. }
  2493. module.exports = doccy;
  2494. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2495. },{"min-document":13}],16:[function(require,module,exports){
  2496. (function (global){
  2497. var win;
  2498. if (typeof window !== "undefined") {
  2499. win = window;
  2500. } else if (typeof global !== "undefined") {
  2501. win = global;
  2502. } else if (typeof self !== "undefined"){
  2503. win = self;
  2504. } else {
  2505. win = {};
  2506. }
  2507. module.exports = win;
  2508. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2509. },{}],17:[function(require,module,exports){
  2510. module.exports = isFunction
  2511. var toString = Object.prototype.toString
  2512. function isFunction (fn) {
  2513. var string = toString.call(fn)
  2514. return string === '[object Function]' ||
  2515. (typeof fn === 'function' && string !== '[object RegExp]') ||
  2516. (typeof window !== 'undefined' &&
  2517. // IE8 and below
  2518. (fn === window.setTimeout ||
  2519. fn === window.alert ||
  2520. fn === window.confirm ||
  2521. fn === window.prompt))
  2522. };
  2523. },{}],18:[function(require,module,exports){
  2524. /**
  2525. * mux.js
  2526. *
  2527. * Copyright (c) 2016 Brightcove
  2528. * All rights reserved.
  2529. *
  2530. * A stream-based aac to mp4 converter. This utility can be used to
  2531. * deliver mp4s to a SourceBuffer on platforms that support native
  2532. * Media Source Extensions.
  2533. */
  2534. 'use strict';
  2535. var Stream = require('../utils/stream.js');
  2536. // Constants
  2537. var AacStream;
  2538. /**
  2539. * Splits an incoming stream of binary data into ADTS and ID3 Frames.
  2540. */
  2541. AacStream = function() {
  2542. var
  2543. everything = new Uint8Array(),
  2544. timeStamp = 0;
  2545. AacStream.prototype.init.call(this);
  2546. this.setTimestamp = function(timestamp) {
  2547. timeStamp = timestamp;
  2548. };
  2549. this.parseId3TagSize = function(header, byteIndex) {
  2550. var
  2551. returnSize = (header[byteIndex + 6] << 21) |
  2552. (header[byteIndex + 7] << 14) |
  2553. (header[byteIndex + 8] << 7) |
  2554. (header[byteIndex + 9]),
  2555. flags = header[byteIndex + 5],
  2556. footerPresent = (flags & 16) >> 4;
  2557. if (footerPresent) {
  2558. return returnSize + 20;
  2559. }
  2560. return returnSize + 10;
  2561. };
  2562. this.parseAdtsSize = function(header, byteIndex) {
  2563. var
  2564. lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
  2565. middle = header[byteIndex + 4] << 3,
  2566. highTwo = header[byteIndex + 3] & 0x3 << 11;
  2567. return (highTwo | middle) | lowThree;
  2568. };
  2569. this.push = function(bytes) {
  2570. var
  2571. frameSize = 0,
  2572. byteIndex = 0,
  2573. bytesLeft,
  2574. chunk,
  2575. packet,
  2576. tempLength;
  2577. // If there are bytes remaining from the last segment, prepend them to the
  2578. // bytes that were pushed in
  2579. if (everything.length) {
  2580. tempLength = everything.length;
  2581. everything = new Uint8Array(bytes.byteLength + tempLength);
  2582. everything.set(everything.subarray(0, tempLength));
  2583. everything.set(bytes, tempLength);
  2584. } else {
  2585. everything = bytes;
  2586. }
  2587. while (everything.length - byteIndex >= 3) {
  2588. if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
  2589. (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
  2590. (everything[byteIndex + 2] === '3'.charCodeAt(0))) {
  2591. // Exit early because we don't have enough to parse
  2592. // the ID3 tag header
  2593. if (everything.length - byteIndex < 10) {
  2594. break;
  2595. }
  2596. // check framesize
  2597. frameSize = this.parseId3TagSize(everything, byteIndex);
  2598. // Exit early if we don't have enough in the buffer
  2599. // to emit a full packet
  2600. if (frameSize > everything.length) {
  2601. break;
  2602. }
  2603. chunk = {
  2604. type: 'timed-metadata',
  2605. data: everything.subarray(byteIndex, byteIndex + frameSize)
  2606. };
  2607. this.trigger('data', chunk);
  2608. byteIndex += frameSize;
  2609. continue;
  2610. } else if ((everything[byteIndex] & 0xff === 0xff) &&
  2611. ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {
  2612. // Exit early because we don't have enough to parse
  2613. // the ADTS frame header
  2614. if (everything.length - byteIndex < 7) {
  2615. break;
  2616. }
  2617. frameSize = this.parseAdtsSize(everything, byteIndex);
  2618. // Exit early if we don't have enough in the buffer
  2619. // to emit a full packet
  2620. if (frameSize > everything.length) {
  2621. break;
  2622. }
  2623. packet = {
  2624. type: 'audio',
  2625. data: everything.subarray(byteIndex, byteIndex + frameSize),
  2626. pts: timeStamp,
  2627. dts: timeStamp
  2628. };
  2629. this.trigger('data', packet);
  2630. byteIndex += frameSize;
  2631. continue;
  2632. }
  2633. byteIndex++;
  2634. }
  2635. bytesLeft = everything.length - byteIndex;
  2636. if (bytesLeft > 0) {
  2637. everything = everything.subarray(byteIndex);
  2638. } else {
  2639. everything = new Uint8Array();
  2640. }
  2641. };
  2642. };
  2643. AacStream.prototype = new Stream();
  2644. module.exports = AacStream;
  2645. },{"../utils/stream.js":38}],19:[function(require,module,exports){
  2646. 'use strict';
  2647. var Stream = require('../utils/stream.js');
  2648. var AdtsStream;
  2649. var
  2650. ADTS_SAMPLING_FREQUENCIES = [
  2651. 96000,
  2652. 88200,
  2653. 64000,
  2654. 48000,
  2655. 44100,
  2656. 32000,
  2657. 24000,
  2658. 22050,
  2659. 16000,
  2660. 12000,
  2661. 11025,
  2662. 8000,
  2663. 7350
  2664. ];
  2665. /*
  2666. * Accepts a ElementaryStream and emits data events with parsed
  2667. * AAC Audio Frames of the individual packets. Input audio in ADTS
  2668. * format is unpacked and re-emitted as AAC frames.
  2669. *
  2670. * @see http://wiki.multimedia.cx/index.php?title=ADTS
  2671. * @see http://wiki.multimedia.cx/?title=Understanding_AAC
  2672. */
  2673. AdtsStream = function() {
  2674. var buffer;
  2675. AdtsStream.prototype.init.call(this);
  2676. this.push = function(packet) {
  2677. var
  2678. i = 0,
  2679. frameNum = 0,
  2680. frameLength,
  2681. protectionSkipBytes,
  2682. frameEnd,
  2683. oldBuffer,
  2684. sampleCount,
  2685. adtsFrameDuration;
  2686. if (packet.type !== 'audio') {
  2687. // ignore non-audio data
  2688. return;
  2689. }
  2690. // Prepend any data in the buffer to the input data so that we can parse
  2691. // aac frames the cross a PES packet boundary
  2692. if (buffer) {
  2693. oldBuffer = buffer;
  2694. buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
  2695. buffer.set(oldBuffer);
  2696. buffer.set(packet.data, oldBuffer.byteLength);
  2697. } else {
  2698. buffer = packet.data;
  2699. }
  2700. // unpack any ADTS frames which have been fully received
  2701. // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
  2702. while (i + 5 < buffer.length) {
  2703. // Loook for the start of an ADTS header..
  2704. if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
  2705. // If a valid header was not found, jump one forward and attempt to
  2706. // find a valid ADTS header starting at the next byte
  2707. i++;
  2708. continue;
  2709. }
  2710. // The protection skip bit tells us if we have 2 bytes of CRC data at the
  2711. // end of the ADTS header
  2712. protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;
  2713. // Frame length is a 13 bit integer starting 16 bits from the
  2714. // end of the sync sequence
  2715. frameLength = ((buffer[i + 3] & 0x03) << 11) |
  2716. (buffer[i + 4] << 3) |
  2717. ((buffer[i + 5] & 0xe0) >> 5);
  2718. sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
  2719. adtsFrameDuration = (sampleCount * 90000) /
  2720. ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];
  2721. frameEnd = i + frameLength;
  2722. // If we don't have enough data to actually finish this ADTS frame, return
  2723. // and wait for more data
  2724. if (buffer.byteLength < frameEnd) {
  2725. return;
  2726. }
  2727. // Otherwise, deliver the complete AAC frame
  2728. this.trigger('data', {
  2729. pts: packet.pts + (frameNum * adtsFrameDuration),
  2730. dts: packet.dts + (frameNum * adtsFrameDuration),
  2731. sampleCount: sampleCount,
  2732. audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
  2733. channelcount: ((buffer[i + 2] & 1) << 2) |
  2734. ((buffer[i + 3] & 0xc0) >>> 6),
  2735. samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
  2736. samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
  2737. // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
  2738. samplesize: 16,
  2739. data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
  2740. });
  2741. // If the buffer is empty, clear it and return
  2742. if (buffer.byteLength === frameEnd) {
  2743. buffer = undefined;
  2744. return;
  2745. }
  2746. frameNum++;
  2747. // Remove the finished frame from the buffer and start the process again
  2748. buffer = buffer.subarray(frameEnd);
  2749. }
  2750. };
  2751. this.flush = function() {
  2752. this.trigger('done');
  2753. };
  2754. };
  2755. AdtsStream.prototype = new Stream();
  2756. module.exports = AdtsStream;
  2757. },{"../utils/stream.js":38}],20:[function(require,module,exports){
  2758. 'use strict';
  2759. var Stream = require('../utils/stream.js');
  2760. var ExpGolomb = require('../utils/exp-golomb.js');
  2761. var H264Stream, NalByteStream;
  2762. var PROFILES_WITH_OPTIONAL_SPS_DATA;
  2763. /**
  2764. * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
  2765. */
  2766. NalByteStream = function() {
  2767. var
  2768. syncPoint = 0,
  2769. i,
  2770. buffer;
  2771. NalByteStream.prototype.init.call(this);
  2772. this.push = function(data) {
  2773. var swapBuffer;
  2774. if (!buffer) {
  2775. buffer = data.data;
  2776. } else {
  2777. swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
  2778. swapBuffer.set(buffer);
  2779. swapBuffer.set(data.data, buffer.byteLength);
  2780. buffer = swapBuffer;
  2781. }
  2782. // Rec. ITU-T H.264, Annex B
  2783. // scan for NAL unit boundaries
  2784. // a match looks like this:
  2785. // 0 0 1 .. NAL .. 0 0 1
  2786. // ^ sync point ^ i
  2787. // or this:
  2788. // 0 0 1 .. NAL .. 0 0 0
  2789. // ^ sync point ^ i
  2790. // advance the sync point to a NAL start, if necessary
  2791. for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
  2792. if (buffer[syncPoint + 2] === 1) {
  2793. // the sync point is properly aligned
  2794. i = syncPoint + 5;
  2795. break;
  2796. }
  2797. }
  2798. while (i < buffer.byteLength) {
  2799. // look at the current byte to determine if we've hit the end of
  2800. // a NAL unit boundary
  2801. switch (buffer[i]) {
  2802. case 0:
  2803. // skip past non-sync sequences
  2804. if (buffer[i - 1] !== 0) {
  2805. i += 2;
  2806. break;
  2807. } else if (buffer[i - 2] !== 0) {
  2808. i++;
  2809. break;
  2810. }
  2811. // deliver the NAL unit if it isn't empty
  2812. if (syncPoint + 3 !== i - 2) {
  2813. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  2814. }
  2815. // drop trailing zeroes
  2816. do {
  2817. i++;
  2818. } while (buffer[i] !== 1 && i < buffer.length);
  2819. syncPoint = i - 2;
  2820. i += 3;
  2821. break;
  2822. case 1:
  2823. // skip past non-sync sequences
  2824. if (buffer[i - 1] !== 0 ||
  2825. buffer[i - 2] !== 0) {
  2826. i += 3;
  2827. break;
  2828. }
  2829. // deliver the NAL unit
  2830. this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
  2831. syncPoint = i - 2;
  2832. i += 3;
  2833. break;
  2834. default:
  2835. // the current byte isn't a one or zero, so it cannot be part
  2836. // of a sync sequence
  2837. i += 3;
  2838. break;
  2839. }
  2840. }
  2841. // filter out the NAL units that were delivered
  2842. buffer = buffer.subarray(syncPoint);
  2843. i -= syncPoint;
  2844. syncPoint = 0;
  2845. };
  2846. this.flush = function() {
  2847. // deliver the last buffered NAL unit
  2848. if (buffer && buffer.byteLength > 3) {
  2849. this.trigger('data', buffer.subarray(syncPoint + 3));
  2850. }
  2851. // reset the stream state
  2852. buffer = null;
  2853. syncPoint = 0;
  2854. this.trigger('done');
  2855. };
  2856. };
  2857. NalByteStream.prototype = new Stream();
  2858. // values of profile_idc that indicate additional fields are included in the SPS
  2859. // see Recommendation ITU-T H.264 (4/2013),
  2860. // 7.3.2.1.1 Sequence parameter set data syntax
  2861. PROFILES_WITH_OPTIONAL_SPS_DATA = {
  2862. 100: true,
  2863. 110: true,
  2864. 122: true,
  2865. 244: true,
  2866. 44: true,
  2867. 83: true,
  2868. 86: true,
  2869. 118: true,
  2870. 128: true,
  2871. 138: true,
  2872. 139: true,
  2873. 134: true
  2874. };
  2875. /**
  2876. * Accepts input from a ElementaryStream and produces H.264 NAL unit data
  2877. * events.
  2878. */
  2879. H264Stream = function() {
  2880. var
  2881. nalByteStream = new NalByteStream(),
  2882. self,
  2883. trackId,
  2884. currentPts,
  2885. currentDts,
  2886. discardEmulationPreventionBytes,
  2887. readSequenceParameterSet,
  2888. skipScalingList;
  2889. H264Stream.prototype.init.call(this);
  2890. self = this;
  2891. this.push = function(packet) {
  2892. if (packet.type !== 'video') {
  2893. return;
  2894. }
  2895. trackId = packet.trackId;
  2896. currentPts = packet.pts;
  2897. currentDts = packet.dts;
  2898. nalByteStream.push(packet);
  2899. };
  2900. nalByteStream.on('data', function(data) {
  2901. var
  2902. event = {
  2903. trackId: trackId,
  2904. pts: currentPts,
  2905. dts: currentDts,
  2906. data: data
  2907. };
  2908. switch (data[0] & 0x1f) {
  2909. case 0x05:
  2910. event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
  2911. break;
  2912. case 0x06:
  2913. event.nalUnitType = 'sei_rbsp';
  2914. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  2915. break;
  2916. case 0x07:
  2917. event.nalUnitType = 'seq_parameter_set_rbsp';
  2918. event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
  2919. event.config = readSequenceParameterSet(event.escapedRBSP);
  2920. break;
  2921. case 0x08:
  2922. event.nalUnitType = 'pic_parameter_set_rbsp';
  2923. break;
  2924. case 0x09:
  2925. event.nalUnitType = 'access_unit_delimiter_rbsp';
  2926. break;
  2927. default:
  2928. break;
  2929. }
  2930. self.trigger('data', event);
  2931. });
  2932. nalByteStream.on('done', function() {
  2933. self.trigger('done');
  2934. });
  2935. this.flush = function() {
  2936. nalByteStream.flush();
  2937. };
  2938. /**
  2939. * Advance the ExpGolomb decoder past a scaling list. The scaling
  2940. * list is optionally transmitted as part of a sequence parameter
  2941. * set and is not relevant to transmuxing.
  2942. * @param count {number} the number of entries in this scaling list
  2943. * @param expGolombDecoder {object} an ExpGolomb pointed to the
  2944. * start of a scaling list
  2945. * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
  2946. */
  2947. skipScalingList = function(count, expGolombDecoder) {
  2948. var
  2949. lastScale = 8,
  2950. nextScale = 8,
  2951. j,
  2952. deltaScale;
  2953. for (j = 0; j < count; j++) {
  2954. if (nextScale !== 0) {
  2955. deltaScale = expGolombDecoder.readExpGolomb();
  2956. nextScale = (lastScale + deltaScale + 256) % 256;
  2957. }
  2958. lastScale = (nextScale === 0) ? lastScale : nextScale;
  2959. }
  2960. };
  2961. /**
  2962. * Expunge any "Emulation Prevention" bytes from a "Raw Byte
  2963. * Sequence Payload"
  2964. * @param data {Uint8Array} the bytes of a RBSP from a NAL
  2965. * unit
  2966. * @return {Uint8Array} the RBSP without any Emulation
  2967. * Prevention Bytes
  2968. */
  2969. discardEmulationPreventionBytes = function(data) {
  2970. var
  2971. length = data.byteLength,
  2972. emulationPreventionBytesPositions = [],
  2973. i = 1,
  2974. newLength, newData;
  2975. // Find all `Emulation Prevention Bytes`
  2976. while (i < length - 2) {
  2977. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  2978. emulationPreventionBytesPositions.push(i + 2);
  2979. i += 2;
  2980. } else {
  2981. i++;
  2982. }
  2983. }
  2984. // If no Emulation Prevention Bytes were found just return the original
  2985. // array
  2986. if (emulationPreventionBytesPositions.length === 0) {
  2987. return data;
  2988. }
  2989. // Create a new array to hold the NAL unit data
  2990. newLength = length - emulationPreventionBytesPositions.length;
  2991. newData = new Uint8Array(newLength);
  2992. var sourceIndex = 0;
  2993. for (i = 0; i < newLength; sourceIndex++, i++) {
  2994. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  2995. // Skip this byte
  2996. sourceIndex++;
  2997. // Remove this position index
  2998. emulationPreventionBytesPositions.shift();
  2999. }
  3000. newData[i] = data[sourceIndex];
  3001. }
  3002. return newData;
  3003. };
  3004. /**
  3005. * Read a sequence parameter set and return some interesting video
  3006. * properties. A sequence parameter set is the H264 metadata that
  3007. * describes the properties of upcoming video frames.
  3008. * @param data {Uint8Array} the bytes of a sequence parameter set
  3009. * @return {object} an object with configuration parsed from the
  3010. * sequence parameter set, including the dimensions of the
  3011. * associated video frames.
  3012. */
  3013. readSequenceParameterSet = function(data) {
  3014. var
  3015. frameCropLeftOffset = 0,
  3016. frameCropRightOffset = 0,
  3017. frameCropTopOffset = 0,
  3018. frameCropBottomOffset = 0,
  3019. sarScale = 1,
  3020. expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
  3021. chromaFormatIdc, picOrderCntType,
  3022. numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
  3023. picHeightInMapUnitsMinus1,
  3024. frameMbsOnlyFlag,
  3025. scalingListCount,
  3026. sarRatio,
  3027. aspectRatioIdc,
  3028. i;
  3029. expGolombDecoder = new ExpGolomb(data);
  3030. profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
  3031. profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
  3032. levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
  3033. expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id
  3034. // some profiles have more optional data we don't need
  3035. if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
  3036. chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
  3037. if (chromaFormatIdc === 3) {
  3038. expGolombDecoder.skipBits(1); // separate_colour_plane_flag
  3039. }
  3040. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
  3041. expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
  3042. expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
  3043. if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
  3044. scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
  3045. for (i = 0; i < scalingListCount; i++) {
  3046. if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
  3047. if (i < 6) {
  3048. skipScalingList(16, expGolombDecoder);
  3049. } else {
  3050. skipScalingList(64, expGolombDecoder);
  3051. }
  3052. }
  3053. }
  3054. }
  3055. }
  3056. expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
  3057. picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();
  3058. if (picOrderCntType === 0) {
  3059. expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
  3060. } else if (picOrderCntType === 1) {
  3061. expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
  3062. expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
  3063. expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
  3064. numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
  3065. for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
  3066. expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
  3067. }
  3068. }
  3069. expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
  3070. expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag
  3071. picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  3072. picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
  3073. frameMbsOnlyFlag = expGolombDecoder.readBits(1);
  3074. if (frameMbsOnlyFlag === 0) {
  3075. expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
  3076. }
  3077. expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
  3078. if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
  3079. frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
  3080. frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
  3081. frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
  3082. frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
  3083. }
  3084. if (expGolombDecoder.readBoolean()) {
  3085. // vui_parameters_present_flag
  3086. if (expGolombDecoder.readBoolean()) {
  3087. // aspect_ratio_info_present_flag
  3088. aspectRatioIdc = expGolombDecoder.readUnsignedByte();
  3089. switch (aspectRatioIdc) {
  3090. case 1: sarRatio = [1, 1]; break;
  3091. case 2: sarRatio = [12, 11]; break;
  3092. case 3: sarRatio = [10, 11]; break;
  3093. case 4: sarRatio = [16, 11]; break;
  3094. case 5: sarRatio = [40, 33]; break;
  3095. case 6: sarRatio = [24, 11]; break;
  3096. case 7: sarRatio = [20, 11]; break;
  3097. case 8: sarRatio = [32, 11]; break;
  3098. case 9: sarRatio = [80, 33]; break;
  3099. case 10: sarRatio = [18, 11]; break;
  3100. case 11: sarRatio = [15, 11]; break;
  3101. case 12: sarRatio = [64, 33]; break;
  3102. case 13: sarRatio = [160, 99]; break;
  3103. case 14: sarRatio = [4, 3]; break;
  3104. case 15: sarRatio = [3, 2]; break;
  3105. case 16: sarRatio = [2, 1]; break;
  3106. case 255: {
  3107. sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
  3108. expGolombDecoder.readUnsignedByte(),
  3109. expGolombDecoder.readUnsignedByte() << 8 |
  3110. expGolombDecoder.readUnsignedByte() ];
  3111. break;
  3112. }
  3113. }
  3114. if (sarRatio) {
  3115. sarScale = sarRatio[0] / sarRatio[1];
  3116. }
  3117. }
  3118. }
  3119. return {
  3120. profileIdc: profileIdc,
  3121. levelIdc: levelIdc,
  3122. profileCompatibility: profileCompatibility,
  3123. width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
  3124. height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
  3125. };
  3126. };
  3127. };
  3128. H264Stream.prototype = new Stream();
  3129. module.exports = {
  3130. H264Stream: H264Stream,
  3131. NalByteStream: NalByteStream
  3132. };
  3133. },{"../utils/exp-golomb.js":37,"../utils/stream.js":38}],21:[function(require,module,exports){
  3134. var highPrefix = [33, 16, 5, 32, 164, 27];
  3135. var lowPrefix = [33, 65, 108, 84, 1, 2, 4, 8, 168, 2, 4, 8, 17, 191, 252];
  3136. var zeroFill = function(count) {
  3137. var a = [];
  3138. while (count--) {
  3139. a.push(0);
  3140. }
  3141. return a;
  3142. };
  3143. var makeTable = function(metaTable) {
  3144. return Object.keys(metaTable).reduce(function(obj, key) {
  3145. obj[key] = new Uint8Array(metaTable[key].reduce(function(arr, part) {
  3146. return arr.concat(part);
  3147. }, []));
  3148. return obj;
  3149. }, {});
  3150. };
  3151. // Frames-of-silence to use for filling in missing AAC frames
  3152. var coneOfSilence = {
  3153. 96000: [highPrefix, [227, 64], zeroFill(154), [56]],
  3154. 88200: [highPrefix, [231], zeroFill(170), [56]],
  3155. 64000: [highPrefix, [248, 192], zeroFill(240), [56]],
  3156. 48000: [highPrefix, [255, 192], zeroFill(268), [55, 148, 128], zeroFill(54), [112]],
  3157. 44100: [highPrefix, [255, 192], zeroFill(268), [55, 163, 128], zeroFill(84), [112]],
  3158. 32000: [highPrefix, [255, 192], zeroFill(268), [55, 234], zeroFill(226), [112]],
  3159. 24000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 112], zeroFill(126), [224]],
  3160. 16000: [highPrefix, [255, 192], zeroFill(268), [55, 255, 128], zeroFill(268), [111, 255], zeroFill(269), [223, 108], zeroFill(195), [1, 192]],
  3161. 12000: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 253, 128], zeroFill(259), [56]],
  3162. 11025: [lowPrefix, zeroFill(268), [3, 127, 248], zeroFill(268), [6, 255, 240], zeroFill(268), [13, 255, 224], zeroFill(268), [27, 255, 192], zeroFill(268), [55, 175, 128], zeroFill(108), [112]],
  3163. 8000: [lowPrefix, zeroFill(268), [3, 121, 16], zeroFill(47), [7]]
  3164. };
  3165. module.exports = makeTable(coneOfSilence);
  3166. },{}],22:[function(require,module,exports){
  3167. 'use strict';
  3168. var Stream = require('../utils/stream.js');
  3169. /**
  3170. * The final stage of the transmuxer that emits the flv tags
  3171. * for audio, video, and metadata. Also tranlates in time and
  3172. * outputs caption data and id3 cues.
  3173. */
  3174. var CoalesceStream = function(options) {
  3175. // Number of Tracks per output segment
  3176. // If greater than 1, we combine multiple
  3177. // tracks into a single segment
  3178. this.numberOfTracks = 0;
  3179. this.metadataStream = options.metadataStream;
  3180. this.videoTags = [];
  3181. this.audioTags = [];
  3182. this.videoTrack = null;
  3183. this.audioTrack = null;
  3184. this.pendingCaptions = [];
  3185. this.pendingMetadata = [];
  3186. this.pendingTracks = 0;
  3187. this.processedTracks = 0;
  3188. CoalesceStream.prototype.init.call(this);
  3189. // Take output from multiple
  3190. this.push = function(output) {
  3191. // buffer incoming captions until the associated video segment
  3192. // finishes
  3193. if (output.text) {
  3194. return this.pendingCaptions.push(output);
  3195. }
  3196. // buffer incoming id3 tags until the final flush
  3197. if (output.frames) {
  3198. return this.pendingMetadata.push(output);
  3199. }
  3200. if (output.track.type === 'video') {
  3201. this.videoTrack = output.track;
  3202. this.videoTags = output.tags;
  3203. this.pendingTracks++;
  3204. }
  3205. if (output.track.type === 'audio') {
  3206. this.audioTrack = output.track;
  3207. this.audioTags = output.tags;
  3208. this.pendingTracks++;
  3209. }
  3210. };
  3211. };
  3212. CoalesceStream.prototype = new Stream();
  3213. CoalesceStream.prototype.flush = function(flushSource) {
  3214. var
  3215. id3,
  3216. caption,
  3217. i,
  3218. timelineStartPts,
  3219. event = {
  3220. tags: {},
  3221. captions: [],
  3222. captionStreams: {},
  3223. metadata: []
  3224. };
  3225. if (this.pendingTracks < this.numberOfTracks) {
  3226. if (flushSource !== 'VideoSegmentStream' &&
  3227. flushSource !== 'AudioSegmentStream') {
  3228. // Return because we haven't received a flush from a data-generating
  3229. // portion of the segment (meaning that we have only recieved meta-data
  3230. // or captions.)
  3231. return;
  3232. } else if (this.pendingTracks === 0) {
  3233. // In the case where we receive a flush without any data having been
  3234. // received we consider it an emitted track for the purposes of coalescing
  3235. // `done` events.
  3236. // We do this for the case where there is an audio and video track in the
  3237. // segment but no audio data. (seen in several playlists with alternate
  3238. // audio tracks and no audio present in the main TS segments.)
  3239. this.processedTracks++;
  3240. if (this.processedTracks < this.numberOfTracks) {
  3241. return;
  3242. }
  3243. }
  3244. }
  3245. this.processedTracks += this.pendingTracks;
  3246. this.pendingTracks = 0;
  3247. if (this.processedTracks < this.numberOfTracks) {
  3248. return;
  3249. }
  3250. if (this.videoTrack) {
  3251. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  3252. } else if (this.audioTrack) {
  3253. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  3254. }
  3255. event.tags.videoTags = this.videoTags;
  3256. event.tags.audioTags = this.audioTags;
  3257. // Translate caption PTS times into second offsets into the
  3258. // video timeline for the segment, and add track info
  3259. for (i = 0; i < this.pendingCaptions.length; i++) {
  3260. caption = this.pendingCaptions[i];
  3261. caption.startTime = caption.startPts - timelineStartPts;
  3262. caption.startTime /= 90e3;
  3263. caption.endTime = caption.endPts - timelineStartPts;
  3264. caption.endTime /= 90e3;
  3265. event.captionStreams[caption.stream] = true;
  3266. event.captions.push(caption);
  3267. }
  3268. // Translate ID3 frame PTS times into second offsets into the
  3269. // video timeline for the segment
  3270. for (i = 0; i < this.pendingMetadata.length; i++) {
  3271. id3 = this.pendingMetadata[i];
  3272. id3.cueTime = id3.pts - timelineStartPts;
  3273. id3.cueTime /= 90e3;
  3274. event.metadata.push(id3);
  3275. }
  3276. // We add this to every single emitted segment even though we only need
  3277. // it for the first
  3278. event.metadata.dispatchType = this.metadataStream.dispatchType;
  3279. // Reset stream state
  3280. this.videoTrack = null;
  3281. this.audioTrack = null;
  3282. this.videoTags = [];
  3283. this.audioTags = [];
  3284. this.pendingCaptions.length = 0;
  3285. this.pendingMetadata.length = 0;
  3286. this.pendingTracks = 0;
  3287. this.processedTracks = 0;
  3288. // Emit the final segment
  3289. this.trigger('data', event);
  3290. this.trigger('done');
  3291. };
  3292. module.exports = CoalesceStream;
  3293. },{"../utils/stream.js":38}],23:[function(require,module,exports){
  3294. 'use strict';
  3295. var FlvTag = require('./flv-tag.js');
  3296. // For information on the FLV format, see
  3297. // http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
  3298. // Technically, this function returns the header and a metadata FLV tag
  3299. // if duration is greater than zero
  3300. // duration in seconds
  3301. // @return {object} the bytes of the FLV header as a Uint8Array
  3302. var getFlvHeader = function(duration, audio, video) { // :ByteArray {
  3303. var
  3304. headBytes = new Uint8Array(3 + 1 + 1 + 4),
  3305. head = new DataView(headBytes.buffer),
  3306. metadata,
  3307. result,
  3308. metadataLength;
  3309. // default arguments
  3310. duration = duration || 0;
  3311. audio = audio === undefined ? true : audio;
  3312. video = video === undefined ? true : video;
  3313. // signature
  3314. head.setUint8(0, 0x46); // 'F'
  3315. head.setUint8(1, 0x4c); // 'L'
  3316. head.setUint8(2, 0x56); // 'V'
  3317. // version
  3318. head.setUint8(3, 0x01);
  3319. // flags
  3320. head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));
  3321. // data offset, should be 9 for FLV v1
  3322. head.setUint32(5, headBytes.byteLength);
  3323. // init the first FLV tag
  3324. if (duration <= 0) {
  3325. // no duration available so just write the first field of the first
  3326. // FLV tag
  3327. result = new Uint8Array(headBytes.byteLength + 4);
  3328. result.set(headBytes);
  3329. result.set([0, 0, 0, 0], headBytes.byteLength);
  3330. return result;
  3331. }
  3332. // write out the duration metadata tag
  3333. metadata = new FlvTag(FlvTag.METADATA_TAG);
  3334. metadata.pts = metadata.dts = 0;
  3335. metadata.writeMetaDataDouble('duration', duration);
  3336. metadataLength = metadata.finalize().length;
  3337. result = new Uint8Array(headBytes.byteLength + metadataLength);
  3338. result.set(headBytes);
  3339. result.set(head.byteLength, metadataLength);
  3340. return result;
  3341. };
  3342. module.exports = getFlvHeader;
  3343. },{"./flv-tag.js":24}],24:[function(require,module,exports){
  3344. /**
  3345. * An object that stores the bytes of an FLV tag and methods for
  3346. * querying and manipulating that data.
  3347. * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
  3348. */
  3349. 'use strict';
  3350. var FlvTag;
  3351. // (type:uint, extraData:Boolean = false) extends ByteArray
  3352. FlvTag = function(type, extraData) {
  3353. var
  3354. // Counter if this is a metadata tag, nal start marker if this is a video
  3355. // tag. unused if this is an audio tag
  3356. adHoc = 0, // :uint
  3357. // The default size is 16kb but this is not enough to hold iframe
  3358. // data and the resizing algorithm costs a bit so we create a larger
  3359. // starting buffer for video tags
  3360. bufferStartSize = 16384,
  3361. // checks whether the FLV tag has enough capacity to accept the proposed
  3362. // write and re-allocates the internal buffers if necessary
  3363. prepareWrite = function(flv, count) {
  3364. var
  3365. bytes,
  3366. minLength = flv.position + count;
  3367. if (minLength < flv.bytes.byteLength) {
  3368. // there's enough capacity so do nothing
  3369. return;
  3370. }
  3371. // allocate a new buffer and copy over the data that will not be modified
  3372. bytes = new Uint8Array(minLength * 2);
  3373. bytes.set(flv.bytes.subarray(0, flv.position), 0);
  3374. flv.bytes = bytes;
  3375. flv.view = new DataView(flv.bytes.buffer);
  3376. },
  3377. // commonly used metadata properties
  3378. widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
  3379. heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
  3380. videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
  3381. i;
  3382. if (!FlvTag.widthBytes) {
  3383. // calculating the bytes of common metadata names ahead of time makes the
  3384. // corresponding writes faster because we don't have to loop over the
  3385. // characters
  3386. // re-test with test/perf.html if you're planning on changing this
  3387. for (i = 0; i < 'width'.length; i++) {
  3388. widthBytes[i] = 'width'.charCodeAt(i);
  3389. }
  3390. for (i = 0; i < 'height'.length; i++) {
  3391. heightBytes[i] = 'height'.charCodeAt(i);
  3392. }
  3393. for (i = 0; i < 'videocodecid'.length; i++) {
  3394. videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
  3395. }
  3396. FlvTag.widthBytes = widthBytes;
  3397. FlvTag.heightBytes = heightBytes;
  3398. FlvTag.videocodecidBytes = videocodecidBytes;
  3399. }
  3400. this.keyFrame = false; // :Boolean
  3401. switch (type) {
  3402. case FlvTag.VIDEO_TAG:
  3403. this.length = 16;
  3404. // Start the buffer at 256k
  3405. bufferStartSize *= 6;
  3406. break;
  3407. case FlvTag.AUDIO_TAG:
  3408. this.length = 13;
  3409. this.keyFrame = true;
  3410. break;
  3411. case FlvTag.METADATA_TAG:
  3412. this.length = 29;
  3413. this.keyFrame = true;
  3414. break;
  3415. default:
  3416. throw new Error('Unknown FLV tag type');
  3417. }
  3418. this.bytes = new Uint8Array(bufferStartSize);
  3419. this.view = new DataView(this.bytes.buffer);
  3420. this.bytes[0] = type;
  3421. this.position = this.length;
  3422. this.keyFrame = extraData; // Defaults to false
  3423. // presentation timestamp
  3424. this.pts = 0;
  3425. // decoder timestamp
  3426. this.dts = 0;
  3427. // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
  3428. this.writeBytes = function(bytes, offset, length) {
  3429. var
  3430. start = offset || 0,
  3431. end;
  3432. length = length || bytes.byteLength;
  3433. end = start + length;
  3434. prepareWrite(this, length);
  3435. this.bytes.set(bytes.subarray(start, end), this.position);
  3436. this.position += length;
  3437. this.length = Math.max(this.length, this.position);
  3438. };
  3439. // ByteArray#writeByte(value:int):void
  3440. this.writeByte = function(byte) {
  3441. prepareWrite(this, 1);
  3442. this.bytes[this.position] = byte;
  3443. this.position++;
  3444. this.length = Math.max(this.length, this.position);
  3445. };
  3446. // ByteArray#writeShort(value:int):void
  3447. this.writeShort = function(short) {
  3448. prepareWrite(this, 2);
  3449. this.view.setUint16(this.position, short);
  3450. this.position += 2;
  3451. this.length = Math.max(this.length, this.position);
  3452. };
  3453. // Negative index into array
  3454. // (pos:uint):int
  3455. this.negIndex = function(pos) {
  3456. return this.bytes[this.length - pos];
  3457. };
  3458. // The functions below ONLY work when this[0] == VIDEO_TAG.
  3459. // We are not going to check for that because we dont want the overhead
  3460. // (nal:ByteArray = null):int
  3461. this.nalUnitSize = function() {
  3462. if (adHoc === 0) {
  3463. return 0;
  3464. }
  3465. return this.length - (adHoc + 4);
  3466. };
  3467. this.startNalUnit = function() {
  3468. // remember position and add 4 bytes
  3469. if (adHoc > 0) {
  3470. throw new Error('Attempted to create new NAL wihout closing the old one');
  3471. }
  3472. // reserve 4 bytes for nal unit size
  3473. adHoc = this.length;
  3474. this.length += 4;
  3475. this.position = this.length;
  3476. };
  3477. // (nal:ByteArray = null):void
  3478. this.endNalUnit = function(nalContainer) {
  3479. var
  3480. nalStart, // :uint
  3481. nalLength; // :uint
  3482. // Rewind to the marker and write the size
  3483. if (this.length === adHoc + 4) {
  3484. // we started a nal unit, but didnt write one, so roll back the 4 byte size value
  3485. this.length -= 4;
  3486. } else if (adHoc > 0) {
  3487. nalStart = adHoc + 4;
  3488. nalLength = this.length - nalStart;
  3489. this.position = adHoc;
  3490. this.view.setUint32(this.position, nalLength);
  3491. this.position = this.length;
  3492. if (nalContainer) {
  3493. // Add the tag to the NAL unit
  3494. nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
  3495. }
  3496. }
  3497. adHoc = 0;
  3498. };
  3499. /**
  3500. * Write out a 64-bit floating point valued metadata property. This method is
  3501. * called frequently during a typical parse and needs to be fast.
  3502. */
  3503. // (key:String, val:Number):void
  3504. this.writeMetaDataDouble = function(key, val) {
  3505. var i;
  3506. prepareWrite(this, 2 + key.length + 9);
  3507. // write size of property name
  3508. this.view.setUint16(this.position, key.length);
  3509. this.position += 2;
  3510. // this next part looks terrible but it improves parser throughput by
  3511. // 10kB/s in my testing
  3512. // write property name
  3513. if (key === 'width') {
  3514. this.bytes.set(widthBytes, this.position);
  3515. this.position += 5;
  3516. } else if (key === 'height') {
  3517. this.bytes.set(heightBytes, this.position);
  3518. this.position += 6;
  3519. } else if (key === 'videocodecid') {
  3520. this.bytes.set(videocodecidBytes, this.position);
  3521. this.position += 12;
  3522. } else {
  3523. for (i = 0; i < key.length; i++) {
  3524. this.bytes[this.position] = key.charCodeAt(i);
  3525. this.position++;
  3526. }
  3527. }
  3528. // skip null byte
  3529. this.position++;
  3530. // write property value
  3531. this.view.setFloat64(this.position, val);
  3532. this.position += 8;
  3533. // update flv tag length
  3534. this.length = Math.max(this.length, this.position);
  3535. ++adHoc;
  3536. };
  3537. // (key:String, val:Boolean):void
  3538. this.writeMetaDataBoolean = function(key, val) {
  3539. var i;
  3540. prepareWrite(this, 2);
  3541. this.view.setUint16(this.position, key.length);
  3542. this.position += 2;
  3543. for (i = 0; i < key.length; i++) {
  3544. // if key.charCodeAt(i) >= 255, handle error
  3545. prepareWrite(this, 1);
  3546. this.bytes[this.position] = key.charCodeAt(i);
  3547. this.position++;
  3548. }
  3549. prepareWrite(this, 2);
  3550. this.view.setUint8(this.position, 0x01);
  3551. this.position++;
  3552. this.view.setUint8(this.position, val ? 0x01 : 0x00);
  3553. this.position++;
  3554. this.length = Math.max(this.length, this.position);
  3555. ++adHoc;
  3556. };
  3557. // ():ByteArray
  3558. this.finalize = function() {
  3559. var
  3560. dtsDelta, // :int
  3561. len; // :int
  3562. switch (this.bytes[0]) {
  3563. // Video Data
  3564. case FlvTag.VIDEO_TAG:
  3565. // We only support AVC, 1 = key frame (for AVC, a seekable
  3566. // frame), 2 = inter frame (for AVC, a non-seekable frame)
  3567. this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20) | 0x07;
  3568. this.bytes[12] = extraData ? 0x00 : 0x01;
  3569. dtsDelta = this.pts - this.dts;
  3570. this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
  3571. this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8;
  3572. this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0;
  3573. break;
  3574. case FlvTag.AUDIO_TAG:
  3575. this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
  3576. this.bytes[12] = extraData ? 0x00 : 0x01;
  3577. break;
  3578. case FlvTag.METADATA_TAG:
  3579. this.position = 11;
  3580. this.view.setUint8(this.position, 0x02); // String type
  3581. this.position++;
  3582. this.view.setUint16(this.position, 0x0A); // 10 Bytes
  3583. this.position += 2;
  3584. // set "onMetaData"
  3585. this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
  3586. 0x74, 0x61, 0x44, 0x61,
  3587. 0x74, 0x61], this.position);
  3588. this.position += 10;
  3589. this.bytes[this.position] = 0x08; // Array type
  3590. this.position++;
  3591. this.view.setUint32(this.position, adHoc);
  3592. this.position = this.length;
  3593. this.bytes.set([0, 0, 9], this.position);
  3594. this.position += 3; // End Data Tag
  3595. this.length = this.position;
  3596. break;
  3597. }
  3598. len = this.length - 11;
  3599. // write the DataSize field
  3600. this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
  3601. this.bytes[ 2] = (len & 0x0000FF00) >>> 8;
  3602. this.bytes[ 3] = (len & 0x000000FF) >>> 0;
  3603. // write the Timestamp
  3604. this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
  3605. this.bytes[ 5] = (this.dts & 0x0000FF00) >>> 8;
  3606. this.bytes[ 6] = (this.dts & 0x000000FF) >>> 0;
  3607. this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
  3608. // write the StreamID
  3609. this.bytes[ 8] = 0;
  3610. this.bytes[ 9] = 0;
  3611. this.bytes[10] = 0;
  3612. // Sometimes we're at the end of the view and have one slot to write a
  3613. // uint32, so, prepareWrite of count 4, since, view is uint8
  3614. prepareWrite(this, 4);
  3615. this.view.setUint32(this.length, this.length);
  3616. this.length += 4;
  3617. this.position += 4;
  3618. // trim down the byte buffer to what is actually being used
  3619. this.bytes = this.bytes.subarray(0, this.length);
  3620. this.frameTime = FlvTag.frameTime(this.bytes);
  3621. // if bytes.bytelength isn't equal to this.length, handle error
  3622. return this;
  3623. };
  3624. };
  3625. FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
  3626. FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
  3627. FlvTag.METADATA_TAG = 0x12; // == 18, :uint
  3628. // (tag:ByteArray):Boolean {
  3629. FlvTag.isAudioFrame = function(tag) {
  3630. return FlvTag.AUDIO_TAG === tag[0];
  3631. };
  3632. // (tag:ByteArray):Boolean {
  3633. FlvTag.isVideoFrame = function(tag) {
  3634. return FlvTag.VIDEO_TAG === tag[0];
  3635. };
  3636. // (tag:ByteArray):Boolean {
  3637. FlvTag.isMetaData = function(tag) {
  3638. return FlvTag.METADATA_TAG === tag[0];
  3639. };
  3640. // (tag:ByteArray):Boolean {
  3641. FlvTag.isKeyFrame = function(tag) {
  3642. if (FlvTag.isVideoFrame(tag)) {
  3643. return tag[11] === 0x17;
  3644. }
  3645. if (FlvTag.isAudioFrame(tag)) {
  3646. return true;
  3647. }
  3648. if (FlvTag.isMetaData(tag)) {
  3649. return true;
  3650. }
  3651. return false;
  3652. };
  3653. // (tag:ByteArray):uint {
  3654. FlvTag.frameTime = function(tag) {
  3655. var pts = tag[ 4] << 16; // :uint
  3656. pts |= tag[ 5] << 8;
  3657. pts |= tag[ 6] << 0;
  3658. pts |= tag[ 7] << 24;
  3659. return pts;
  3660. };
  3661. module.exports = FlvTag;
  3662. },{}],25:[function(require,module,exports){
  3663. module.exports = {
  3664. tag: require('./flv-tag'),
  3665. Transmuxer: require('./transmuxer'),
  3666. getFlvHeader: require('./flv-header')
  3667. };
  3668. },{"./flv-header":23,"./flv-tag":24,"./transmuxer":27}],26:[function(require,module,exports){
  3669. 'use strict';
  3670. var TagList = function() {
  3671. var self = this;
  3672. this.list = [];
  3673. this.push = function(tag) {
  3674. this.list.push({
  3675. bytes: tag.bytes,
  3676. dts: tag.dts,
  3677. pts: tag.pts,
  3678. keyFrame: tag.keyFrame,
  3679. metaDataTag: tag.metaDataTag
  3680. });
  3681. };
  3682. Object.defineProperty(this, 'length', {
  3683. get: function() {
  3684. return self.list.length;
  3685. }
  3686. });
  3687. };
  3688. module.exports = TagList;
  3689. },{}],27:[function(require,module,exports){
  3690. 'use strict';
  3691. var Stream = require('../utils/stream.js');
  3692. var FlvTag = require('./flv-tag.js');
  3693. var m2ts = require('../m2ts/m2ts.js');
  3694. var AdtsStream = require('../codecs/adts.js');
  3695. var H264Stream = require('../codecs/h264').H264Stream;
  3696. var CoalesceStream = require('./coalesce-stream.js');
  3697. var TagList = require('./tag-list.js');
  3698. var
  3699. Transmuxer,
  3700. VideoSegmentStream,
  3701. AudioSegmentStream,
  3702. collectTimelineInfo,
  3703. metaDataTag,
  3704. extraDataTag;
  3705. /**
  3706. * Store information about the start and end of the tracka and the
  3707. * duration for each frame/sample we process in order to calculate
  3708. * the baseMediaDecodeTime
  3709. */
  3710. collectTimelineInfo = function(track, data) {
  3711. if (typeof data.pts === 'number') {
  3712. if (track.timelineStartInfo.pts === undefined) {
  3713. track.timelineStartInfo.pts = data.pts;
  3714. } else {
  3715. track.timelineStartInfo.pts =
  3716. Math.min(track.timelineStartInfo.pts, data.pts);
  3717. }
  3718. }
  3719. if (typeof data.dts === 'number') {
  3720. if (track.timelineStartInfo.dts === undefined) {
  3721. track.timelineStartInfo.dts = data.dts;
  3722. } else {
  3723. track.timelineStartInfo.dts =
  3724. Math.min(track.timelineStartInfo.dts, data.dts);
  3725. }
  3726. }
  3727. };
  3728. metaDataTag = function(track, pts) {
  3729. var
  3730. tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag
  3731. tag.dts = pts;
  3732. tag.pts = pts;
  3733. tag.writeMetaDataDouble('videocodecid', 7);
  3734. tag.writeMetaDataDouble('width', track.width);
  3735. tag.writeMetaDataDouble('height', track.height);
  3736. return tag;
  3737. };
  3738. extraDataTag = function(track, pts) {
  3739. var
  3740. i,
  3741. tag = new FlvTag(FlvTag.VIDEO_TAG, true);
  3742. tag.dts = pts;
  3743. tag.pts = pts;
  3744. tag.writeByte(0x01);// version
  3745. tag.writeByte(track.profileIdc);// profile
  3746. tag.writeByte(track.profileCompatibility);// compatibility
  3747. tag.writeByte(track.levelIdc);// level
  3748. tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
  3749. tag.writeByte(0xE0 | 0x01); // reserved (3 bits), num of SPS (5 bits)
  3750. tag.writeShort(track.sps[0].length); // data of SPS
  3751. tag.writeBytes(track.sps[0]); // SPS
  3752. tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
  3753. for (i = 0; i < track.pps.length; ++i) {
  3754. tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
  3755. tag.writeBytes(track.pps[i]); // data of PPS
  3756. }
  3757. return tag;
  3758. };
  3759. /**
  3760. * Constructs a single-track, media segment from AAC data
  3761. * events. The output of this stream can be fed to flash.
  3762. */
  3763. AudioSegmentStream = function(track) {
  3764. var
  3765. adtsFrames = [],
  3766. videoKeyFrames = [],
  3767. oldExtraData;
  3768. AudioSegmentStream.prototype.init.call(this);
  3769. this.push = function(data) {
  3770. collectTimelineInfo(track, data);
  3771. if (track) {
  3772. track.audioobjecttype = data.audioobjecttype;
  3773. track.channelcount = data.channelcount;
  3774. track.samplerate = data.samplerate;
  3775. track.samplingfrequencyindex = data.samplingfrequencyindex;
  3776. track.samplesize = data.samplesize;
  3777. track.extraData = (track.audioobjecttype << 11) |
  3778. (track.samplingfrequencyindex << 7) |
  3779. (track.channelcount << 3);
  3780. }
  3781. data.pts = Math.round(data.pts / 90);
  3782. data.dts = Math.round(data.dts / 90);
  3783. // buffer audio data until end() is called
  3784. adtsFrames.push(data);
  3785. };
  3786. this.flush = function() {
  3787. var currentFrame, adtsFrame, lastMetaPts, tags = new TagList();
  3788. // return early if no audio data has been observed
  3789. if (adtsFrames.length === 0) {
  3790. this.trigger('done', 'AudioSegmentStream');
  3791. return;
  3792. }
  3793. lastMetaPts = -Infinity;
  3794. while (adtsFrames.length) {
  3795. currentFrame = adtsFrames.shift();
  3796. // write out a metadata frame at every video key frame
  3797. if (videoKeyFrames.length && currentFrame.pts >= videoKeyFrames[0]) {
  3798. lastMetaPts = videoKeyFrames.shift();
  3799. this.writeMetaDataTags(tags, lastMetaPts);
  3800. }
  3801. // also write out metadata tags every 1 second so that the decoder
  3802. // is re-initialized quickly after seeking into a different
  3803. // audio configuration.
  3804. if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
  3805. this.writeMetaDataTags(tags, currentFrame.pts);
  3806. oldExtraData = track.extraData;
  3807. lastMetaPts = currentFrame.pts;
  3808. }
  3809. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
  3810. adtsFrame.pts = currentFrame.pts;
  3811. adtsFrame.dts = currentFrame.dts;
  3812. adtsFrame.writeBytes(currentFrame.data);
  3813. tags.push(adtsFrame.finalize());
  3814. }
  3815. videoKeyFrames.length = 0;
  3816. oldExtraData = null;
  3817. this.trigger('data', {track: track, tags: tags.list});
  3818. this.trigger('done', 'AudioSegmentStream');
  3819. };
  3820. this.writeMetaDataTags = function(tags, pts) {
  3821. var adtsFrame;
  3822. adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
  3823. // For audio, DTS is always the same as PTS. We want to set the DTS
  3824. // however so we can compare with video DTS to determine approximate
  3825. // packet order
  3826. adtsFrame.pts = pts;
  3827. adtsFrame.dts = pts;
  3828. // AAC is always 10
  3829. adtsFrame.writeMetaDataDouble('audiocodecid', 10);
  3830. adtsFrame.writeMetaDataBoolean('stereo', track.channelcount === 2);
  3831. adtsFrame.writeMetaDataDouble('audiosamplerate', track.samplerate);
  3832. // Is AAC always 16 bit?
  3833. adtsFrame.writeMetaDataDouble('audiosamplesize', 16);
  3834. tags.push(adtsFrame.finalize());
  3835. adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
  3836. // For audio, DTS is always the same as PTS. We want to set the DTS
  3837. // however so we can compare with video DTS to determine approximate
  3838. // packet order
  3839. adtsFrame.pts = pts;
  3840. adtsFrame.dts = pts;
  3841. adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
  3842. adtsFrame.position += 2;
  3843. adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);
  3844. tags.push(adtsFrame.finalize());
  3845. };
  3846. this.onVideoKeyFrame = function(pts) {
  3847. videoKeyFrames.push(pts);
  3848. };
  3849. };
  3850. AudioSegmentStream.prototype = new Stream();
  3851. /**
  3852. * Store FlvTags for the h264 stream
  3853. * @param track {object} track metadata configuration
  3854. */
  3855. VideoSegmentStream = function(track) {
  3856. var
  3857. nalUnits = [],
  3858. config,
  3859. h264Frame;
  3860. VideoSegmentStream.prototype.init.call(this);
  3861. this.finishFrame = function(tags, frame) {
  3862. if (!frame) {
  3863. return;
  3864. }
  3865. // Check if keyframe and the length of tags.
  3866. // This makes sure we write metadata on the first frame of a segment.
  3867. if (config && track && track.newMetadata &&
  3868. (frame.keyFrame || tags.length === 0)) {
  3869. // Push extra data on every IDR frame in case we did a stream change + seek
  3870. var metaTag = metaDataTag(config, frame.dts).finalize();
  3871. var extraTag = extraDataTag(track, frame.dts).finalize();
  3872. metaTag.metaDataTag = extraTag.metaDataTag = true;
  3873. tags.push(metaTag);
  3874. tags.push(extraTag);
  3875. track.newMetadata = false;
  3876. this.trigger('keyframe', frame.dts);
  3877. }
  3878. frame.endNalUnit();
  3879. tags.push(frame.finalize());
  3880. h264Frame = null;
  3881. };
  3882. this.push = function(data) {
  3883. collectTimelineInfo(track, data);
  3884. data.pts = Math.round(data.pts / 90);
  3885. data.dts = Math.round(data.dts / 90);
  3886. // buffer video until flush() is called
  3887. nalUnits.push(data);
  3888. };
  3889. this.flush = function() {
  3890. var
  3891. currentNal,
  3892. tags = new TagList();
  3893. // Throw away nalUnits at the start of the byte stream until we find
  3894. // the first AUD
  3895. while (nalUnits.length) {
  3896. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  3897. break;
  3898. }
  3899. nalUnits.shift();
  3900. }
  3901. // return early if no video data has been observed
  3902. if (nalUnits.length === 0) {
  3903. this.trigger('done', 'VideoSegmentStream');
  3904. return;
  3905. }
  3906. while (nalUnits.length) {
  3907. currentNal = nalUnits.shift();
  3908. // record the track config
  3909. if (currentNal.nalUnitType === 'seq_parameter_set_rbsp') {
  3910. track.newMetadata = true;
  3911. config = currentNal.config;
  3912. track.width = config.width;
  3913. track.height = config.height;
  3914. track.sps = [currentNal.data];
  3915. track.profileIdc = config.profileIdc;
  3916. track.levelIdc = config.levelIdc;
  3917. track.profileCompatibility = config.profileCompatibility;
  3918. h264Frame.endNalUnit();
  3919. } else if (currentNal.nalUnitType === 'pic_parameter_set_rbsp') {
  3920. track.newMetadata = true;
  3921. track.pps = [currentNal.data];
  3922. h264Frame.endNalUnit();
  3923. } else if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  3924. if (h264Frame) {
  3925. this.finishFrame(tags, h264Frame);
  3926. }
  3927. h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
  3928. h264Frame.pts = currentNal.pts;
  3929. h264Frame.dts = currentNal.dts;
  3930. } else {
  3931. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  3932. // the current sample is a key frame
  3933. h264Frame.keyFrame = true;
  3934. }
  3935. h264Frame.endNalUnit();
  3936. }
  3937. h264Frame.startNalUnit();
  3938. h264Frame.writeBytes(currentNal.data);
  3939. }
  3940. if (h264Frame) {
  3941. this.finishFrame(tags, h264Frame);
  3942. }
  3943. this.trigger('data', {track: track, tags: tags.list});
  3944. // Continue with the flush process now
  3945. this.trigger('done', 'VideoSegmentStream');
  3946. };
  3947. };
  3948. VideoSegmentStream.prototype = new Stream();
  3949. /**
  3950. * An object that incrementally transmuxes MPEG2 Trasport Stream
  3951. * chunks into an FLV.
  3952. */
  3953. Transmuxer = function(options) {
  3954. var
  3955. self = this,
  3956. packetStream, parseStream, elementaryStream,
  3957. videoTimestampRolloverStream, audioTimestampRolloverStream,
  3958. timedMetadataTimestampRolloverStream,
  3959. adtsStream, h264Stream,
  3960. videoSegmentStream, audioSegmentStream, captionStream,
  3961. coalesceStream;
  3962. Transmuxer.prototype.init.call(this);
  3963. options = options || {};
  3964. // expose the metadata stream
  3965. this.metadataStream = new m2ts.MetadataStream();
  3966. options.metadataStream = this.metadataStream;
  3967. // set up the parsing pipeline
  3968. packetStream = new m2ts.TransportPacketStream();
  3969. parseStream = new m2ts.TransportParseStream();
  3970. elementaryStream = new m2ts.ElementaryStream();
  3971. videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  3972. audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  3973. timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  3974. adtsStream = new AdtsStream();
  3975. h264Stream = new H264Stream();
  3976. coalesceStream = new CoalesceStream(options);
  3977. // disassemble MPEG2-TS packets into elementary streams
  3978. packetStream
  3979. .pipe(parseStream)
  3980. .pipe(elementaryStream);
  3981. // !!THIS ORDER IS IMPORTANT!!
  3982. // demux the streams
  3983. elementaryStream
  3984. .pipe(videoTimestampRolloverStream)
  3985. .pipe(h264Stream);
  3986. elementaryStream
  3987. .pipe(audioTimestampRolloverStream)
  3988. .pipe(adtsStream);
  3989. elementaryStream
  3990. .pipe(timedMetadataTimestampRolloverStream)
  3991. .pipe(this.metadataStream)
  3992. .pipe(coalesceStream);
  3993. // if CEA-708 parsing is available, hook up a caption stream
  3994. captionStream = new m2ts.CaptionStream();
  3995. h264Stream.pipe(captionStream)
  3996. .pipe(coalesceStream);
  3997. // hook up the segment streams once track metadata is delivered
  3998. elementaryStream.on('data', function(data) {
  3999. var i, videoTrack, audioTrack;
  4000. if (data.type === 'metadata') {
  4001. i = data.tracks.length;
  4002. // scan the tracks listed in the metadata
  4003. while (i--) {
  4004. if (data.tracks[i].type === 'video') {
  4005. videoTrack = data.tracks[i];
  4006. } else if (data.tracks[i].type === 'audio') {
  4007. audioTrack = data.tracks[i];
  4008. }
  4009. }
  4010. // hook up the video segment stream to the first track with h264 data
  4011. if (videoTrack && !videoSegmentStream) {
  4012. coalesceStream.numberOfTracks++;
  4013. videoSegmentStream = new VideoSegmentStream(videoTrack);
  4014. // Set up the final part of the video pipeline
  4015. h264Stream
  4016. .pipe(videoSegmentStream)
  4017. .pipe(coalesceStream);
  4018. }
  4019. if (audioTrack && !audioSegmentStream) {
  4020. // hook up the audio segment stream to the first track with aac data
  4021. coalesceStream.numberOfTracks++;
  4022. audioSegmentStream = new AudioSegmentStream(audioTrack);
  4023. // Set up the final part of the audio pipeline
  4024. adtsStream
  4025. .pipe(audioSegmentStream)
  4026. .pipe(coalesceStream);
  4027. if (videoSegmentStream) {
  4028. videoSegmentStream.on('keyframe', audioSegmentStream.onVideoKeyFrame);
  4029. }
  4030. }
  4031. }
  4032. });
  4033. // feed incoming data to the front of the parsing pipeline
  4034. this.push = function(data) {
  4035. packetStream.push(data);
  4036. };
  4037. // flush any buffered data
  4038. this.flush = function() {
  4039. // Start at the top of the pipeline and flush all pending work
  4040. packetStream.flush();
  4041. };
  4042. // Caption data has to be reset when seeking outside buffered range
  4043. this.resetCaptions = function() {
  4044. captionStream.reset();
  4045. };
  4046. // Re-emit any data coming from the coalesce stream to the outside world
  4047. coalesceStream.on('data', function(event) {
  4048. self.trigger('data', event);
  4049. });
  4050. // Let the consumer know we have finished flushing the entire pipeline
  4051. coalesceStream.on('done', function() {
  4052. self.trigger('done');
  4053. });
  4054. };
  4055. Transmuxer.prototype = new Stream();
  4056. // forward compatibility
  4057. module.exports = Transmuxer;
  4058. },{"../codecs/adts.js":19,"../codecs/h264":20,"../m2ts/m2ts.js":29,"../utils/stream.js":38,"./coalesce-stream.js":22,"./flv-tag.js":24,"./tag-list.js":26}],28:[function(require,module,exports){
  4059. /**
  4060. * mux.js
  4061. *
  4062. * Copyright (c) 2015 Brightcove
  4063. * All rights reserved.
  4064. *
  4065. * Reads in-band caption information from a video elementary
  4066. * stream. Captions must follow the CEA-708 standard for injection
  4067. * into an MPEG-2 transport streams.
  4068. * @see https://en.wikipedia.org/wiki/CEA-708
  4069. * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
  4070. */
  4071. 'use strict';
  4072. // -----------------
  4073. // Link To Transport
  4074. // -----------------
  4075. // Supplemental enhancement information (SEI) NAL units have a
  4076. // payload type field to indicate how they are to be
  4077. // interpreted. CEAS-708 caption content is always transmitted with
  4078. // payload type 0x04.
  4079. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  4080. RBSP_TRAILING_BITS = 128,
  4081. Stream = require('../utils/stream');
  4082. /**
  4083. * Parse a supplemental enhancement information (SEI) NAL unit.
  4084. * Stops parsing once a message of type ITU T T35 has been found.
  4085. *
  4086. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  4087. * @return {object} the parsed SEI payload
  4088. * @see Rec. ITU-T H.264, 7.3.2.3.1
  4089. */
  4090. var parseSei = function(bytes) {
  4091. var
  4092. i = 0,
  4093. result = {
  4094. payloadType: -1,
  4095. payloadSize: 0
  4096. },
  4097. payloadType = 0,
  4098. payloadSize = 0;
  4099. // go through the sei_rbsp parsing each each individual sei_message
  4100. while (i < bytes.byteLength) {
  4101. // stop once we have hit the end of the sei_rbsp
  4102. if (bytes[i] === RBSP_TRAILING_BITS) {
  4103. break;
  4104. }
  4105. // Parse payload type
  4106. while (bytes[i] === 0xFF) {
  4107. payloadType += 255;
  4108. i++;
  4109. }
  4110. payloadType += bytes[i++];
  4111. // Parse payload size
  4112. while (bytes[i] === 0xFF) {
  4113. payloadSize += 255;
  4114. i++;
  4115. }
  4116. payloadSize += bytes[i++];
  4117. // this sei_message is a 608/708 caption so save it and break
  4118. // there can only ever be one caption message in a frame's sei
  4119. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  4120. result.payloadType = payloadType;
  4121. result.payloadSize = payloadSize;
  4122. result.payload = bytes.subarray(i, i + payloadSize);
  4123. break;
  4124. }
  4125. // skip the payload and parse the next message
  4126. i += payloadSize;
  4127. payloadType = 0;
  4128. payloadSize = 0;
  4129. }
  4130. return result;
  4131. };
  4132. // see ANSI/SCTE 128-1 (2013), section 8.1
  4133. var parseUserData = function(sei) {
  4134. // itu_t_t35_contry_code must be 181 (United States) for
  4135. // captions
  4136. if (sei.payload[0] !== 181) {
  4137. return null;
  4138. }
  4139. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  4140. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  4141. return null;
  4142. }
  4143. // the user_identifier should be "GA94" to indicate ATSC1 data
  4144. if (String.fromCharCode(sei.payload[3],
  4145. sei.payload[4],
  4146. sei.payload[5],
  4147. sei.payload[6]) !== 'GA94') {
  4148. return null;
  4149. }
  4150. // finally, user_data_type_code should be 0x03 for caption data
  4151. if (sei.payload[7] !== 0x03) {
  4152. return null;
  4153. }
  4154. // return the user_data_type_structure and strip the trailing
  4155. // marker bits
  4156. return sei.payload.subarray(8, sei.payload.length - 1);
  4157. };
  4158. // see CEA-708-D, section 4.4
  4159. var parseCaptionPackets = function(pts, userData) {
  4160. var results = [], i, count, offset, data;
  4161. // if this is just filler, return immediately
  4162. if (!(userData[0] & 0x40)) {
  4163. return results;
  4164. }
  4165. // parse out the cc_data_1 and cc_data_2 fields
  4166. count = userData[0] & 0x1f;
  4167. for (i = 0; i < count; i++) {
  4168. offset = i * 3;
  4169. data = {
  4170. type: userData[offset + 2] & 0x03,
  4171. pts: pts
  4172. };
  4173. // capture cc data when cc_valid is 1
  4174. if (userData[offset + 2] & 0x04) {
  4175. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  4176. results.push(data);
  4177. }
  4178. }
  4179. return results;
  4180. };
  4181. var CaptionStream = function() {
  4182. CaptionStream.prototype.init.call(this);
  4183. this.captionPackets_ = [];
  4184. this.ccStreams_ = [
  4185. new Cea608Stream(0, 0), // eslint-disable-line no-use-before-define
  4186. new Cea608Stream(0, 1), // eslint-disable-line no-use-before-define
  4187. new Cea608Stream(1, 0), // eslint-disable-line no-use-before-define
  4188. new Cea608Stream(1, 1) // eslint-disable-line no-use-before-define
  4189. ];
  4190. this.reset();
  4191. // forward data and done events from CCs to this CaptionStream
  4192. this.ccStreams_.forEach(function(cc) {
  4193. cc.on('data', this.trigger.bind(this, 'data'));
  4194. cc.on('done', this.trigger.bind(this, 'done'));
  4195. }, this);
  4196. };
  4197. CaptionStream.prototype = new Stream();
  4198. CaptionStream.prototype.push = function(event) {
  4199. var sei, userData;
  4200. // only examine SEI NALs
  4201. if (event.nalUnitType !== 'sei_rbsp') {
  4202. return;
  4203. }
  4204. // parse the sei
  4205. sei = parseSei(event.escapedRBSP);
  4206. // ignore everything but user_data_registered_itu_t_t35
  4207. if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
  4208. return;
  4209. }
  4210. // parse out the user data payload
  4211. userData = parseUserData(sei);
  4212. // ignore unrecognized userData
  4213. if (!userData) {
  4214. return;
  4215. }
  4216. // Sometimes, the same segment # will be downloaded twice. To stop the
  4217. // caption data from being processed twice, we track the latest dts we've
  4218. // received and ignore everything with a dts before that. However, since
  4219. // data for a specific dts can be split across 2 packets on either side of
  4220. // a segment boundary, we need to make sure we *don't* ignore the second
  4221. // dts packet we receive that has dts === this.latestDts_. And thus, the
  4222. // ignoreNextEqualDts_ flag was born.
  4223. if (event.dts < this.latestDts_) {
  4224. // We've started getting older data, so set the flag.
  4225. this.ignoreNextEqualDts_ = true;
  4226. return;
  4227. } else if ((event.dts === this.latestDts_) && (this.ignoreNextEqualDts_)) {
  4228. // We've received the last duplicate packet, time to start processing again
  4229. this.ignoreNextEqualDts_ = false;
  4230. return;
  4231. }
  4232. // parse out CC data packets and save them for later
  4233. this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
  4234. this.latestDts_ = event.dts;
  4235. };
  4236. CaptionStream.prototype.flush = function() {
  4237. // make sure we actually parsed captions before proceeding
  4238. if (!this.captionPackets_.length) {
  4239. this.ccStreams_.forEach(function(cc) {
  4240. cc.flush();
  4241. }, this);
  4242. return;
  4243. }
  4244. // In Chrome, the Array#sort function is not stable so add a
  4245. // presortIndex that we can use to ensure we get a stable-sort
  4246. this.captionPackets_.forEach(function(elem, idx) {
  4247. elem.presortIndex = idx;
  4248. });
  4249. // sort caption byte-pairs based on their PTS values
  4250. this.captionPackets_.sort(function(a, b) {
  4251. if (a.pts === b.pts) {
  4252. return a.presortIndex - b.presortIndex;
  4253. }
  4254. return a.pts - b.pts;
  4255. });
  4256. this.captionPackets_.forEach(function(packet) {
  4257. if (packet.type < 2) {
  4258. // Dispatch packet to the right Cea608Stream
  4259. this.dispatchCea608Packet(packet);
  4260. }
  4261. // this is where an 'else' would go for a dispatching packets
  4262. // to a theoretical Cea708Stream that handles SERVICEn data
  4263. }, this);
  4264. this.captionPackets_.length = 0;
  4265. this.ccStreams_.forEach(function(cc) {
  4266. cc.flush();
  4267. }, this);
  4268. return;
  4269. };
  4270. CaptionStream.prototype.reset = function() {
  4271. this.latestDts_ = null;
  4272. this.ignoreNextEqualDts_ = false;
  4273. this.activeCea608Channel_ = [null, null];
  4274. this.ccStreams_.forEach(function(ccStream) {
  4275. ccStream.reset();
  4276. });
  4277. };
  4278. CaptionStream.prototype.dispatchCea608Packet = function(packet) {
  4279. // NOTE: packet.type is the CEA608 field
  4280. if (this.setsChannel1Active(packet)) {
  4281. this.activeCea608Channel_[packet.type] = 0;
  4282. } else if (this.setsChannel2Active(packet)) {
  4283. this.activeCea608Channel_[packet.type] = 1;
  4284. }
  4285. if (this.activeCea608Channel_[packet.type] === null) {
  4286. // If we haven't received anything to set the active channel, discard the
  4287. // data; we don't want jumbled captions
  4288. return;
  4289. }
  4290. this.ccStreams_[(packet.type << 1) + this.activeCea608Channel_[packet.type]].push(packet);
  4291. };
  4292. CaptionStream.prototype.setsChannel1Active = function(packet) {
  4293. return ((packet.ccData & 0x7800) === 0x1000);
  4294. };
  4295. CaptionStream.prototype.setsChannel2Active = function(packet) {
  4296. return ((packet.ccData & 0x7800) === 0x1800);
  4297. };
  4298. // ----------------------
  4299. // Session to Application
  4300. // ----------------------
  4301. var CHARACTER_TRANSLATION = {
  4302. 0x2a: 0xe1, // á
  4303. 0x5c: 0xe9, // é
  4304. 0x5e: 0xed, // í
  4305. 0x5f: 0xf3, // ó
  4306. 0x60: 0xfa, // ú
  4307. 0x7b: 0xe7, // ç
  4308. 0x7c: 0xf7, // ÷
  4309. 0x7d: 0xd1, // Ñ
  4310. 0x7e: 0xf1, // ñ
  4311. 0x7f: 0x2588, // █
  4312. 0x0130: 0xae, // ®
  4313. 0x0131: 0xb0, // °
  4314. 0x0132: 0xbd, // ½
  4315. 0x0133: 0xbf, // ¿
  4316. 0x0134: 0x2122, // ™
  4317. 0x0135: 0xa2, // ¢
  4318. 0x0136: 0xa3, // £
  4319. 0x0137: 0x266a, // ♪
  4320. 0x0138: 0xe0, // à
  4321. 0x0139: 0xa0, //
  4322. 0x013a: 0xe8, // è
  4323. 0x013b: 0xe2, // â
  4324. 0x013c: 0xea, // ê
  4325. 0x013d: 0xee, // î
  4326. 0x013e: 0xf4, // ô
  4327. 0x013f: 0xfb, // û
  4328. 0x0220: 0xc1, // Á
  4329. 0x0221: 0xc9, // É
  4330. 0x0222: 0xd3, // Ó
  4331. 0x0223: 0xda, // Ú
  4332. 0x0224: 0xdc, // Ü
  4333. 0x0225: 0xfc, // ü
  4334. 0x0226: 0x2018, // ‘
  4335. 0x0227: 0xa1, // ¡
  4336. 0x0228: 0x2a, // *
  4337. 0x0229: 0x27, // '
  4338. 0x022a: 0x2014, // —
  4339. 0x022b: 0xa9, // ©
  4340. 0x022c: 0x2120, // ℠
  4341. 0x022d: 0x2022, // •
  4342. 0x022e: 0x201c, // “
  4343. 0x022f: 0x201d, // ”
  4344. 0x0230: 0xc0, // À
  4345. 0x0231: 0xc2, // Â
  4346. 0x0232: 0xc7, // Ç
  4347. 0x0233: 0xc8, // È
  4348. 0x0234: 0xca, // Ê
  4349. 0x0235: 0xcb, // Ë
  4350. 0x0236: 0xeb, // ë
  4351. 0x0237: 0xce, // Î
  4352. 0x0238: 0xcf, // Ï
  4353. 0x0239: 0xef, // ï
  4354. 0x023a: 0xd4, // Ô
  4355. 0x023b: 0xd9, // Ù
  4356. 0x023c: 0xf9, // ù
  4357. 0x023d: 0xdb, // Û
  4358. 0x023e: 0xab, // «
  4359. 0x023f: 0xbb, // »
  4360. 0x0320: 0xc3, // Ã
  4361. 0x0321: 0xe3, // ã
  4362. 0x0322: 0xcd, // Í
  4363. 0x0323: 0xcc, // Ì
  4364. 0x0324: 0xec, // ì
  4365. 0x0325: 0xd2, // Ò
  4366. 0x0326: 0xf2, // ò
  4367. 0x0327: 0xd5, // Õ
  4368. 0x0328: 0xf5, // õ
  4369. 0x0329: 0x7b, // {
  4370. 0x032a: 0x7d, // }
  4371. 0x032b: 0x5c, // \
  4372. 0x032c: 0x5e, // ^
  4373. 0x032d: 0x5f, // _
  4374. 0x032e: 0x7c, // |
  4375. 0x032f: 0x7e, // ~
  4376. 0x0330: 0xc4, // Ä
  4377. 0x0331: 0xe4, // ä
  4378. 0x0332: 0xd6, // Ö
  4379. 0x0333: 0xf6, // ö
  4380. 0x0334: 0xdf, // ß
  4381. 0x0335: 0xa5, // ¥
  4382. 0x0336: 0xa4, // ¤
  4383. 0x0337: 0x2502, // │
  4384. 0x0338: 0xc5, // Å
  4385. 0x0339: 0xe5, // å
  4386. 0x033a: 0xd8, // Ø
  4387. 0x033b: 0xf8, // ø
  4388. 0x033c: 0x250c, // ┌
  4389. 0x033d: 0x2510, // ┐
  4390. 0x033e: 0x2514, // └
  4391. 0x033f: 0x2518 // ┘
  4392. };
  4393. var getCharFromCode = function(code) {
  4394. if (code === null) {
  4395. return '';
  4396. }
  4397. code = CHARACTER_TRANSLATION[code] || code;
  4398. return String.fromCharCode(code);
  4399. };
  4400. // the index of the last row in a CEA-608 display buffer
  4401. var BOTTOM_ROW = 14;
  4402. // This array is used for mapping PACs -> row #, since there's no way of
  4403. // getting it through bit logic.
  4404. var ROWS = [0x1100, 0x1120, 0x1200, 0x1220, 0x1500, 0x1520, 0x1600, 0x1620,
  4405. 0x1700, 0x1720, 0x1000, 0x1300, 0x1320, 0x1400, 0x1420];
  4406. // CEA-608 captions are rendered onto a 34x15 matrix of character
  4407. // cells. The "bottom" row is the last element in the outer array.
  4408. var createDisplayBuffer = function() {
  4409. var result = [], i = BOTTOM_ROW + 1;
  4410. while (i--) {
  4411. result.push('');
  4412. }
  4413. return result;
  4414. };
  4415. var Cea608Stream = function(field, dataChannel) {
  4416. Cea608Stream.prototype.init.call(this);
  4417. this.field_ = field || 0;
  4418. this.dataChannel_ = dataChannel || 0;
  4419. this.name_ = 'CC' + (((this.field_ << 1) | this.dataChannel_) + 1);
  4420. this.setConstants();
  4421. this.reset();
  4422. this.push = function(packet) {
  4423. var data, swap, char0, char1, text;
  4424. // remove the parity bits
  4425. data = packet.ccData & 0x7f7f;
  4426. // ignore duplicate control codes; the spec demands they're sent twice
  4427. if (data === this.lastControlCode_) {
  4428. this.lastControlCode_ = null;
  4429. return;
  4430. }
  4431. // Store control codes
  4432. if ((data & 0xf000) === 0x1000) {
  4433. this.lastControlCode_ = data;
  4434. } else if (data !== this.PADDING_) {
  4435. this.lastControlCode_ = null;
  4436. }
  4437. char0 = data >>> 8;
  4438. char1 = data & 0xff;
  4439. if (data === this.PADDING_) {
  4440. return;
  4441. } else if (data === this.RESUME_CAPTION_LOADING_) {
  4442. this.mode_ = 'popOn';
  4443. } else if (data === this.END_OF_CAPTION_) {
  4444. this.clearFormatting(packet.pts);
  4445. // if a caption was being displayed, it's gone now
  4446. this.flushDisplayed(packet.pts);
  4447. // flip memory
  4448. swap = this.displayed_;
  4449. this.displayed_ = this.nonDisplayed_;
  4450. this.nonDisplayed_ = swap;
  4451. // start measuring the time to display the caption
  4452. this.startPts_ = packet.pts;
  4453. } else if (data === this.ROLL_UP_2_ROWS_) {
  4454. this.topRow_ = BOTTOM_ROW - 1;
  4455. this.mode_ = 'rollUp';
  4456. } else if (data === this.ROLL_UP_3_ROWS_) {
  4457. this.topRow_ = BOTTOM_ROW - 2;
  4458. this.mode_ = 'rollUp';
  4459. } else if (data === this.ROLL_UP_4_ROWS_) {
  4460. this.topRow_ = BOTTOM_ROW - 3;
  4461. this.mode_ = 'rollUp';
  4462. } else if (data === this.CARRIAGE_RETURN_) {
  4463. this.clearFormatting(packet.pts);
  4464. this.flushDisplayed(packet.pts);
  4465. this.shiftRowsUp_();
  4466. this.startPts_ = packet.pts;
  4467. } else if (data === this.BACKSPACE_) {
  4468. if (this.mode_ === 'popOn') {
  4469. this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
  4470. } else {
  4471. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  4472. }
  4473. } else if (data === this.ERASE_DISPLAYED_MEMORY_) {
  4474. this.flushDisplayed(packet.pts);
  4475. this.displayed_ = createDisplayBuffer();
  4476. } else if (data === this.ERASE_NON_DISPLAYED_MEMORY_) {
  4477. this.nonDisplayed_ = createDisplayBuffer();
  4478. } else if (data === this.RESUME_DIRECT_CAPTIONING_) {
  4479. this.mode_ = 'paintOn';
  4480. // Append special characters to caption text
  4481. } else if (this.isSpecialCharacter(char0, char1)) {
  4482. // Bitmask char0 so that we can apply character transformations
  4483. // regardless of field and data channel.
  4484. // Then byte-shift to the left and OR with char1 so we can pass the
  4485. // entire character code to `getCharFromCode`.
  4486. char0 = (char0 & 0x03) << 8;
  4487. text = getCharFromCode(char0 | char1);
  4488. this[this.mode_](packet.pts, text);
  4489. this.column_++;
  4490. // Append extended characters to caption text
  4491. } else if (this.isExtCharacter(char0, char1)) {
  4492. // Extended characters always follow their "non-extended" equivalents.
  4493. // IE if a "è" is desired, you'll always receive "eè"; non-compliant
  4494. // decoders are supposed to drop the "è", while compliant decoders
  4495. // backspace the "e" and insert "è".
  4496. // Delete the previous character
  4497. if (this.mode_ === 'popOn') {
  4498. this.nonDisplayed_[this.row_] = this.nonDisplayed_[this.row_].slice(0, -1);
  4499. } else {
  4500. this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
  4501. }
  4502. // Bitmask char0 so that we can apply character transformations
  4503. // regardless of field and data channel.
  4504. // Then byte-shift to the left and OR with char1 so we can pass the
  4505. // entire character code to `getCharFromCode`.
  4506. char0 = (char0 & 0x03) << 8;
  4507. text = getCharFromCode(char0 | char1);
  4508. this[this.mode_](packet.pts, text);
  4509. this.column_++;
  4510. // Process mid-row codes
  4511. } else if (this.isMidRowCode(char0, char1)) {
  4512. // Attributes are not additive, so clear all formatting
  4513. this.clearFormatting(packet.pts);
  4514. // According to the standard, mid-row codes
  4515. // should be replaced with spaces, so add one now
  4516. this[this.mode_](packet.pts, ' ');
  4517. this.column_++;
  4518. if ((char1 & 0xe) === 0xe) {
  4519. this.addFormatting(packet.pts, ['i']);
  4520. }
  4521. if ((char1 & 0x1) === 0x1) {
  4522. this.addFormatting(packet.pts, ['u']);
  4523. }
  4524. // Detect offset control codes and adjust cursor
  4525. } else if (this.isOffsetControlCode(char0, char1)) {
  4526. // Cursor position is set by indent PAC (see below) in 4-column
  4527. // increments, with an additional offset code of 1-3 to reach any
  4528. // of the 32 columns specified by CEA-608. So all we need to do
  4529. // here is increment the column cursor by the given offset.
  4530. this.column_ += (char1 & 0x03);
  4531. // Detect PACs (Preamble Address Codes)
  4532. } else if (this.isPAC(char0, char1)) {
  4533. // There's no logic for PAC -> row mapping, so we have to just
  4534. // find the row code in an array and use its index :(
  4535. var row = ROWS.indexOf(data & 0x1f20);
  4536. if (row !== this.row_) {
  4537. // formatting is only persistent for current row
  4538. this.clearFormatting(packet.pts);
  4539. this.row_ = row;
  4540. }
  4541. // All PACs can apply underline, so detect and apply
  4542. // (All odd-numbered second bytes set underline)
  4543. if ((char1 & 0x1) && (this.formatting_.indexOf('u') === -1)) {
  4544. this.addFormatting(packet.pts, ['u']);
  4545. }
  4546. if ((data & 0x10) === 0x10) {
  4547. // We've got an indent level code. Each successive even number
  4548. // increments the column cursor by 4, so we can get the desired
  4549. // column position by bit-shifting to the right (to get n/2)
  4550. // and multiplying by 4.
  4551. this.column_ = ((data & 0xe) >> 1) * 4;
  4552. }
  4553. if (this.isColorPAC(char1)) {
  4554. // it's a color code, though we only support white, which
  4555. // can be either normal or italicized. white italics can be
  4556. // either 0x4e or 0x6e depending on the row, so we just
  4557. // bitwise-and with 0xe to see if italics should be turned on
  4558. if ((char1 & 0xe) === 0xe) {
  4559. this.addFormatting(packet.pts, ['i']);
  4560. }
  4561. }
  4562. // We have a normal character in char0, and possibly one in char1
  4563. } else if (this.isNormalChar(char0)) {
  4564. if (char1 === 0x00) {
  4565. char1 = null;
  4566. }
  4567. text = getCharFromCode(char0);
  4568. text += getCharFromCode(char1);
  4569. this[this.mode_](packet.pts, text);
  4570. this.column_ += text.length;
  4571. } // finish data processing
  4572. };
  4573. };
  4574. Cea608Stream.prototype = new Stream();
  4575. // Trigger a cue point that captures the current state of the
  4576. // display buffer
  4577. Cea608Stream.prototype.flushDisplayed = function(pts) {
  4578. var content = this.displayed_
  4579. // remove spaces from the start and end of the string
  4580. .map(function(row) {
  4581. return row.trim();
  4582. })
  4583. // combine all text rows to display in one cue
  4584. .join('\n')
  4585. // and remove blank rows from the start and end, but not the middle
  4586. .replace(/^\n+|\n+$/g, '');
  4587. if (content.length) {
  4588. this.trigger('data', {
  4589. startPts: this.startPts_,
  4590. endPts: pts,
  4591. text: content,
  4592. stream: this.name_
  4593. });
  4594. }
  4595. };
  4596. /**
  4597. * Zero out the data, used for startup and on seek
  4598. */
  4599. Cea608Stream.prototype.reset = function() {
  4600. this.mode_ = 'popOn';
  4601. // When in roll-up mode, the index of the last row that will
  4602. // actually display captions. If a caption is shifted to a row
  4603. // with a lower index than this, it is cleared from the display
  4604. // buffer
  4605. this.topRow_ = 0;
  4606. this.startPts_ = 0;
  4607. this.displayed_ = createDisplayBuffer();
  4608. this.nonDisplayed_ = createDisplayBuffer();
  4609. this.lastControlCode_ = null;
  4610. // Track row and column for proper line-breaking and spacing
  4611. this.column_ = 0;
  4612. this.row_ = BOTTOM_ROW;
  4613. // This variable holds currently-applied formatting
  4614. this.formatting_ = [];
  4615. };
  4616. /**
  4617. * Sets up control code and related constants for this instance
  4618. */
  4619. Cea608Stream.prototype.setConstants = function() {
  4620. // The following attributes have these uses:
  4621. // ext_ : char0 for mid-row codes, and the base for extended
  4622. // chars (ext_+0, ext_+1, and ext_+2 are char0s for
  4623. // extended codes)
  4624. // control_: char0 for control codes, except byte-shifted to the
  4625. // left so that we can do this.control_ | CONTROL_CODE
  4626. // offset_: char0 for tab offset codes
  4627. //
  4628. // It's also worth noting that control codes, and _only_ control codes,
  4629. // differ between field 1 and field2. Field 2 control codes are always
  4630. // their field 1 value plus 1. That's why there's the "| field" on the
  4631. // control value.
  4632. if (this.dataChannel_ === 0) {
  4633. this.BASE_ = 0x10;
  4634. this.EXT_ = 0x11;
  4635. this.CONTROL_ = (0x14 | this.field_) << 8;
  4636. this.OFFSET_ = 0x17;
  4637. } else if (this.dataChannel_ === 1) {
  4638. this.BASE_ = 0x18;
  4639. this.EXT_ = 0x19;
  4640. this.CONTROL_ = (0x1c | this.field_) << 8;
  4641. this.OFFSET_ = 0x1f;
  4642. }
  4643. // Constants for the LSByte command codes recognized by Cea608Stream. This
  4644. // list is not exhaustive. For a more comprehensive listing and semantics see
  4645. // http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
  4646. // Padding
  4647. this.PADDING_ = 0x0000;
  4648. // Pop-on Mode
  4649. this.RESUME_CAPTION_LOADING_ = this.CONTROL_ | 0x20;
  4650. this.END_OF_CAPTION_ = this.CONTROL_ | 0x2f;
  4651. // Roll-up Mode
  4652. this.ROLL_UP_2_ROWS_ = this.CONTROL_ | 0x25;
  4653. this.ROLL_UP_3_ROWS_ = this.CONTROL_ | 0x26;
  4654. this.ROLL_UP_4_ROWS_ = this.CONTROL_ | 0x27;
  4655. this.CARRIAGE_RETURN_ = this.CONTROL_ | 0x2d;
  4656. // paint-on mode (not supported)
  4657. this.RESUME_DIRECT_CAPTIONING_ = this.CONTROL_ | 0x29;
  4658. // Erasure
  4659. this.BACKSPACE_ = this.CONTROL_ | 0x21;
  4660. this.ERASE_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2c;
  4661. this.ERASE_NON_DISPLAYED_MEMORY_ = this.CONTROL_ | 0x2e;
  4662. };
  4663. /**
  4664. * Detects if the 2-byte packet data is a special character
  4665. *
  4666. * Special characters have a second byte in the range 0x30 to 0x3f,
  4667. * with the first byte being 0x11 (for data channel 1) or 0x19 (for
  4668. * data channel 2).
  4669. *
  4670. * @param {Integer} char0 The first byte
  4671. * @param {Integer} char1 The second byte
  4672. * @return {Boolean} Whether the 2 bytes are an special character
  4673. */
  4674. Cea608Stream.prototype.isSpecialCharacter = function(char0, char1) {
  4675. return (char0 === this.EXT_ && char1 >= 0x30 && char1 <= 0x3f);
  4676. };
  4677. /**
  4678. * Detects if the 2-byte packet data is an extended character
  4679. *
  4680. * Extended characters have a second byte in the range 0x20 to 0x3f,
  4681. * with the first byte being 0x12 or 0x13 (for data channel 1) or
  4682. * 0x1a or 0x1b (for data channel 2).
  4683. *
  4684. * @param {Integer} char0 The first byte
  4685. * @param {Integer} char1 The second byte
  4686. * @return {Boolean} Whether the 2 bytes are an extended character
  4687. */
  4688. Cea608Stream.prototype.isExtCharacter = function(char0, char1) {
  4689. return ((char0 === (this.EXT_ + 1) || char0 === (this.EXT_ + 2)) &&
  4690. (char1 >= 0x20 && char1 <= 0x3f));
  4691. };
  4692. /**
  4693. * Detects if the 2-byte packet is a mid-row code
  4694. *
  4695. * Mid-row codes have a second byte in the range 0x20 to 0x2f, with
  4696. * the first byte being 0x11 (for data channel 1) or 0x19 (for data
  4697. * channel 2).
  4698. *
  4699. * @param {Integer} char0 The first byte
  4700. * @param {Integer} char1 The second byte
  4701. * @return {Boolean} Whether the 2 bytes are a mid-row code
  4702. */
  4703. Cea608Stream.prototype.isMidRowCode = function(char0, char1) {
  4704. return (char0 === this.EXT_ && (char1 >= 0x20 && char1 <= 0x2f));
  4705. };
  4706. /**
  4707. * Detects if the 2-byte packet is an offset control code
  4708. *
  4709. * Offset control codes have a second byte in the range 0x21 to 0x23,
  4710. * with the first byte being 0x17 (for data channel 1) or 0x1f (for
  4711. * data channel 2).
  4712. *
  4713. * @param {Integer} char0 The first byte
  4714. * @param {Integer} char1 The second byte
  4715. * @return {Boolean} Whether the 2 bytes are an offset control code
  4716. */
  4717. Cea608Stream.prototype.isOffsetControlCode = function(char0, char1) {
  4718. return (char0 === this.OFFSET_ && (char1 >= 0x21 && char1 <= 0x23));
  4719. };
  4720. /**
  4721. * Detects if the 2-byte packet is a Preamble Address Code
  4722. *
  4723. * PACs have a first byte in the range 0x10 to 0x17 (for data channel 1)
  4724. * or 0x18 to 0x1f (for data channel 2), with the second byte in the
  4725. * range 0x40 to 0x7f.
  4726. *
  4727. * @param {Integer} char0 The first byte
  4728. * @param {Integer} char1 The second byte
  4729. * @return {Boolean} Whether the 2 bytes are a PAC
  4730. */
  4731. Cea608Stream.prototype.isPAC = function(char0, char1) {
  4732. return (char0 >= this.BASE_ && char0 < (this.BASE_ + 8) &&
  4733. (char1 >= 0x40 && char1 <= 0x7f));
  4734. };
  4735. /**
  4736. * Detects if a packet's second byte is in the range of a PAC color code
  4737. *
  4738. * PAC color codes have the second byte be in the range 0x40 to 0x4f, or
  4739. * 0x60 to 0x6f.
  4740. *
  4741. * @param {Integer} char1 The second byte
  4742. * @return {Boolean} Whether the byte is a color PAC
  4743. */
  4744. Cea608Stream.prototype.isColorPAC = function(char1) {
  4745. return ((char1 >= 0x40 && char1 <= 0x4f) || (char1 >= 0x60 && char1 <= 0x7f));
  4746. };
  4747. /**
  4748. * Detects if a single byte is in the range of a normal character
  4749. *
  4750. * Normal text bytes are in the range 0x20 to 0x7f.
  4751. *
  4752. * @param {Integer} char The byte
  4753. * @return {Boolean} Whether the byte is a normal character
  4754. */
  4755. Cea608Stream.prototype.isNormalChar = function(char) {
  4756. return (char >= 0x20 && char <= 0x7f);
  4757. };
  4758. // Adds the opening HTML tag for the passed character to the caption text,
  4759. // and keeps track of it for later closing
  4760. Cea608Stream.prototype.addFormatting = function(pts, format) {
  4761. this.formatting_ = this.formatting_.concat(format);
  4762. var text = format.reduce(function(text, format) {
  4763. return text + '<' + format + '>';
  4764. }, '');
  4765. this[this.mode_](pts, text);
  4766. };
  4767. // Adds HTML closing tags for current formatting to caption text and
  4768. // clears remembered formatting
  4769. Cea608Stream.prototype.clearFormatting = function(pts) {
  4770. if (!this.formatting_.length) {
  4771. return;
  4772. }
  4773. var text = this.formatting_.reverse().reduce(function(text, format) {
  4774. return text + '</' + format + '>';
  4775. }, '');
  4776. this.formatting_ = [];
  4777. this[this.mode_](pts, text);
  4778. };
  4779. // Mode Implementations
  4780. Cea608Stream.prototype.popOn = function(pts, text) {
  4781. var baseRow = this.nonDisplayed_[this.row_];
  4782. // buffer characters
  4783. baseRow += text;
  4784. this.nonDisplayed_[this.row_] = baseRow;
  4785. };
  4786. Cea608Stream.prototype.rollUp = function(pts, text) {
  4787. var baseRow = this.displayed_[BOTTOM_ROW];
  4788. baseRow += text;
  4789. this.displayed_[BOTTOM_ROW] = baseRow;
  4790. };
  4791. Cea608Stream.prototype.shiftRowsUp_ = function() {
  4792. var i;
  4793. // clear out inactive rows
  4794. for (i = 0; i < this.topRow_; i++) {
  4795. this.displayed_[i] = '';
  4796. }
  4797. // shift displayed rows up
  4798. for (i = this.topRow_; i < BOTTOM_ROW; i++) {
  4799. this.displayed_[i] = this.displayed_[i + 1];
  4800. }
  4801. // clear out the bottom row
  4802. this.displayed_[BOTTOM_ROW] = '';
  4803. };
  4804. // paintOn mode is not implemented
  4805. Cea608Stream.prototype.paintOn = function() {};
  4806. // exports
  4807. module.exports = {
  4808. CaptionStream: CaptionStream,
  4809. Cea608Stream: Cea608Stream
  4810. };
  4811. },{"../utils/stream":38}],29:[function(require,module,exports){
  4812. /**
  4813. * mux.js
  4814. *
  4815. * Copyright (c) 2015 Brightcove
  4816. * All rights reserved.
  4817. *
  4818. * A stream-based mp2t to mp4 converter. This utility can be used to
  4819. * deliver mp4s to a SourceBuffer on platforms that support native
  4820. * Media Source Extensions.
  4821. */
  4822. 'use strict';
  4823. var Stream = require('../utils/stream.js'),
  4824. CaptionStream = require('./caption-stream'),
  4825. StreamTypes = require('./stream-types'),
  4826. TimestampRolloverStream = require('./timestamp-rollover-stream').TimestampRolloverStream;
  4827. var m2tsStreamTypes = require('./stream-types.js');
  4828. // object types
  4829. var TransportPacketStream, TransportParseStream, ElementaryStream;
  4830. // constants
  4831. var
  4832. MP2T_PACKET_LENGTH = 188, // bytes
  4833. SYNC_BYTE = 0x47;
  4834. /**
  4835. * Splits an incoming stream of binary data into MPEG-2 Transport
  4836. * Stream packets.
  4837. */
  4838. TransportPacketStream = function() {
  4839. var
  4840. buffer = new Uint8Array(MP2T_PACKET_LENGTH),
  4841. bytesInBuffer = 0;
  4842. TransportPacketStream.prototype.init.call(this);
  4843. // Deliver new bytes to the stream.
  4844. this.push = function(bytes) {
  4845. var
  4846. startIndex = 0,
  4847. endIndex = MP2T_PACKET_LENGTH,
  4848. everything;
  4849. // If there are bytes remaining from the last segment, prepend them to the
  4850. // bytes that were pushed in
  4851. if (bytesInBuffer) {
  4852. everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
  4853. everything.set(buffer.subarray(0, bytesInBuffer));
  4854. everything.set(bytes, bytesInBuffer);
  4855. bytesInBuffer = 0;
  4856. } else {
  4857. everything = bytes;
  4858. }
  4859. // While we have enough data for a packet
  4860. while (endIndex < everything.byteLength) {
  4861. // Look for a pair of start and end sync bytes in the data..
  4862. if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
  4863. // We found a packet so emit it and jump one whole packet forward in
  4864. // the stream
  4865. this.trigger('data', everything.subarray(startIndex, endIndex));
  4866. startIndex += MP2T_PACKET_LENGTH;
  4867. endIndex += MP2T_PACKET_LENGTH;
  4868. continue;
  4869. }
  4870. // If we get here, we have somehow become de-synchronized and we need to step
  4871. // forward one byte at a time until we find a pair of sync bytes that denote
  4872. // a packet
  4873. startIndex++;
  4874. endIndex++;
  4875. }
  4876. // If there was some data left over at the end of the segment that couldn't
  4877. // possibly be a whole packet, keep it because it might be the start of a packet
  4878. // that continues in the next segment
  4879. if (startIndex < everything.byteLength) {
  4880. buffer.set(everything.subarray(startIndex), 0);
  4881. bytesInBuffer = everything.byteLength - startIndex;
  4882. }
  4883. };
  4884. this.flush = function() {
  4885. // If the buffer contains a whole packet when we are being flushed, emit it
  4886. // and empty the buffer. Otherwise hold onto the data because it may be
  4887. // important for decoding the next segment
  4888. if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
  4889. this.trigger('data', buffer);
  4890. bytesInBuffer = 0;
  4891. }
  4892. this.trigger('done');
  4893. };
  4894. };
  4895. TransportPacketStream.prototype = new Stream();
  4896. /**
  4897. * Accepts an MP2T TransportPacketStream and emits data events with parsed
  4898. * forms of the individual transport stream packets.
  4899. */
  4900. TransportParseStream = function() {
  4901. var parsePsi, parsePat, parsePmt, self;
  4902. TransportParseStream.prototype.init.call(this);
  4903. self = this;
  4904. this.packetsWaitingForPmt = [];
  4905. this.programMapTable = undefined;
  4906. parsePsi = function(payload, psi) {
  4907. var offset = 0;
  4908. // PSI packets may be split into multiple sections and those
  4909. // sections may be split into multiple packets. If a PSI
  4910. // section starts in this packet, the payload_unit_start_indicator
  4911. // will be true and the first byte of the payload will indicate
  4912. // the offset from the current position to the start of the
  4913. // section.
  4914. if (psi.payloadUnitStartIndicator) {
  4915. offset += payload[offset] + 1;
  4916. }
  4917. if (psi.type === 'pat') {
  4918. parsePat(payload.subarray(offset), psi);
  4919. } else {
  4920. parsePmt(payload.subarray(offset), psi);
  4921. }
  4922. };
  4923. parsePat = function(payload, pat) {
  4924. pat.section_number = payload[7]; // eslint-disable-line camelcase
  4925. pat.last_section_number = payload[8]; // eslint-disable-line camelcase
  4926. // skip the PSI header and parse the first PMT entry
  4927. self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
  4928. pat.pmtPid = self.pmtPid;
  4929. };
  4930. /**
  4931. * Parse out the relevant fields of a Program Map Table (PMT).
  4932. * @param payload {Uint8Array} the PMT-specific portion of an MP2T
  4933. * packet. The first byte in this array should be the table_id
  4934. * field.
  4935. * @param pmt {object} the object that should be decorated with
  4936. * fields parsed from the PMT.
  4937. */
  4938. parsePmt = function(payload, pmt) {
  4939. var sectionLength, tableEnd, programInfoLength, offset;
  4940. // PMTs can be sent ahead of the time when they should actually
  4941. // take effect. We don't believe this should ever be the case
  4942. // for HLS but we'll ignore "forward" PMT declarations if we see
  4943. // them. Future PMT declarations have the current_next_indicator
  4944. // set to zero.
  4945. if (!(payload[5] & 0x01)) {
  4946. return;
  4947. }
  4948. // overwrite any existing program map table
  4949. self.programMapTable = {
  4950. video: null,
  4951. audio: null,
  4952. 'timed-metadata': {}
  4953. };
  4954. // the mapping table ends at the end of the current section
  4955. sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
  4956. tableEnd = 3 + sectionLength - 4;
  4957. // to determine where the table is, we have to figure out how
  4958. // long the program info descriptors are
  4959. programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];
  4960. // advance the offset to the first entry in the mapping table
  4961. offset = 12 + programInfoLength;
  4962. while (offset < tableEnd) {
  4963. var streamType = payload[offset];
  4964. var pid = (payload[offset + 1] & 0x1F) << 8 | payload[offset + 2];
  4965. // only map a single elementary_pid for audio and video stream types
  4966. // TODO: should this be done for metadata too? for now maintain behavior of
  4967. // multiple metadata streams
  4968. if (streamType === StreamTypes.H264_STREAM_TYPE &&
  4969. self.programMapTable.video === null) {
  4970. self.programMapTable.video = pid;
  4971. } else if (streamType === StreamTypes.ADTS_STREAM_TYPE &&
  4972. self.programMapTable.audio === null) {
  4973. self.programMapTable.audio = pid;
  4974. } else if (streamType === StreamTypes.METADATA_STREAM_TYPE) {
  4975. // map pid to stream type for metadata streams
  4976. self.programMapTable['timed-metadata'][pid] = streamType;
  4977. }
  4978. // move to the next table entry
  4979. // skip past the elementary stream descriptors, if present
  4980. offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
  4981. }
  4982. // record the map on the packet as well
  4983. pmt.programMapTable = self.programMapTable;
  4984. };
  4985. /**
  4986. * Deliver a new MP2T packet to the stream.
  4987. */
  4988. this.push = function(packet) {
  4989. var
  4990. result = {},
  4991. offset = 4;
  4992. result.payloadUnitStartIndicator = !!(packet[1] & 0x40);
  4993. // pid is a 13-bit field starting at the last bit of packet[1]
  4994. result.pid = packet[1] & 0x1f;
  4995. result.pid <<= 8;
  4996. result.pid |= packet[2];
  4997. // if an adaption field is present, its length is specified by the
  4998. // fifth byte of the TS packet header. The adaptation field is
  4999. // used to add stuffing to PES packets that don't fill a complete
  5000. // TS packet, and to specify some forms of timing and control data
  5001. // that we do not currently use.
  5002. if (((packet[3] & 0x30) >>> 4) > 0x01) {
  5003. offset += packet[offset] + 1;
  5004. }
  5005. // parse the rest of the packet based on the type
  5006. if (result.pid === 0) {
  5007. result.type = 'pat';
  5008. parsePsi(packet.subarray(offset), result);
  5009. this.trigger('data', result);
  5010. } else if (result.pid === this.pmtPid) {
  5011. result.type = 'pmt';
  5012. parsePsi(packet.subarray(offset), result);
  5013. this.trigger('data', result);
  5014. // if there are any packets waiting for a PMT to be found, process them now
  5015. while (this.packetsWaitingForPmt.length) {
  5016. this.processPes_.apply(this, this.packetsWaitingForPmt.shift());
  5017. }
  5018. } else if (this.programMapTable === undefined) {
  5019. // When we have not seen a PMT yet, defer further processing of
  5020. // PES packets until one has been parsed
  5021. this.packetsWaitingForPmt.push([packet, offset, result]);
  5022. } else {
  5023. this.processPes_(packet, offset, result);
  5024. }
  5025. };
  5026. this.processPes_ = function(packet, offset, result) {
  5027. // set the appropriate stream type
  5028. if (result.pid === this.programMapTable.video) {
  5029. result.streamType = StreamTypes.H264_STREAM_TYPE;
  5030. } else if (result.pid === this.programMapTable.audio) {
  5031. result.streamType = StreamTypes.ADTS_STREAM_TYPE;
  5032. } else {
  5033. // if not video or audio, it is timed-metadata or unknown
  5034. // if unknown, streamType will be undefined
  5035. result.streamType = this.programMapTable['timed-metadata'][result.pid];
  5036. }
  5037. result.type = 'pes';
  5038. result.data = packet.subarray(offset);
  5039. this.trigger('data', result);
  5040. };
  5041. };
  5042. TransportParseStream.prototype = new Stream();
  5043. TransportParseStream.STREAM_TYPES = {
  5044. h264: 0x1b,
  5045. adts: 0x0f
  5046. };
  5047. /**
  5048. * Reconsistutes program elementary stream (PES) packets from parsed
  5049. * transport stream packets. That is, if you pipe an
  5050. * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
  5051. * events will be events which capture the bytes for individual PES
  5052. * packets plus relevant metadata that has been extracted from the
  5053. * container.
  5054. */
  5055. ElementaryStream = function() {
  5056. var
  5057. self = this,
  5058. // PES packet fragments
  5059. video = {
  5060. data: [],
  5061. size: 0
  5062. },
  5063. audio = {
  5064. data: [],
  5065. size: 0
  5066. },
  5067. timedMetadata = {
  5068. data: [],
  5069. size: 0
  5070. },
  5071. parsePes = function(payload, pes) {
  5072. var ptsDtsFlags;
  5073. // get the packet length, this will be 0 for video
  5074. pes.packetLength = 6 + ((payload[4] << 8) | payload[5]);
  5075. // find out if this packets starts a new keyframe
  5076. pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
  5077. // PES packets may be annotated with a PTS value, or a PTS value
  5078. // and a DTS value. Determine what combination of values is
  5079. // available to work with.
  5080. ptsDtsFlags = payload[7];
  5081. // PTS and DTS are normally stored as a 33-bit number. Javascript
  5082. // performs all bitwise operations on 32-bit integers but javascript
  5083. // supports a much greater range (52-bits) of integer using standard
  5084. // mathematical operations.
  5085. // We construct a 31-bit value using bitwise operators over the 31
  5086. // most significant bits and then multiply by 4 (equal to a left-shift
  5087. // of 2) before we add the final 2 least significant bits of the
  5088. // timestamp (equal to an OR.)
  5089. if (ptsDtsFlags & 0xC0) {
  5090. // the PTS and DTS are not written out directly. For information
  5091. // on how they are encoded, see
  5092. // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  5093. pes.pts = (payload[9] & 0x0E) << 27 |
  5094. (payload[10] & 0xFF) << 20 |
  5095. (payload[11] & 0xFE) << 12 |
  5096. (payload[12] & 0xFF) << 5 |
  5097. (payload[13] & 0xFE) >>> 3;
  5098. pes.pts *= 4; // Left shift by 2
  5099. pes.pts += (payload[13] & 0x06) >>> 1; // OR by the two LSBs
  5100. pes.dts = pes.pts;
  5101. if (ptsDtsFlags & 0x40) {
  5102. pes.dts = (payload[14] & 0x0E) << 27 |
  5103. (payload[15] & 0xFF) << 20 |
  5104. (payload[16] & 0xFE) << 12 |
  5105. (payload[17] & 0xFF) << 5 |
  5106. (payload[18] & 0xFE) >>> 3;
  5107. pes.dts *= 4; // Left shift by 2
  5108. pes.dts += (payload[18] & 0x06) >>> 1; // OR by the two LSBs
  5109. }
  5110. }
  5111. // the data section starts immediately after the PES header.
  5112. // pes_header_data_length specifies the number of header bytes
  5113. // that follow the last byte of the field.
  5114. pes.data = payload.subarray(9 + payload[8]);
  5115. },
  5116. flushStream = function(stream, type, forceFlush) {
  5117. var
  5118. packetData = new Uint8Array(stream.size),
  5119. event = {
  5120. type: type
  5121. },
  5122. i = 0,
  5123. offset = 0,
  5124. packetFlushable = false,
  5125. fragment;
  5126. // do nothing if there is not enough buffered data for a complete
  5127. // PES header
  5128. if (!stream.data.length || stream.size < 9) {
  5129. return;
  5130. }
  5131. event.trackId = stream.data[0].pid;
  5132. // reassemble the packet
  5133. for (i = 0; i < stream.data.length; i++) {
  5134. fragment = stream.data[i];
  5135. packetData.set(fragment.data, offset);
  5136. offset += fragment.data.byteLength;
  5137. }
  5138. // parse assembled packet's PES header
  5139. parsePes(packetData, event);
  5140. // non-video PES packets MUST have a non-zero PES_packet_length
  5141. // check that there is enough stream data to fill the packet
  5142. packetFlushable = type === 'video' || event.packetLength <= stream.size;
  5143. // flush pending packets if the conditions are right
  5144. if (forceFlush || packetFlushable) {
  5145. stream.size = 0;
  5146. stream.data.length = 0;
  5147. }
  5148. // only emit packets that are complete. this is to avoid assembling
  5149. // incomplete PES packets due to poor segmentation
  5150. if (packetFlushable) {
  5151. self.trigger('data', event);
  5152. }
  5153. };
  5154. ElementaryStream.prototype.init.call(this);
  5155. this.push = function(data) {
  5156. ({
  5157. pat: function() {
  5158. // we have to wait for the PMT to arrive as well before we
  5159. // have any meaningful metadata
  5160. },
  5161. pes: function() {
  5162. var stream, streamType;
  5163. switch (data.streamType) {
  5164. case StreamTypes.H264_STREAM_TYPE:
  5165. case m2tsStreamTypes.H264_STREAM_TYPE:
  5166. stream = video;
  5167. streamType = 'video';
  5168. break;
  5169. case StreamTypes.ADTS_STREAM_TYPE:
  5170. stream = audio;
  5171. streamType = 'audio';
  5172. break;
  5173. case StreamTypes.METADATA_STREAM_TYPE:
  5174. stream = timedMetadata;
  5175. streamType = 'timed-metadata';
  5176. break;
  5177. default:
  5178. // ignore unknown stream types
  5179. return;
  5180. }
  5181. // if a new packet is starting, we can flush the completed
  5182. // packet
  5183. if (data.payloadUnitStartIndicator) {
  5184. flushStream(stream, streamType, true);
  5185. }
  5186. // buffer this fragment until we are sure we've received the
  5187. // complete payload
  5188. stream.data.push(data);
  5189. stream.size += data.data.byteLength;
  5190. },
  5191. pmt: function() {
  5192. var
  5193. event = {
  5194. type: 'metadata',
  5195. tracks: []
  5196. },
  5197. programMapTable = data.programMapTable;
  5198. // translate audio and video streams to tracks
  5199. if (programMapTable.video !== null) {
  5200. event.tracks.push({
  5201. timelineStartInfo: {
  5202. baseMediaDecodeTime: 0
  5203. },
  5204. id: +programMapTable.video,
  5205. codec: 'avc',
  5206. type: 'video'
  5207. });
  5208. }
  5209. if (programMapTable.audio !== null) {
  5210. event.tracks.push({
  5211. timelineStartInfo: {
  5212. baseMediaDecodeTime: 0
  5213. },
  5214. id: +programMapTable.audio,
  5215. codec: 'adts',
  5216. type: 'audio'
  5217. });
  5218. }
  5219. self.trigger('data', event);
  5220. }
  5221. })[data.type]();
  5222. };
  5223. /**
  5224. * Flush any remaining input. Video PES packets may be of variable
  5225. * length. Normally, the start of a new video packet can trigger the
  5226. * finalization of the previous packet. That is not possible if no
  5227. * more video is forthcoming, however. In that case, some other
  5228. * mechanism (like the end of the file) has to be employed. When it is
  5229. * clear that no additional data is forthcoming, calling this method
  5230. * will flush the buffered packets.
  5231. */
  5232. this.flush = function() {
  5233. // !!THIS ORDER IS IMPORTANT!!
  5234. // video first then audio
  5235. flushStream(video, 'video');
  5236. flushStream(audio, 'audio');
  5237. flushStream(timedMetadata, 'timed-metadata');
  5238. this.trigger('done');
  5239. };
  5240. };
  5241. ElementaryStream.prototype = new Stream();
  5242. var m2ts = {
  5243. PAT_PID: 0x0000,
  5244. MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  5245. TransportPacketStream: TransportPacketStream,
  5246. TransportParseStream: TransportParseStream,
  5247. ElementaryStream: ElementaryStream,
  5248. TimestampRolloverStream: TimestampRolloverStream,
  5249. CaptionStream: CaptionStream.CaptionStream,
  5250. Cea608Stream: CaptionStream.Cea608Stream,
  5251. MetadataStream: require('./metadata-stream')
  5252. };
  5253. for (var type in StreamTypes) {
  5254. if (StreamTypes.hasOwnProperty(type)) {
  5255. m2ts[type] = StreamTypes[type];
  5256. }
  5257. }
  5258. module.exports = m2ts;
  5259. },{"../utils/stream.js":38,"./caption-stream":28,"./metadata-stream":30,"./stream-types":31,"./stream-types.js":31,"./timestamp-rollover-stream":32}],30:[function(require,module,exports){
  5260. /**
  5261. * Accepts program elementary stream (PES) data events and parses out
  5262. * ID3 metadata from them, if present.
  5263. * @see http://id3.org/id3v2.3.0
  5264. */
  5265. 'use strict';
  5266. var
  5267. Stream = require('../utils/stream'),
  5268. StreamTypes = require('./stream-types'),
  5269. // return a percent-encoded representation of the specified byte range
  5270. // @see http://en.wikipedia.org/wiki/Percent-encoding
  5271. percentEncode = function(bytes, start, end) {
  5272. var i, result = '';
  5273. for (i = start; i < end; i++) {
  5274. result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
  5275. }
  5276. return result;
  5277. },
  5278. // return the string representation of the specified byte range,
  5279. // interpreted as UTf-8.
  5280. parseUtf8 = function(bytes, start, end) {
  5281. return decodeURIComponent(percentEncode(bytes, start, end));
  5282. },
  5283. // return the string representation of the specified byte range,
  5284. // interpreted as ISO-8859-1.
  5285. parseIso88591 = function(bytes, start, end) {
  5286. return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  5287. },
  5288. parseSyncSafeInteger = function(data) {
  5289. return (data[0] << 21) |
  5290. (data[1] << 14) |
  5291. (data[2] << 7) |
  5292. (data[3]);
  5293. },
  5294. tagParsers = {
  5295. TXXX: function(tag) {
  5296. var i;
  5297. if (tag.data[0] !== 3) {
  5298. // ignore frames with unrecognized character encodings
  5299. return;
  5300. }
  5301. for (i = 1; i < tag.data.length; i++) {
  5302. if (tag.data[i] === 0) {
  5303. // parse the text fields
  5304. tag.description = parseUtf8(tag.data, 1, i);
  5305. // do not include the null terminator in the tag value
  5306. tag.value = parseUtf8(tag.data, i + 1, tag.data.length).replace(/\0*$/, '');
  5307. break;
  5308. }
  5309. }
  5310. tag.data = tag.value;
  5311. },
  5312. WXXX: function(tag) {
  5313. var i;
  5314. if (tag.data[0] !== 3) {
  5315. // ignore frames with unrecognized character encodings
  5316. return;
  5317. }
  5318. for (i = 1; i < tag.data.length; i++) {
  5319. if (tag.data[i] === 0) {
  5320. // parse the description and URL fields
  5321. tag.description = parseUtf8(tag.data, 1, i);
  5322. tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
  5323. break;
  5324. }
  5325. }
  5326. },
  5327. PRIV: function(tag) {
  5328. var i;
  5329. for (i = 0; i < tag.data.length; i++) {
  5330. if (tag.data[i] === 0) {
  5331. // parse the description and URL fields
  5332. tag.owner = parseIso88591(tag.data, 0, i);
  5333. break;
  5334. }
  5335. }
  5336. tag.privateData = tag.data.subarray(i + 1);
  5337. tag.data = tag.privateData;
  5338. }
  5339. },
  5340. MetadataStream;
  5341. MetadataStream = function(options) {
  5342. var
  5343. settings = {
  5344. debug: !!(options && options.debug),
  5345. // the bytes of the program-level descriptor field in MP2T
  5346. // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
  5347. // program element descriptors"
  5348. descriptor: options && options.descriptor
  5349. },
  5350. // the total size in bytes of the ID3 tag being parsed
  5351. tagSize = 0,
  5352. // tag data that is not complete enough to be parsed
  5353. buffer = [],
  5354. // the total number of bytes currently in the buffer
  5355. bufferSize = 0,
  5356. i;
  5357. MetadataStream.prototype.init.call(this);
  5358. // calculate the text track in-band metadata track dispatch type
  5359. // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  5360. this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  5361. if (settings.descriptor) {
  5362. for (i = 0; i < settings.descriptor.length; i++) {
  5363. this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
  5364. }
  5365. }
  5366. this.push = function(chunk) {
  5367. var tag, frameStart, frameSize, frame, i, frameHeader;
  5368. if (chunk.type !== 'timed-metadata') {
  5369. return;
  5370. }
  5371. // if data_alignment_indicator is set in the PES header,
  5372. // we must have the start of a new ID3 tag. Assume anything
  5373. // remaining in the buffer was malformed and throw it out
  5374. if (chunk.dataAlignmentIndicator) {
  5375. bufferSize = 0;
  5376. buffer.length = 0;
  5377. }
  5378. // ignore events that don't look like ID3 data
  5379. if (buffer.length === 0 &&
  5380. (chunk.data.length < 10 ||
  5381. chunk.data[0] !== 'I'.charCodeAt(0) ||
  5382. chunk.data[1] !== 'D'.charCodeAt(0) ||
  5383. chunk.data[2] !== '3'.charCodeAt(0))) {
  5384. if (settings.debug) {
  5385. // eslint-disable-next-line no-console
  5386. console.log('Skipping unrecognized metadata packet');
  5387. }
  5388. return;
  5389. }
  5390. // add this chunk to the data we've collected so far
  5391. buffer.push(chunk);
  5392. bufferSize += chunk.data.byteLength;
  5393. // grab the size of the entire frame from the ID3 header
  5394. if (buffer.length === 1) {
  5395. // the frame size is transmitted as a 28-bit integer in the
  5396. // last four bytes of the ID3 header.
  5397. // The most significant bit of each byte is dropped and the
  5398. // results concatenated to recover the actual value.
  5399. tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));
  5400. // ID3 reports the tag size excluding the header but it's more
  5401. // convenient for our comparisons to include it
  5402. tagSize += 10;
  5403. }
  5404. // if the entire frame has not arrived, wait for more data
  5405. if (bufferSize < tagSize) {
  5406. return;
  5407. }
  5408. // collect the entire frame so it can be parsed
  5409. tag = {
  5410. data: new Uint8Array(tagSize),
  5411. frames: [],
  5412. pts: buffer[0].pts,
  5413. dts: buffer[0].dts
  5414. };
  5415. for (i = 0; i < tagSize;) {
  5416. tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
  5417. i += buffer[0].data.byteLength;
  5418. bufferSize -= buffer[0].data.byteLength;
  5419. buffer.shift();
  5420. }
  5421. // find the start of the first frame and the end of the tag
  5422. frameStart = 10;
  5423. if (tag.data[5] & 0x40) {
  5424. // advance the frame start past the extended header
  5425. frameStart += 4; // header size field
  5426. frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));
  5427. // clip any padding off the end
  5428. tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
  5429. }
  5430. // parse one or more ID3 frames
  5431. // http://id3.org/id3v2.3.0#ID3v2_frame_overview
  5432. do {
  5433. // determine the number of bytes in this frame
  5434. frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
  5435. if (frameSize < 1) {
  5436. // eslint-disable-next-line no-console
  5437. return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
  5438. }
  5439. frameHeader = String.fromCharCode(tag.data[frameStart],
  5440. tag.data[frameStart + 1],
  5441. tag.data[frameStart + 2],
  5442. tag.data[frameStart + 3]);
  5443. frame = {
  5444. id: frameHeader,
  5445. data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
  5446. };
  5447. frame.key = frame.id;
  5448. if (tagParsers[frame.id]) {
  5449. tagParsers[frame.id](frame);
  5450. // handle the special PRIV frame used to indicate the start
  5451. // time for raw AAC data
  5452. if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
  5453. var
  5454. d = frame.data,
  5455. size = ((d[3] & 0x01) << 30) |
  5456. (d[4] << 22) |
  5457. (d[5] << 14) |
  5458. (d[6] << 6) |
  5459. (d[7] >>> 2);
  5460. size *= 4;
  5461. size += d[7] & 0x03;
  5462. frame.timeStamp = size;
  5463. // in raw AAC, all subsequent data will be timestamped based
  5464. // on the value of this frame
  5465. // we couldn't have known the appropriate pts and dts before
  5466. // parsing this ID3 tag so set those values now
  5467. if (tag.pts === undefined && tag.dts === undefined) {
  5468. tag.pts = frame.timeStamp;
  5469. tag.dts = frame.timeStamp;
  5470. }
  5471. this.trigger('timestamp', frame);
  5472. }
  5473. }
  5474. tag.frames.push(frame);
  5475. frameStart += 10; // advance past the frame header
  5476. frameStart += frameSize; // advance past the frame body
  5477. } while (frameStart < tagSize);
  5478. this.trigger('data', tag);
  5479. };
  5480. };
  5481. MetadataStream.prototype = new Stream();
  5482. module.exports = MetadataStream;
  5483. },{"../utils/stream":38,"./stream-types":31}],31:[function(require,module,exports){
  5484. 'use strict';
  5485. module.exports = {
  5486. H264_STREAM_TYPE: 0x1B,
  5487. ADTS_STREAM_TYPE: 0x0F,
  5488. METADATA_STREAM_TYPE: 0x15
  5489. };
  5490. },{}],32:[function(require,module,exports){
  5491. /**
  5492. * mux.js
  5493. *
  5494. * Copyright (c) 2016 Brightcove
  5495. * All rights reserved.
  5496. *
  5497. * Accepts program elementary stream (PES) data events and corrects
  5498. * decode and presentation time stamps to account for a rollover
  5499. * of the 33 bit value.
  5500. */
  5501. 'use strict';
  5502. var Stream = require('../utils/stream');
  5503. var MAX_TS = 8589934592;
  5504. var RO_THRESH = 4294967296;
  5505. var handleRollover = function(value, reference) {
  5506. var direction = 1;
  5507. if (value > reference) {
  5508. // If the current timestamp value is greater than our reference timestamp and we detect a
  5509. // timestamp rollover, this means the roll over is happening in the opposite direction.
  5510. // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
  5511. // point will be set to a small number, e.g. 1. The user then seeks backwards over the
  5512. // rollover point. In loading this segment, the timestamp values will be very large,
  5513. // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
  5514. // the time stamp to be `value - 2^33`.
  5515. direction = -1;
  5516. }
  5517. // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  5518. // cause an incorrect adjustment.
  5519. while (Math.abs(reference - value) > RO_THRESH) {
  5520. value += (direction * MAX_TS);
  5521. }
  5522. return value;
  5523. };
  5524. var TimestampRolloverStream = function(type) {
  5525. var lastDTS, referenceDTS;
  5526. TimestampRolloverStream.prototype.init.call(this);
  5527. this.type_ = type;
  5528. this.push = function(data) {
  5529. if (data.type !== this.type_) {
  5530. return;
  5531. }
  5532. if (referenceDTS === undefined) {
  5533. referenceDTS = data.dts;
  5534. }
  5535. data.dts = handleRollover(data.dts, referenceDTS);
  5536. data.pts = handleRollover(data.pts, referenceDTS);
  5537. lastDTS = data.dts;
  5538. this.trigger('data', data);
  5539. };
  5540. this.flush = function() {
  5541. referenceDTS = lastDTS;
  5542. this.trigger('done');
  5543. };
  5544. this.discontinuity = function() {
  5545. referenceDTS = void 0;
  5546. lastDTS = void 0;
  5547. };
  5548. };
  5549. TimestampRolloverStream.prototype = new Stream();
  5550. module.exports = {
  5551. TimestampRolloverStream: TimestampRolloverStream,
  5552. handleRollover: handleRollover
  5553. };
  5554. },{"../utils/stream":38}],33:[function(require,module,exports){
  5555. module.exports = {
  5556. generator: require('./mp4-generator'),
  5557. Transmuxer: require('./transmuxer').Transmuxer,
  5558. AudioSegmentStream: require('./transmuxer').AudioSegmentStream,
  5559. VideoSegmentStream: require('./transmuxer').VideoSegmentStream
  5560. };
  5561. },{"./mp4-generator":34,"./transmuxer":35}],34:[function(require,module,exports){
  5562. /**
  5563. * mux.js
  5564. *
  5565. * Copyright (c) 2015 Brightcove
  5566. * All rights reserved.
  5567. *
  5568. * Functions that generate fragmented MP4s suitable for use with Media
  5569. * Source Extensions.
  5570. */
  5571. 'use strict';
  5572. var UINT32_MAX = Math.pow(2, 32) - 1;
  5573. var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
  5574. trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
  5575. trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,
  5576. AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;
  5577. // pre-calculate constants
  5578. (function() {
  5579. var i;
  5580. types = {
  5581. avc1: [], // codingname
  5582. avcC: [],
  5583. btrt: [],
  5584. dinf: [],
  5585. dref: [],
  5586. esds: [],
  5587. ftyp: [],
  5588. hdlr: [],
  5589. mdat: [],
  5590. mdhd: [],
  5591. mdia: [],
  5592. mfhd: [],
  5593. minf: [],
  5594. moof: [],
  5595. moov: [],
  5596. mp4a: [], // codingname
  5597. mvex: [],
  5598. mvhd: [],
  5599. sdtp: [],
  5600. smhd: [],
  5601. stbl: [],
  5602. stco: [],
  5603. stsc: [],
  5604. stsd: [],
  5605. stsz: [],
  5606. stts: [],
  5607. styp: [],
  5608. tfdt: [],
  5609. tfhd: [],
  5610. traf: [],
  5611. trak: [],
  5612. trun: [],
  5613. trex: [],
  5614. tkhd: [],
  5615. vmhd: []
  5616. };
  5617. // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
  5618. // don't throw an error
  5619. if (typeof Uint8Array === 'undefined') {
  5620. return;
  5621. }
  5622. for (i in types) {
  5623. if (types.hasOwnProperty(i)) {
  5624. types[i] = [
  5625. i.charCodeAt(0),
  5626. i.charCodeAt(1),
  5627. i.charCodeAt(2),
  5628. i.charCodeAt(3)
  5629. ];
  5630. }
  5631. }
  5632. MAJOR_BRAND = new Uint8Array([
  5633. 'i'.charCodeAt(0),
  5634. 's'.charCodeAt(0),
  5635. 'o'.charCodeAt(0),
  5636. 'm'.charCodeAt(0)
  5637. ]);
  5638. AVC1_BRAND = new Uint8Array([
  5639. 'a'.charCodeAt(0),
  5640. 'v'.charCodeAt(0),
  5641. 'c'.charCodeAt(0),
  5642. '1'.charCodeAt(0)
  5643. ]);
  5644. MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
  5645. VIDEO_HDLR = new Uint8Array([
  5646. 0x00, // version 0
  5647. 0x00, 0x00, 0x00, // flags
  5648. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5649. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  5650. 0x00, 0x00, 0x00, 0x00, // reserved
  5651. 0x00, 0x00, 0x00, 0x00, // reserved
  5652. 0x00, 0x00, 0x00, 0x00, // reserved
  5653. 0x56, 0x69, 0x64, 0x65,
  5654. 0x6f, 0x48, 0x61, 0x6e,
  5655. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  5656. ]);
  5657. AUDIO_HDLR = new Uint8Array([
  5658. 0x00, // version 0
  5659. 0x00, 0x00, 0x00, // flags
  5660. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5661. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  5662. 0x00, 0x00, 0x00, 0x00, // reserved
  5663. 0x00, 0x00, 0x00, 0x00, // reserved
  5664. 0x00, 0x00, 0x00, 0x00, // reserved
  5665. 0x53, 0x6f, 0x75, 0x6e,
  5666. 0x64, 0x48, 0x61, 0x6e,
  5667. 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  5668. ]);
  5669. HDLR_TYPES = {
  5670. video: VIDEO_HDLR,
  5671. audio: AUDIO_HDLR
  5672. };
  5673. DREF = new Uint8Array([
  5674. 0x00, // version 0
  5675. 0x00, 0x00, 0x00, // flags
  5676. 0x00, 0x00, 0x00, 0x01, // entry_count
  5677. 0x00, 0x00, 0x00, 0x0c, // entry_size
  5678. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  5679. 0x00, // version 0
  5680. 0x00, 0x00, 0x01 // entry_flags
  5681. ]);
  5682. SMHD = new Uint8Array([
  5683. 0x00, // version
  5684. 0x00, 0x00, 0x00, // flags
  5685. 0x00, 0x00, // balance, 0 means centered
  5686. 0x00, 0x00 // reserved
  5687. ]);
  5688. STCO = new Uint8Array([
  5689. 0x00, // version
  5690. 0x00, 0x00, 0x00, // flags
  5691. 0x00, 0x00, 0x00, 0x00 // entry_count
  5692. ]);
  5693. STSC = STCO;
  5694. STSZ = new Uint8Array([
  5695. 0x00, // version
  5696. 0x00, 0x00, 0x00, // flags
  5697. 0x00, 0x00, 0x00, 0x00, // sample_size
  5698. 0x00, 0x00, 0x00, 0x00 // sample_count
  5699. ]);
  5700. STTS = STCO;
  5701. VMHD = new Uint8Array([
  5702. 0x00, // version
  5703. 0x00, 0x00, 0x01, // flags
  5704. 0x00, 0x00, // graphicsmode
  5705. 0x00, 0x00,
  5706. 0x00, 0x00,
  5707. 0x00, 0x00 // opcolor
  5708. ]);
  5709. }());
  5710. box = function(type) {
  5711. var
  5712. payload = [],
  5713. size = 0,
  5714. i,
  5715. result,
  5716. view;
  5717. for (i = 1; i < arguments.length; i++) {
  5718. payload.push(arguments[i]);
  5719. }
  5720. i = payload.length;
  5721. // calculate the total size we need to allocate
  5722. while (i--) {
  5723. size += payload[i].byteLength;
  5724. }
  5725. result = new Uint8Array(size + 8);
  5726. view = new DataView(result.buffer, result.byteOffset, result.byteLength);
  5727. view.setUint32(0, result.byteLength);
  5728. result.set(type, 4);
  5729. // copy the payload into the result
  5730. for (i = 0, size = 8; i < payload.length; i++) {
  5731. result.set(payload[i], size);
  5732. size += payload[i].byteLength;
  5733. }
  5734. return result;
  5735. };
  5736. dinf = function() {
  5737. return box(types.dinf, box(types.dref, DREF));
  5738. };
  5739. esds = function(track) {
  5740. return box(types.esds, new Uint8Array([
  5741. 0x00, // version
  5742. 0x00, 0x00, 0x00, // flags
  5743. // ES_Descriptor
  5744. 0x03, // tag, ES_DescrTag
  5745. 0x19, // length
  5746. 0x00, 0x00, // ES_ID
  5747. 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
  5748. // DecoderConfigDescriptor
  5749. 0x04, // tag, DecoderConfigDescrTag
  5750. 0x11, // length
  5751. 0x40, // object type
  5752. 0x15, // streamType
  5753. 0x00, 0x06, 0x00, // bufferSizeDB
  5754. 0x00, 0x00, 0xda, 0xc0, // maxBitrate
  5755. 0x00, 0x00, 0xda, 0xc0, // avgBitrate
  5756. // DecoderSpecificInfo
  5757. 0x05, // tag, DecoderSpecificInfoTag
  5758. 0x02, // length
  5759. // ISO/IEC 14496-3, AudioSpecificConfig
  5760. // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
  5761. (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
  5762. (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
  5763. 0x06, 0x01, 0x02 // GASpecificConfig
  5764. ]));
  5765. };
  5766. ftyp = function() {
  5767. return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
  5768. };
  5769. hdlr = function(type) {
  5770. return box(types.hdlr, HDLR_TYPES[type]);
  5771. };
  5772. mdat = function(data) {
  5773. return box(types.mdat, data);
  5774. };
  5775. mdhd = function(track) {
  5776. var result = new Uint8Array([
  5777. 0x00, // version 0
  5778. 0x00, 0x00, 0x00, // flags
  5779. 0x00, 0x00, 0x00, 0x02, // creation_time
  5780. 0x00, 0x00, 0x00, 0x03, // modification_time
  5781. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  5782. (track.duration >>> 24) & 0xFF,
  5783. (track.duration >>> 16) & 0xFF,
  5784. (track.duration >>> 8) & 0xFF,
  5785. track.duration & 0xFF, // duration
  5786. 0x55, 0xc4, // 'und' language (undetermined)
  5787. 0x00, 0x00
  5788. ]);
  5789. // Use the sample rate from the track metadata, when it is
  5790. // defined. The sample rate can be parsed out of an ADTS header, for
  5791. // instance.
  5792. if (track.samplerate) {
  5793. result[12] = (track.samplerate >>> 24) & 0xFF;
  5794. result[13] = (track.samplerate >>> 16) & 0xFF;
  5795. result[14] = (track.samplerate >>> 8) & 0xFF;
  5796. result[15] = (track.samplerate) & 0xFF;
  5797. }
  5798. return box(types.mdhd, result);
  5799. };
  5800. mdia = function(track) {
  5801. return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
  5802. };
  5803. mfhd = function(sequenceNumber) {
  5804. return box(types.mfhd, new Uint8Array([
  5805. 0x00,
  5806. 0x00, 0x00, 0x00, // flags
  5807. (sequenceNumber & 0xFF000000) >> 24,
  5808. (sequenceNumber & 0xFF0000) >> 16,
  5809. (sequenceNumber & 0xFF00) >> 8,
  5810. sequenceNumber & 0xFF // sequence_number
  5811. ]));
  5812. };
  5813. minf = function(track) {
  5814. return box(types.minf,
  5815. track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
  5816. dinf(),
  5817. stbl(track));
  5818. };
  5819. moof = function(sequenceNumber, tracks) {
  5820. var
  5821. trackFragments = [],
  5822. i = tracks.length;
  5823. // build traf boxes for each track fragment
  5824. while (i--) {
  5825. trackFragments[i] = traf(tracks[i]);
  5826. }
  5827. return box.apply(null, [
  5828. types.moof,
  5829. mfhd(sequenceNumber)
  5830. ].concat(trackFragments));
  5831. };
  5832. /**
  5833. * Returns a movie box.
  5834. * @param tracks {array} the tracks associated with this movie
  5835. * @see ISO/IEC 14496-12:2012(E), section 8.2.1
  5836. */
  5837. moov = function(tracks) {
  5838. var
  5839. i = tracks.length,
  5840. boxes = [];
  5841. while (i--) {
  5842. boxes[i] = trak(tracks[i]);
  5843. }
  5844. return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));
  5845. };
  5846. mvex = function(tracks) {
  5847. var
  5848. i = tracks.length,
  5849. boxes = [];
  5850. while (i--) {
  5851. boxes[i] = trex(tracks[i]);
  5852. }
  5853. return box.apply(null, [types.mvex].concat(boxes));
  5854. };
  5855. mvhd = function(duration) {
  5856. var
  5857. bytes = new Uint8Array([
  5858. 0x00, // version 0
  5859. 0x00, 0x00, 0x00, // flags
  5860. 0x00, 0x00, 0x00, 0x01, // creation_time
  5861. 0x00, 0x00, 0x00, 0x02, // modification_time
  5862. 0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
  5863. (duration & 0xFF000000) >> 24,
  5864. (duration & 0xFF0000) >> 16,
  5865. (duration & 0xFF00) >> 8,
  5866. duration & 0xFF, // duration
  5867. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  5868. 0x01, 0x00, // 1.0 volume
  5869. 0x00, 0x00, // reserved
  5870. 0x00, 0x00, 0x00, 0x00, // reserved
  5871. 0x00, 0x00, 0x00, 0x00, // reserved
  5872. 0x00, 0x01, 0x00, 0x00,
  5873. 0x00, 0x00, 0x00, 0x00,
  5874. 0x00, 0x00, 0x00, 0x00,
  5875. 0x00, 0x00, 0x00, 0x00,
  5876. 0x00, 0x01, 0x00, 0x00,
  5877. 0x00, 0x00, 0x00, 0x00,
  5878. 0x00, 0x00, 0x00, 0x00,
  5879. 0x00, 0x00, 0x00, 0x00,
  5880. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  5881. 0x00, 0x00, 0x00, 0x00,
  5882. 0x00, 0x00, 0x00, 0x00,
  5883. 0x00, 0x00, 0x00, 0x00,
  5884. 0x00, 0x00, 0x00, 0x00,
  5885. 0x00, 0x00, 0x00, 0x00,
  5886. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5887. 0xff, 0xff, 0xff, 0xff // next_track_ID
  5888. ]);
  5889. return box(types.mvhd, bytes);
  5890. };
  5891. sdtp = function(track) {
  5892. var
  5893. samples = track.samples || [],
  5894. bytes = new Uint8Array(4 + samples.length),
  5895. flags,
  5896. i;
  5897. // leave the full box header (4 bytes) all zero
  5898. // write the sample table
  5899. for (i = 0; i < samples.length; i++) {
  5900. flags = samples[i].flags;
  5901. bytes[i + 4] = (flags.dependsOn << 4) |
  5902. (flags.isDependedOn << 2) |
  5903. (flags.hasRedundancy);
  5904. }
  5905. return box(types.sdtp,
  5906. bytes);
  5907. };
  5908. stbl = function(track) {
  5909. return box(types.stbl,
  5910. stsd(track),
  5911. box(types.stts, STTS),
  5912. box(types.stsc, STSC),
  5913. box(types.stsz, STSZ),
  5914. box(types.stco, STCO));
  5915. };
  5916. (function() {
  5917. var videoSample, audioSample;
  5918. stsd = function(track) {
  5919. return box(types.stsd, new Uint8Array([
  5920. 0x00, // version 0
  5921. 0x00, 0x00, 0x00, // flags
  5922. 0x00, 0x00, 0x00, 0x01
  5923. ]), track.type === 'video' ? videoSample(track) : audioSample(track));
  5924. };
  5925. videoSample = function(track) {
  5926. var
  5927. sps = track.sps || [],
  5928. pps = track.pps || [],
  5929. sequenceParameterSets = [],
  5930. pictureParameterSets = [],
  5931. i;
  5932. // assemble the SPSs
  5933. for (i = 0; i < sps.length; i++) {
  5934. sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
  5935. sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
  5936. sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
  5937. }
  5938. // assemble the PPSs
  5939. for (i = 0; i < pps.length; i++) {
  5940. pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
  5941. pictureParameterSets.push((pps[i].byteLength & 0xFF));
  5942. pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
  5943. }
  5944. return box(types.avc1, new Uint8Array([
  5945. 0x00, 0x00, 0x00,
  5946. 0x00, 0x00, 0x00, // reserved
  5947. 0x00, 0x01, // data_reference_index
  5948. 0x00, 0x00, // pre_defined
  5949. 0x00, 0x00, // reserved
  5950. 0x00, 0x00, 0x00, 0x00,
  5951. 0x00, 0x00, 0x00, 0x00,
  5952. 0x00, 0x00, 0x00, 0x00, // pre_defined
  5953. (track.width & 0xff00) >> 8,
  5954. track.width & 0xff, // width
  5955. (track.height & 0xff00) >> 8,
  5956. track.height & 0xff, // height
  5957. 0x00, 0x48, 0x00, 0x00, // horizresolution
  5958. 0x00, 0x48, 0x00, 0x00, // vertresolution
  5959. 0x00, 0x00, 0x00, 0x00, // reserved
  5960. 0x00, 0x01, // frame_count
  5961. 0x13,
  5962. 0x76, 0x69, 0x64, 0x65,
  5963. 0x6f, 0x6a, 0x73, 0x2d,
  5964. 0x63, 0x6f, 0x6e, 0x74,
  5965. 0x72, 0x69, 0x62, 0x2d,
  5966. 0x68, 0x6c, 0x73, 0x00,
  5967. 0x00, 0x00, 0x00, 0x00,
  5968. 0x00, 0x00, 0x00, 0x00,
  5969. 0x00, 0x00, 0x00, // compressorname
  5970. 0x00, 0x18, // depth = 24
  5971. 0x11, 0x11 // pre_defined = -1
  5972. ]), box(types.avcC, new Uint8Array([
  5973. 0x01, // configurationVersion
  5974. track.profileIdc, // AVCProfileIndication
  5975. track.profileCompatibility, // profile_compatibility
  5976. track.levelIdc, // AVCLevelIndication
  5977. 0xff // lengthSizeMinusOne, hard-coded to 4 bytes
  5978. ].concat([
  5979. sps.length // numOfSequenceParameterSets
  5980. ]).concat(sequenceParameterSets).concat([
  5981. pps.length // numOfPictureParameterSets
  5982. ]).concat(pictureParameterSets))), // "PPS"
  5983. box(types.btrt, new Uint8Array([
  5984. 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
  5985. 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
  5986. 0x00, 0x2d, 0xc6, 0xc0
  5987. ])) // avgBitrate
  5988. );
  5989. };
  5990. audioSample = function(track) {
  5991. return box(types.mp4a, new Uint8Array([
  5992. // SampleEntry, ISO/IEC 14496-12
  5993. 0x00, 0x00, 0x00,
  5994. 0x00, 0x00, 0x00, // reserved
  5995. 0x00, 0x01, // data_reference_index
  5996. // AudioSampleEntry, ISO/IEC 14496-12
  5997. 0x00, 0x00, 0x00, 0x00, // reserved
  5998. 0x00, 0x00, 0x00, 0x00, // reserved
  5999. (track.channelcount & 0xff00) >> 8,
  6000. (track.channelcount & 0xff), // channelcount
  6001. (track.samplesize & 0xff00) >> 8,
  6002. (track.samplesize & 0xff), // samplesize
  6003. 0x00, 0x00, // pre_defined
  6004. 0x00, 0x00, // reserved
  6005. (track.samplerate & 0xff00) >> 8,
  6006. (track.samplerate & 0xff),
  6007. 0x00, 0x00 // samplerate, 16.16
  6008. // MP4AudioSampleEntry, ISO/IEC 14496-14
  6009. ]), esds(track));
  6010. };
  6011. }());
  6012. tkhd = function(track) {
  6013. var result = new Uint8Array([
  6014. 0x00, // version 0
  6015. 0x00, 0x00, 0x07, // flags
  6016. 0x00, 0x00, 0x00, 0x00, // creation_time
  6017. 0x00, 0x00, 0x00, 0x00, // modification_time
  6018. (track.id & 0xFF000000) >> 24,
  6019. (track.id & 0xFF0000) >> 16,
  6020. (track.id & 0xFF00) >> 8,
  6021. track.id & 0xFF, // track_ID
  6022. 0x00, 0x00, 0x00, 0x00, // reserved
  6023. (track.duration & 0xFF000000) >> 24,
  6024. (track.duration & 0xFF0000) >> 16,
  6025. (track.duration & 0xFF00) >> 8,
  6026. track.duration & 0xFF, // duration
  6027. 0x00, 0x00, 0x00, 0x00,
  6028. 0x00, 0x00, 0x00, 0x00, // reserved
  6029. 0x00, 0x00, // layer
  6030. 0x00, 0x00, // alternate_group
  6031. 0x01, 0x00, // non-audio track volume
  6032. 0x00, 0x00, // reserved
  6033. 0x00, 0x01, 0x00, 0x00,
  6034. 0x00, 0x00, 0x00, 0x00,
  6035. 0x00, 0x00, 0x00, 0x00,
  6036. 0x00, 0x00, 0x00, 0x00,
  6037. 0x00, 0x01, 0x00, 0x00,
  6038. 0x00, 0x00, 0x00, 0x00,
  6039. 0x00, 0x00, 0x00, 0x00,
  6040. 0x00, 0x00, 0x00, 0x00,
  6041. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  6042. (track.width & 0xFF00) >> 8,
  6043. track.width & 0xFF,
  6044. 0x00, 0x00, // width
  6045. (track.height & 0xFF00) >> 8,
  6046. track.height & 0xFF,
  6047. 0x00, 0x00 // height
  6048. ]);
  6049. return box(types.tkhd, result);
  6050. };
  6051. /**
  6052. * Generate a track fragment (traf) box. A traf box collects metadata
  6053. * about tracks in a movie fragment (moof) box.
  6054. */
  6055. traf = function(track) {
  6056. var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,
  6057. sampleDependencyTable, dataOffset,
  6058. upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
  6059. trackFragmentHeader = box(types.tfhd, new Uint8Array([
  6060. 0x00, // version 0
  6061. 0x00, 0x00, 0x3a, // flags
  6062. (track.id & 0xFF000000) >> 24,
  6063. (track.id & 0xFF0000) >> 16,
  6064. (track.id & 0xFF00) >> 8,
  6065. (track.id & 0xFF), // track_ID
  6066. 0x00, 0x00, 0x00, 0x01, // sample_description_index
  6067. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  6068. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  6069. 0x00, 0x00, 0x00, 0x00 // default_sample_flags
  6070. ]));
  6071. upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
  6072. lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
  6073. trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
  6074. 0x01, // version 1
  6075. 0x00, 0x00, 0x00, // flags
  6076. // baseMediaDecodeTime
  6077. (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
  6078. (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
  6079. (upperWordBaseMediaDecodeTime >>> 8) & 0xFF,
  6080. upperWordBaseMediaDecodeTime & 0xFF,
  6081. (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
  6082. (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
  6083. (lowerWordBaseMediaDecodeTime >>> 8) & 0xFF,
  6084. lowerWordBaseMediaDecodeTime & 0xFF
  6085. ]));
  6086. // the data offset specifies the number of bytes from the start of
  6087. // the containing moof to the first payload byte of the associated
  6088. // mdat
  6089. dataOffset = (32 + // tfhd
  6090. 20 + // tfdt
  6091. 8 + // traf header
  6092. 16 + // mfhd
  6093. 8 + // moof header
  6094. 8); // mdat header
  6095. // audio tracks require less metadata
  6096. if (track.type === 'audio') {
  6097. trackFragmentRun = trun(track, dataOffset);
  6098. return box(types.traf,
  6099. trackFragmentHeader,
  6100. trackFragmentDecodeTime,
  6101. trackFragmentRun);
  6102. }
  6103. // video tracks should contain an independent and disposable samples
  6104. // box (sdtp)
  6105. // generate one and adjust offsets to match
  6106. sampleDependencyTable = sdtp(track);
  6107. trackFragmentRun = trun(track,
  6108. sampleDependencyTable.length + dataOffset);
  6109. return box(types.traf,
  6110. trackFragmentHeader,
  6111. trackFragmentDecodeTime,
  6112. trackFragmentRun,
  6113. sampleDependencyTable);
  6114. };
  6115. /**
  6116. * Generate a track box.
  6117. * @param track {object} a track definition
  6118. * @return {Uint8Array} the track box
  6119. */
  6120. trak = function(track) {
  6121. track.duration = track.duration || 0xffffffff;
  6122. return box(types.trak,
  6123. tkhd(track),
  6124. mdia(track));
  6125. };
  6126. trex = function(track) {
  6127. var result = new Uint8Array([
  6128. 0x00, // version 0
  6129. 0x00, 0x00, 0x00, // flags
  6130. (track.id & 0xFF000000) >> 24,
  6131. (track.id & 0xFF0000) >> 16,
  6132. (track.id & 0xFF00) >> 8,
  6133. (track.id & 0xFF), // track_ID
  6134. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  6135. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  6136. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  6137. 0x00, 0x01, 0x00, 0x01 // default_sample_flags
  6138. ]);
  6139. // the last two bytes of default_sample_flags is the sample
  6140. // degradation priority, a hint about the importance of this sample
  6141. // relative to others. Lower the degradation priority for all sample
  6142. // types other than video.
  6143. if (track.type !== 'video') {
  6144. result[result.length - 1] = 0x00;
  6145. }
  6146. return box(types.trex, result);
  6147. };
  6148. (function() {
  6149. var audioTrun, videoTrun, trunHeader;
  6150. // This method assumes all samples are uniform. That is, if a
  6151. // duration is present for the first sample, it will be present for
  6152. // all subsequent samples.
  6153. // see ISO/IEC 14496-12:2012, Section 8.8.8.1
  6154. trunHeader = function(samples, offset) {
  6155. var durationPresent = 0, sizePresent = 0,
  6156. flagsPresent = 0, compositionTimeOffset = 0;
  6157. // trun flag constants
  6158. if (samples.length) {
  6159. if (samples[0].duration !== undefined) {
  6160. durationPresent = 0x1;
  6161. }
  6162. if (samples[0].size !== undefined) {
  6163. sizePresent = 0x2;
  6164. }
  6165. if (samples[0].flags !== undefined) {
  6166. flagsPresent = 0x4;
  6167. }
  6168. if (samples[0].compositionTimeOffset !== undefined) {
  6169. compositionTimeOffset = 0x8;
  6170. }
  6171. }
  6172. return [
  6173. 0x00, // version 0
  6174. 0x00,
  6175. durationPresent | sizePresent | flagsPresent | compositionTimeOffset,
  6176. 0x01, // flags
  6177. (samples.length & 0xFF000000) >>> 24,
  6178. (samples.length & 0xFF0000) >>> 16,
  6179. (samples.length & 0xFF00) >>> 8,
  6180. samples.length & 0xFF, // sample_count
  6181. (offset & 0xFF000000) >>> 24,
  6182. (offset & 0xFF0000) >>> 16,
  6183. (offset & 0xFF00) >>> 8,
  6184. offset & 0xFF // data_offset
  6185. ];
  6186. };
  6187. videoTrun = function(track, offset) {
  6188. var bytes, samples, sample, i;
  6189. samples = track.samples || [];
  6190. offset += 8 + 12 + (16 * samples.length);
  6191. bytes = trunHeader(samples, offset);
  6192. for (i = 0; i < samples.length; i++) {
  6193. sample = samples[i];
  6194. bytes = bytes.concat([
  6195. (sample.duration & 0xFF000000) >>> 24,
  6196. (sample.duration & 0xFF0000) >>> 16,
  6197. (sample.duration & 0xFF00) >>> 8,
  6198. sample.duration & 0xFF, // sample_duration
  6199. (sample.size & 0xFF000000) >>> 24,
  6200. (sample.size & 0xFF0000) >>> 16,
  6201. (sample.size & 0xFF00) >>> 8,
  6202. sample.size & 0xFF, // sample_size
  6203. (sample.flags.isLeading << 2) | sample.flags.dependsOn,
  6204. (sample.flags.isDependedOn << 6) |
  6205. (sample.flags.hasRedundancy << 4) |
  6206. (sample.flags.paddingValue << 1) |
  6207. sample.flags.isNonSyncSample,
  6208. sample.flags.degradationPriority & 0xF0 << 8,
  6209. sample.flags.degradationPriority & 0x0F, // sample_flags
  6210. (sample.compositionTimeOffset & 0xFF000000) >>> 24,
  6211. (sample.compositionTimeOffset & 0xFF0000) >>> 16,
  6212. (sample.compositionTimeOffset & 0xFF00) >>> 8,
  6213. sample.compositionTimeOffset & 0xFF // sample_composition_time_offset
  6214. ]);
  6215. }
  6216. return box(types.trun, new Uint8Array(bytes));
  6217. };
  6218. audioTrun = function(track, offset) {
  6219. var bytes, samples, sample, i;
  6220. samples = track.samples || [];
  6221. offset += 8 + 12 + (8 * samples.length);
  6222. bytes = trunHeader(samples, offset);
  6223. for (i = 0; i < samples.length; i++) {
  6224. sample = samples[i];
  6225. bytes = bytes.concat([
  6226. (sample.duration & 0xFF000000) >>> 24,
  6227. (sample.duration & 0xFF0000) >>> 16,
  6228. (sample.duration & 0xFF00) >>> 8,
  6229. sample.duration & 0xFF, // sample_duration
  6230. (sample.size & 0xFF000000) >>> 24,
  6231. (sample.size & 0xFF0000) >>> 16,
  6232. (sample.size & 0xFF00) >>> 8,
  6233. sample.size & 0xFF]); // sample_size
  6234. }
  6235. return box(types.trun, new Uint8Array(bytes));
  6236. };
  6237. trun = function(track, offset) {
  6238. if (track.type === 'audio') {
  6239. return audioTrun(track, offset);
  6240. }
  6241. return videoTrun(track, offset);
  6242. };
  6243. }());
  6244. module.exports = {
  6245. ftyp: ftyp,
  6246. mdat: mdat,
  6247. moof: moof,
  6248. moov: moov,
  6249. initSegment: function(tracks) {
  6250. var
  6251. fileType = ftyp(),
  6252. movie = moov(tracks),
  6253. result;
  6254. result = new Uint8Array(fileType.byteLength + movie.byteLength);
  6255. result.set(fileType);
  6256. result.set(movie, fileType.byteLength);
  6257. return result;
  6258. }
  6259. };
  6260. },{}],35:[function(require,module,exports){
  6261. /**
  6262. * mux.js
  6263. *
  6264. * Copyright (c) 2015 Brightcove
  6265. * All rights reserved.
  6266. *
  6267. * A stream-based mp2t to mp4 converter. This utility can be used to
  6268. * deliver mp4s to a SourceBuffer on platforms that support native
  6269. * Media Source Extensions.
  6270. */
  6271. 'use strict';
  6272. var Stream = require('../utils/stream.js');
  6273. var mp4 = require('./mp4-generator.js');
  6274. var m2ts = require('../m2ts/m2ts.js');
  6275. var AdtsStream = require('../codecs/adts.js');
  6276. var H264Stream = require('../codecs/h264').H264Stream;
  6277. var AacStream = require('../aac');
  6278. var coneOfSilence = require('../data/silence');
  6279. var clock = require('../utils/clock');
  6280. // constants
  6281. var AUDIO_PROPERTIES = [
  6282. 'audioobjecttype',
  6283. 'channelcount',
  6284. 'samplerate',
  6285. 'samplingfrequencyindex',
  6286. 'samplesize'
  6287. ];
  6288. var VIDEO_PROPERTIES = [
  6289. 'width',
  6290. 'height',
  6291. 'profileIdc',
  6292. 'levelIdc',
  6293. 'profileCompatibility'
  6294. ];
  6295. var ONE_SECOND_IN_TS = 90000; // 90kHz clock
  6296. // object types
  6297. var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;
  6298. // Helper functions
  6299. var
  6300. createDefaultSample,
  6301. isLikelyAacData,
  6302. collectDtsInfo,
  6303. clearDtsInfo,
  6304. calculateTrackBaseMediaDecodeTime,
  6305. arrayEquals,
  6306. sumFrameByteLengths;
  6307. /**
  6308. * Default sample object
  6309. * see ISO/IEC 14496-12:2012, section 8.6.4.3
  6310. */
  6311. createDefaultSample = function() {
  6312. return {
  6313. size: 0,
  6314. flags: {
  6315. isLeading: 0,
  6316. dependsOn: 1,
  6317. isDependedOn: 0,
  6318. hasRedundancy: 0,
  6319. degradationPriority: 0
  6320. }
  6321. };
  6322. };
  6323. isLikelyAacData = function(data) {
  6324. if ((data[0] === 'I'.charCodeAt(0)) &&
  6325. (data[1] === 'D'.charCodeAt(0)) &&
  6326. (data[2] === '3'.charCodeAt(0))) {
  6327. return true;
  6328. }
  6329. return false;
  6330. };
  6331. /**
  6332. * Compare two arrays (even typed) for same-ness
  6333. */
  6334. arrayEquals = function(a, b) {
  6335. var
  6336. i;
  6337. if (a.length !== b.length) {
  6338. return false;
  6339. }
  6340. // compare the value of each element in the array
  6341. for (i = 0; i < a.length; i++) {
  6342. if (a[i] !== b[i]) {
  6343. return false;
  6344. }
  6345. }
  6346. return true;
  6347. };
  6348. /**
  6349. * Sum the `byteLength` properties of the data in each AAC frame
  6350. */
  6351. sumFrameByteLengths = function(array) {
  6352. var
  6353. i,
  6354. currentObj,
  6355. sum = 0;
  6356. // sum the byteLength's all each nal unit in the frame
  6357. for (i = 0; i < array.length; i++) {
  6358. currentObj = array[i];
  6359. sum += currentObj.data.byteLength;
  6360. }
  6361. return sum;
  6362. };
  6363. /**
  6364. * Constructs a single-track, ISO BMFF media segment from AAC data
  6365. * events. The output of this stream can be fed to a SourceBuffer
  6366. * configured with a suitable initialization segment.
  6367. */
  6368. AudioSegmentStream = function(track) {
  6369. var
  6370. adtsFrames = [],
  6371. sequenceNumber = 0,
  6372. earliestAllowedDts = 0,
  6373. audioAppendStartTs = 0,
  6374. videoBaseMediaDecodeTime = Infinity;
  6375. AudioSegmentStream.prototype.init.call(this);
  6376. this.push = function(data) {
  6377. collectDtsInfo(track, data);
  6378. if (track) {
  6379. AUDIO_PROPERTIES.forEach(function(prop) {
  6380. track[prop] = data[prop];
  6381. });
  6382. }
  6383. // buffer audio data until end() is called
  6384. adtsFrames.push(data);
  6385. };
  6386. this.setEarliestDts = function(earliestDts) {
  6387. earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
  6388. };
  6389. this.setVideoBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  6390. videoBaseMediaDecodeTime = baseMediaDecodeTime;
  6391. };
  6392. this.setAudioAppendStart = function(timestamp) {
  6393. audioAppendStartTs = timestamp;
  6394. };
  6395. this.flush = function() {
  6396. var
  6397. frames,
  6398. moof,
  6399. mdat,
  6400. boxes;
  6401. // return early if no audio data has been observed
  6402. if (adtsFrames.length === 0) {
  6403. this.trigger('done', 'AudioSegmentStream');
  6404. return;
  6405. }
  6406. frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);
  6407. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  6408. this.prefixWithSilence_(track, frames);
  6409. // we have to build the index from byte locations to
  6410. // samples (that is, adts frames) in the audio data
  6411. track.samples = this.generateSampleTable_(frames);
  6412. // concatenate the audio data to constuct the mdat
  6413. mdat = mp4.mdat(this.concatenateFrameData_(frames));
  6414. adtsFrames = [];
  6415. moof = mp4.moof(sequenceNumber, [track]);
  6416. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  6417. // bump the sequence number for next time
  6418. sequenceNumber++;
  6419. boxes.set(moof);
  6420. boxes.set(mdat, moof.byteLength);
  6421. clearDtsInfo(track);
  6422. this.trigger('data', {track: track, boxes: boxes});
  6423. this.trigger('done', 'AudioSegmentStream');
  6424. };
  6425. // Possibly pad (prefix) the audio track with silence if appending this track
  6426. // would lead to the introduction of a gap in the audio buffer
  6427. this.prefixWithSilence_ = function(track, frames) {
  6428. var
  6429. baseMediaDecodeTimeTs,
  6430. frameDuration = 0,
  6431. audioGapDuration = 0,
  6432. audioFillFrameCount = 0,
  6433. audioFillDuration = 0,
  6434. silentFrame,
  6435. i;
  6436. if (!frames.length) {
  6437. return;
  6438. }
  6439. baseMediaDecodeTimeTs = clock.audioTsToVideoTs(track.baseMediaDecodeTime, track.samplerate);
  6440. // determine frame clock duration based on sample rate, round up to avoid overfills
  6441. frameDuration = Math.ceil(ONE_SECOND_IN_TS / (track.samplerate / 1024));
  6442. if (audioAppendStartTs && videoBaseMediaDecodeTime) {
  6443. // insert the shortest possible amount (audio gap or audio to video gap)
  6444. audioGapDuration =
  6445. baseMediaDecodeTimeTs - Math.max(audioAppendStartTs, videoBaseMediaDecodeTime);
  6446. // number of full frames in the audio gap
  6447. audioFillFrameCount = Math.floor(audioGapDuration / frameDuration);
  6448. audioFillDuration = audioFillFrameCount * frameDuration;
  6449. }
  6450. // don't attempt to fill gaps smaller than a single frame or larger
  6451. // than a half second
  6452. if (audioFillFrameCount < 1 || audioFillDuration > ONE_SECOND_IN_TS / 2) {
  6453. return;
  6454. }
  6455. silentFrame = coneOfSilence[track.samplerate];
  6456. if (!silentFrame) {
  6457. // we don't have a silent frame pregenerated for the sample rate, so use a frame
  6458. // from the content instead
  6459. silentFrame = frames[0].data;
  6460. }
  6461. for (i = 0; i < audioFillFrameCount; i++) {
  6462. frames.splice(i, 0, {
  6463. data: silentFrame
  6464. });
  6465. }
  6466. track.baseMediaDecodeTime -=
  6467. Math.floor(clock.videoTsToAudioTs(audioFillDuration, track.samplerate));
  6468. };
  6469. // If the audio segment extends before the earliest allowed dts
  6470. // value, remove AAC frames until starts at or after the earliest
  6471. // allowed DTS so that we don't end up with a negative baseMedia-
  6472. // DecodeTime for the audio track
  6473. this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
  6474. if (track.minSegmentDts >= earliestAllowedDts) {
  6475. return adtsFrames;
  6476. }
  6477. // We will need to recalculate the earliest segment Dts
  6478. track.minSegmentDts = Infinity;
  6479. return adtsFrames.filter(function(currentFrame) {
  6480. // If this is an allowed frame, keep it and record it's Dts
  6481. if (currentFrame.dts >= earliestAllowedDts) {
  6482. track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
  6483. track.minSegmentPts = track.minSegmentDts;
  6484. return true;
  6485. }
  6486. // Otherwise, discard it
  6487. return false;
  6488. });
  6489. };
  6490. // generate the track's raw mdat data from an array of frames
  6491. this.generateSampleTable_ = function(frames) {
  6492. var
  6493. i,
  6494. currentFrame,
  6495. samples = [];
  6496. for (i = 0; i < frames.length; i++) {
  6497. currentFrame = frames[i];
  6498. samples.push({
  6499. size: currentFrame.data.byteLength,
  6500. duration: 1024 // For AAC audio, all samples contain 1024 samples
  6501. });
  6502. }
  6503. return samples;
  6504. };
  6505. // generate the track's sample table from an array of frames
  6506. this.concatenateFrameData_ = function(frames) {
  6507. var
  6508. i,
  6509. currentFrame,
  6510. dataOffset = 0,
  6511. data = new Uint8Array(sumFrameByteLengths(frames));
  6512. for (i = 0; i < frames.length; i++) {
  6513. currentFrame = frames[i];
  6514. data.set(currentFrame.data, dataOffset);
  6515. dataOffset += currentFrame.data.byteLength;
  6516. }
  6517. return data;
  6518. };
  6519. };
  6520. AudioSegmentStream.prototype = new Stream();
  6521. /**
  6522. * Constructs a single-track, ISO BMFF media segment from H264 data
  6523. * events. The output of this stream can be fed to a SourceBuffer
  6524. * configured with a suitable initialization segment.
  6525. * @param track {object} track metadata configuration
  6526. * @param options {object} transmuxer options object
  6527. * @param options.alignGopsAtEnd {boolean} If true, start from the end of the
  6528. * gopsToAlignWith list when attempting to align gop pts
  6529. */
  6530. VideoSegmentStream = function(track, options) {
  6531. var
  6532. sequenceNumber = 0,
  6533. nalUnits = [],
  6534. gopsToAlignWith = [],
  6535. config,
  6536. pps;
  6537. options = options || {};
  6538. VideoSegmentStream.prototype.init.call(this);
  6539. delete track.minPTS;
  6540. this.gopCache_ = [];
  6541. this.push = function(nalUnit) {
  6542. collectDtsInfo(track, nalUnit);
  6543. // record the track config
  6544. if (nalUnit.nalUnitType === 'seq_parameter_set_rbsp' && !config) {
  6545. config = nalUnit.config;
  6546. track.sps = [nalUnit.data];
  6547. VIDEO_PROPERTIES.forEach(function(prop) {
  6548. track[prop] = config[prop];
  6549. }, this);
  6550. }
  6551. if (nalUnit.nalUnitType === 'pic_parameter_set_rbsp' &&
  6552. !pps) {
  6553. pps = nalUnit.data;
  6554. track.pps = [nalUnit.data];
  6555. }
  6556. // buffer video until flush() is called
  6557. nalUnits.push(nalUnit);
  6558. };
  6559. this.flush = function() {
  6560. var
  6561. frames,
  6562. gopForFusion,
  6563. gops,
  6564. moof,
  6565. mdat,
  6566. boxes;
  6567. // Throw away nalUnits at the start of the byte stream until
  6568. // we find the first AUD
  6569. while (nalUnits.length) {
  6570. if (nalUnits[0].nalUnitType === 'access_unit_delimiter_rbsp') {
  6571. break;
  6572. }
  6573. nalUnits.shift();
  6574. }
  6575. // Return early if no video data has been observed
  6576. if (nalUnits.length === 0) {
  6577. this.resetStream_();
  6578. this.trigger('done', 'VideoSegmentStream');
  6579. return;
  6580. }
  6581. // Organize the raw nal-units into arrays that represent
  6582. // higher-level constructs such as frames and gops
  6583. // (group-of-pictures)
  6584. frames = this.groupNalsIntoFrames_(nalUnits);
  6585. gops = this.groupFramesIntoGops_(frames);
  6586. // If the first frame of this fragment is not a keyframe we have
  6587. // a problem since MSE (on Chrome) requires a leading keyframe.
  6588. //
  6589. // We have two approaches to repairing this situation:
  6590. // 1) GOP-FUSION:
  6591. // This is where we keep track of the GOPS (group-of-pictures)
  6592. // from previous fragments and attempt to find one that we can
  6593. // prepend to the current fragment in order to create a valid
  6594. // fragment.
  6595. // 2) KEYFRAME-PULLING:
  6596. // Here we search for the first keyframe in the fragment and
  6597. // throw away all the frames between the start of the fragment
  6598. // and that keyframe. We then extend the duration and pull the
  6599. // PTS of the keyframe forward so that it covers the time range
  6600. // of the frames that were disposed of.
  6601. //
  6602. // #1 is far prefereable over #2 which can cause "stuttering" but
  6603. // requires more things to be just right.
  6604. if (!gops[0][0].keyFrame) {
  6605. // Search for a gop for fusion from our gopCache
  6606. gopForFusion = this.getGopForFusion_(nalUnits[0], track);
  6607. if (gopForFusion) {
  6608. gops.unshift(gopForFusion);
  6609. // Adjust Gops' metadata to account for the inclusion of the
  6610. // new gop at the beginning
  6611. gops.byteLength += gopForFusion.byteLength;
  6612. gops.nalCount += gopForFusion.nalCount;
  6613. gops.pts = gopForFusion.pts;
  6614. gops.dts = gopForFusion.dts;
  6615. gops.duration += gopForFusion.duration;
  6616. } else {
  6617. // If we didn't find a candidate gop fall back to keyrame-pulling
  6618. gops = this.extendFirstKeyFrame_(gops);
  6619. }
  6620. }
  6621. // Trim gops to align with gopsToAlignWith
  6622. if (gopsToAlignWith.length) {
  6623. var alignedGops;
  6624. if (options.alignGopsAtEnd) {
  6625. alignedGops = this.alignGopsAtEnd_(gops);
  6626. } else {
  6627. alignedGops = this.alignGopsAtStart_(gops);
  6628. }
  6629. if (!alignedGops) {
  6630. // save all the nals in the last GOP into the gop cache
  6631. this.gopCache_.unshift({
  6632. gop: gops.pop(),
  6633. pps: track.pps,
  6634. sps: track.sps
  6635. });
  6636. // Keep a maximum of 6 GOPs in the cache
  6637. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  6638. // Clear nalUnits
  6639. nalUnits = [];
  6640. // return early no gops can be aligned with desired gopsToAlignWith
  6641. this.resetStream_();
  6642. this.trigger('done', 'VideoSegmentStream');
  6643. return;
  6644. }
  6645. // Some gops were trimmed. clear dts info so minSegmentDts and pts are correct
  6646. // when recalculated before sending off to CoalesceStream
  6647. clearDtsInfo(track);
  6648. gops = alignedGops;
  6649. }
  6650. collectDtsInfo(track, gops);
  6651. // First, we have to build the index from byte locations to
  6652. // samples (that is, frames) in the video data
  6653. track.samples = this.generateSampleTable_(gops);
  6654. // Concatenate the video data and construct the mdat
  6655. mdat = mp4.mdat(this.concatenateNalData_(gops));
  6656. track.baseMediaDecodeTime = calculateTrackBaseMediaDecodeTime(track);
  6657. this.trigger('processedGopsInfo', gops.map(function(gop) {
  6658. return {
  6659. pts: gop.pts,
  6660. dts: gop.dts,
  6661. byteLength: gop.byteLength
  6662. };
  6663. }));
  6664. // save all the nals in the last GOP into the gop cache
  6665. this.gopCache_.unshift({
  6666. gop: gops.pop(),
  6667. pps: track.pps,
  6668. sps: track.sps
  6669. });
  6670. // Keep a maximum of 6 GOPs in the cache
  6671. this.gopCache_.length = Math.min(6, this.gopCache_.length);
  6672. // Clear nalUnits
  6673. nalUnits = [];
  6674. this.trigger('baseMediaDecodeTime', track.baseMediaDecodeTime);
  6675. this.trigger('timelineStartInfo', track.timelineStartInfo);
  6676. moof = mp4.moof(sequenceNumber, [track]);
  6677. // it would be great to allocate this array up front instead of
  6678. // throwing away hundreds of media segment fragments
  6679. boxes = new Uint8Array(moof.byteLength + mdat.byteLength);
  6680. // Bump the sequence number for next time
  6681. sequenceNumber++;
  6682. boxes.set(moof);
  6683. boxes.set(mdat, moof.byteLength);
  6684. this.trigger('data', {track: track, boxes: boxes});
  6685. this.resetStream_();
  6686. // Continue with the flush process now
  6687. this.trigger('done', 'VideoSegmentStream');
  6688. };
  6689. this.resetStream_ = function() {
  6690. clearDtsInfo(track);
  6691. // reset config and pps because they may differ across segments
  6692. // for instance, when we are rendition switching
  6693. config = undefined;
  6694. pps = undefined;
  6695. };
  6696. // Search for a candidate Gop for gop-fusion from the gop cache and
  6697. // return it or return null if no good candidate was found
  6698. this.getGopForFusion_ = function(nalUnit) {
  6699. var
  6700. halfSecond = 45000, // Half-a-second in a 90khz clock
  6701. allowableOverlap = 10000, // About 3 frames @ 30fps
  6702. nearestDistance = Infinity,
  6703. dtsDistance,
  6704. nearestGopObj,
  6705. currentGop,
  6706. currentGopObj,
  6707. i;
  6708. // Search for the GOP nearest to the beginning of this nal unit
  6709. for (i = 0; i < this.gopCache_.length; i++) {
  6710. currentGopObj = this.gopCache_[i];
  6711. currentGop = currentGopObj.gop;
  6712. // Reject Gops with different SPS or PPS
  6713. if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
  6714. !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
  6715. continue;
  6716. }
  6717. // Reject Gops that would require a negative baseMediaDecodeTime
  6718. if (currentGop.dts < track.timelineStartInfo.dts) {
  6719. continue;
  6720. }
  6721. // The distance between the end of the gop and the start of the nalUnit
  6722. dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;
  6723. // Only consider GOPS that start before the nal unit and end within
  6724. // a half-second of the nal unit
  6725. if (dtsDistance >= -allowableOverlap &&
  6726. dtsDistance <= halfSecond) {
  6727. // Always use the closest GOP we found if there is more than
  6728. // one candidate
  6729. if (!nearestGopObj ||
  6730. nearestDistance > dtsDistance) {
  6731. nearestGopObj = currentGopObj;
  6732. nearestDistance = dtsDistance;
  6733. }
  6734. }
  6735. }
  6736. if (nearestGopObj) {
  6737. return nearestGopObj.gop;
  6738. }
  6739. return null;
  6740. };
  6741. this.extendFirstKeyFrame_ = function(gops) {
  6742. var currentGop;
  6743. if (!gops[0][0].keyFrame && gops.length > 1) {
  6744. // Remove the first GOP
  6745. currentGop = gops.shift();
  6746. gops.byteLength -= currentGop.byteLength;
  6747. gops.nalCount -= currentGop.nalCount;
  6748. // Extend the first frame of what is now the
  6749. // first gop to cover the time period of the
  6750. // frames we just removed
  6751. gops[0][0].dts = currentGop.dts;
  6752. gops[0][0].pts = currentGop.pts;
  6753. gops[0][0].duration += currentGop.duration;
  6754. }
  6755. return gops;
  6756. };
  6757. // Convert an array of nal units into an array of frames with each frame being
  6758. // composed of the nal units that make up that frame
  6759. // Also keep track of cummulative data about the frame from the nal units such
  6760. // as the frame duration, starting pts, etc.
  6761. this.groupNalsIntoFrames_ = function(nalUnits) {
  6762. var
  6763. i,
  6764. currentNal,
  6765. currentFrame = [],
  6766. frames = [];
  6767. currentFrame.byteLength = 0;
  6768. for (i = 0; i < nalUnits.length; i++) {
  6769. currentNal = nalUnits[i];
  6770. // Split on 'aud'-type nal units
  6771. if (currentNal.nalUnitType === 'access_unit_delimiter_rbsp') {
  6772. // Since the very first nal unit is expected to be an AUD
  6773. // only push to the frames array when currentFrame is not empty
  6774. if (currentFrame.length) {
  6775. currentFrame.duration = currentNal.dts - currentFrame.dts;
  6776. frames.push(currentFrame);
  6777. }
  6778. currentFrame = [currentNal];
  6779. currentFrame.byteLength = currentNal.data.byteLength;
  6780. currentFrame.pts = currentNal.pts;
  6781. currentFrame.dts = currentNal.dts;
  6782. } else {
  6783. // Specifically flag key frames for ease of use later
  6784. if (currentNal.nalUnitType === 'slice_layer_without_partitioning_rbsp_idr') {
  6785. currentFrame.keyFrame = true;
  6786. }
  6787. currentFrame.duration = currentNal.dts - currentFrame.dts;
  6788. currentFrame.byteLength += currentNal.data.byteLength;
  6789. currentFrame.push(currentNal);
  6790. }
  6791. }
  6792. // For the last frame, use the duration of the previous frame if we
  6793. // have nothing better to go on
  6794. if (frames.length &&
  6795. (!currentFrame.duration ||
  6796. currentFrame.duration <= 0)) {
  6797. currentFrame.duration = frames[frames.length - 1].duration;
  6798. }
  6799. // Push the final frame
  6800. frames.push(currentFrame);
  6801. return frames;
  6802. };
  6803. // Convert an array of frames into an array of Gop with each Gop being composed
  6804. // of the frames that make up that Gop
  6805. // Also keep track of cummulative data about the Gop from the frames such as the
  6806. // Gop duration, starting pts, etc.
  6807. this.groupFramesIntoGops_ = function(frames) {
  6808. var
  6809. i,
  6810. currentFrame,
  6811. currentGop = [],
  6812. gops = [];
  6813. // We must pre-set some of the values on the Gop since we
  6814. // keep running totals of these values
  6815. currentGop.byteLength = 0;
  6816. currentGop.nalCount = 0;
  6817. currentGop.duration = 0;
  6818. currentGop.pts = frames[0].pts;
  6819. currentGop.dts = frames[0].dts;
  6820. // store some metadata about all the Gops
  6821. gops.byteLength = 0;
  6822. gops.nalCount = 0;
  6823. gops.duration = 0;
  6824. gops.pts = frames[0].pts;
  6825. gops.dts = frames[0].dts;
  6826. for (i = 0; i < frames.length; i++) {
  6827. currentFrame = frames[i];
  6828. if (currentFrame.keyFrame) {
  6829. // Since the very first frame is expected to be an keyframe
  6830. // only push to the gops array when currentGop is not empty
  6831. if (currentGop.length) {
  6832. gops.push(currentGop);
  6833. gops.byteLength += currentGop.byteLength;
  6834. gops.nalCount += currentGop.nalCount;
  6835. gops.duration += currentGop.duration;
  6836. }
  6837. currentGop = [currentFrame];
  6838. currentGop.nalCount = currentFrame.length;
  6839. currentGop.byteLength = currentFrame.byteLength;
  6840. currentGop.pts = currentFrame.pts;
  6841. currentGop.dts = currentFrame.dts;
  6842. currentGop.duration = currentFrame.duration;
  6843. } else {
  6844. currentGop.duration += currentFrame.duration;
  6845. currentGop.nalCount += currentFrame.length;
  6846. currentGop.byteLength += currentFrame.byteLength;
  6847. currentGop.push(currentFrame);
  6848. }
  6849. }
  6850. if (gops.length && currentGop.duration <= 0) {
  6851. currentGop.duration = gops[gops.length - 1].duration;
  6852. }
  6853. gops.byteLength += currentGop.byteLength;
  6854. gops.nalCount += currentGop.nalCount;
  6855. gops.duration += currentGop.duration;
  6856. // push the final Gop
  6857. gops.push(currentGop);
  6858. return gops;
  6859. };
  6860. // generate the track's sample table from an array of gops
  6861. this.generateSampleTable_ = function(gops, baseDataOffset) {
  6862. var
  6863. h, i,
  6864. sample,
  6865. currentGop,
  6866. currentFrame,
  6867. dataOffset = baseDataOffset || 0,
  6868. samples = [];
  6869. for (h = 0; h < gops.length; h++) {
  6870. currentGop = gops[h];
  6871. for (i = 0; i < currentGop.length; i++) {
  6872. currentFrame = currentGop[i];
  6873. sample = createDefaultSample();
  6874. sample.dataOffset = dataOffset;
  6875. sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
  6876. sample.duration = currentFrame.duration;
  6877. sample.size = 4 * currentFrame.length; // Space for nal unit size
  6878. sample.size += currentFrame.byteLength;
  6879. if (currentFrame.keyFrame) {
  6880. sample.flags.dependsOn = 2;
  6881. }
  6882. dataOffset += sample.size;
  6883. samples.push(sample);
  6884. }
  6885. }
  6886. return samples;
  6887. };
  6888. // generate the track's raw mdat data from an array of gops
  6889. this.concatenateNalData_ = function(gops) {
  6890. var
  6891. h, i, j,
  6892. currentGop,
  6893. currentFrame,
  6894. currentNal,
  6895. dataOffset = 0,
  6896. nalsByteLength = gops.byteLength,
  6897. numberOfNals = gops.nalCount,
  6898. totalByteLength = nalsByteLength + 4 * numberOfNals,
  6899. data = new Uint8Array(totalByteLength),
  6900. view = new DataView(data.buffer);
  6901. // For each Gop..
  6902. for (h = 0; h < gops.length; h++) {
  6903. currentGop = gops[h];
  6904. // For each Frame..
  6905. for (i = 0; i < currentGop.length; i++) {
  6906. currentFrame = currentGop[i];
  6907. // For each NAL..
  6908. for (j = 0; j < currentFrame.length; j++) {
  6909. currentNal = currentFrame[j];
  6910. view.setUint32(dataOffset, currentNal.data.byteLength);
  6911. dataOffset += 4;
  6912. data.set(currentNal.data, dataOffset);
  6913. dataOffset += currentNal.data.byteLength;
  6914. }
  6915. }
  6916. }
  6917. return data;
  6918. };
  6919. // trim gop list to the first gop found that has a matching pts with a gop in the list
  6920. // of gopsToAlignWith starting from the START of the list
  6921. this.alignGopsAtStart_ = function(gops) {
  6922. var alignIndex, gopIndex, align, gop, byteLength, nalCount, duration, alignedGops;
  6923. byteLength = gops.byteLength;
  6924. nalCount = gops.nalCount;
  6925. duration = gops.duration;
  6926. alignIndex = gopIndex = 0;
  6927. while (alignIndex < gopsToAlignWith.length && gopIndex < gops.length) {
  6928. align = gopsToAlignWith[alignIndex];
  6929. gop = gops[gopIndex];
  6930. if (align.pts === gop.pts) {
  6931. break;
  6932. }
  6933. if (gop.pts > align.pts) {
  6934. // this current gop starts after the current gop we want to align on, so increment
  6935. // align index
  6936. alignIndex++;
  6937. continue;
  6938. }
  6939. // current gop starts before the current gop we want to align on. so increment gop
  6940. // index
  6941. gopIndex++;
  6942. byteLength -= gop.byteLength;
  6943. nalCount -= gop.nalCount;
  6944. duration -= gop.duration;
  6945. }
  6946. if (gopIndex === 0) {
  6947. // no gops to trim
  6948. return gops;
  6949. }
  6950. if (gopIndex === gops.length) {
  6951. // all gops trimmed, skip appending all gops
  6952. return null;
  6953. }
  6954. alignedGops = gops.slice(gopIndex);
  6955. alignedGops.byteLength = byteLength;
  6956. alignedGops.duration = duration;
  6957. alignedGops.nalCount = nalCount;
  6958. alignedGops.pts = alignedGops[0].pts;
  6959. alignedGops.dts = alignedGops[0].dts;
  6960. return alignedGops;
  6961. };
  6962. // trim gop list to the first gop found that has a matching pts with a gop in the list
  6963. // of gopsToAlignWith starting from the END of the list
  6964. this.alignGopsAtEnd_ = function(gops) {
  6965. var alignIndex, gopIndex, align, gop, alignEndIndex, matchFound;
  6966. alignIndex = gopsToAlignWith.length - 1;
  6967. gopIndex = gops.length - 1;
  6968. alignEndIndex = null;
  6969. matchFound = false;
  6970. while (alignIndex >= 0 && gopIndex >= 0) {
  6971. align = gopsToAlignWith[alignIndex];
  6972. gop = gops[gopIndex];
  6973. if (align.pts === gop.pts) {
  6974. matchFound = true;
  6975. break;
  6976. }
  6977. if (align.pts > gop.pts) {
  6978. alignIndex--;
  6979. continue;
  6980. }
  6981. if (alignIndex === gopsToAlignWith.length - 1) {
  6982. // gop.pts is greater than the last alignment candidate. If no match is found
  6983. // by the end of this loop, we still want to append gops that come after this
  6984. // point
  6985. alignEndIndex = gopIndex;
  6986. }
  6987. gopIndex--;
  6988. }
  6989. if (!matchFound && alignEndIndex === null) {
  6990. return null;
  6991. }
  6992. var trimIndex;
  6993. if (matchFound) {
  6994. trimIndex = gopIndex;
  6995. } else {
  6996. trimIndex = alignEndIndex;
  6997. }
  6998. if (trimIndex === 0) {
  6999. return gops;
  7000. }
  7001. var alignedGops = gops.slice(trimIndex);
  7002. var metadata = alignedGops.reduce(function(total, gop) {
  7003. total.byteLength += gop.byteLength;
  7004. total.duration += gop.duration;
  7005. total.nalCount += gop.nalCount;
  7006. return total;
  7007. }, { byteLength: 0, duration: 0, nalCount: 0 });
  7008. alignedGops.byteLength = metadata.byteLength;
  7009. alignedGops.duration = metadata.duration;
  7010. alignedGops.nalCount = metadata.nalCount;
  7011. alignedGops.pts = alignedGops[0].pts;
  7012. alignedGops.dts = alignedGops[0].dts;
  7013. return alignedGops;
  7014. };
  7015. this.alignGopsWith = function(newGopsToAlignWith) {
  7016. gopsToAlignWith = newGopsToAlignWith;
  7017. };
  7018. };
  7019. VideoSegmentStream.prototype = new Stream();
  7020. /**
  7021. * Store information about the start and end of the track and the
  7022. * duration for each frame/sample we process in order to calculate
  7023. * the baseMediaDecodeTime
  7024. */
  7025. collectDtsInfo = function(track, data) {
  7026. if (typeof data.pts === 'number') {
  7027. if (track.timelineStartInfo.pts === undefined) {
  7028. track.timelineStartInfo.pts = data.pts;
  7029. }
  7030. if (track.minSegmentPts === undefined) {
  7031. track.minSegmentPts = data.pts;
  7032. } else {
  7033. track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
  7034. }
  7035. if (track.maxSegmentPts === undefined) {
  7036. track.maxSegmentPts = data.pts;
  7037. } else {
  7038. track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
  7039. }
  7040. }
  7041. if (typeof data.dts === 'number') {
  7042. if (track.timelineStartInfo.dts === undefined) {
  7043. track.timelineStartInfo.dts = data.dts;
  7044. }
  7045. if (track.minSegmentDts === undefined) {
  7046. track.minSegmentDts = data.dts;
  7047. } else {
  7048. track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
  7049. }
  7050. if (track.maxSegmentDts === undefined) {
  7051. track.maxSegmentDts = data.dts;
  7052. } else {
  7053. track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
  7054. }
  7055. }
  7056. };
  7057. /**
  7058. * Clear values used to calculate the baseMediaDecodeTime between
  7059. * tracks
  7060. */
  7061. clearDtsInfo = function(track) {
  7062. delete track.minSegmentDts;
  7063. delete track.maxSegmentDts;
  7064. delete track.minSegmentPts;
  7065. delete track.maxSegmentPts;
  7066. };
  7067. /**
  7068. * Calculate the track's baseMediaDecodeTime based on the earliest
  7069. * DTS the transmuxer has ever seen and the minimum DTS for the
  7070. * current track
  7071. */
  7072. calculateTrackBaseMediaDecodeTime = function(track) {
  7073. var
  7074. baseMediaDecodeTime,
  7075. scale,
  7076. // Calculate the distance, in time, that this segment starts from the start
  7077. // of the timeline (earliest time seen since the transmuxer initialized)
  7078. timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts;
  7079. // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
  7080. // we want the start of the first segment to be placed
  7081. baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;
  7082. // Add to that the distance this segment is from the very first
  7083. baseMediaDecodeTime += timeSinceStartOfTimeline;
  7084. // baseMediaDecodeTime must not become negative
  7085. baseMediaDecodeTime = Math.max(0, baseMediaDecodeTime);
  7086. if (track.type === 'audio') {
  7087. // Audio has a different clock equal to the sampling_rate so we need to
  7088. // scale the PTS values into the clock rate of the track
  7089. scale = track.samplerate / ONE_SECOND_IN_TS;
  7090. baseMediaDecodeTime *= scale;
  7091. baseMediaDecodeTime = Math.floor(baseMediaDecodeTime);
  7092. }
  7093. return baseMediaDecodeTime;
  7094. };
  7095. /**
  7096. * A Stream that can combine multiple streams (ie. audio & video)
  7097. * into a single output segment for MSE. Also supports audio-only
  7098. * and video-only streams.
  7099. */
  7100. CoalesceStream = function(options, metadataStream) {
  7101. // Number of Tracks per output segment
  7102. // If greater than 1, we combine multiple
  7103. // tracks into a single segment
  7104. this.numberOfTracks = 0;
  7105. this.metadataStream = metadataStream;
  7106. if (typeof options.remux !== 'undefined') {
  7107. this.remuxTracks = !!options.remux;
  7108. } else {
  7109. this.remuxTracks = true;
  7110. }
  7111. this.pendingTracks = [];
  7112. this.videoTrack = null;
  7113. this.pendingBoxes = [];
  7114. this.pendingCaptions = [];
  7115. this.pendingMetadata = [];
  7116. this.pendingBytes = 0;
  7117. this.emittedTracks = 0;
  7118. CoalesceStream.prototype.init.call(this);
  7119. // Take output from multiple
  7120. this.push = function(output) {
  7121. // buffer incoming captions until the associated video segment
  7122. // finishes
  7123. if (output.text) {
  7124. return this.pendingCaptions.push(output);
  7125. }
  7126. // buffer incoming id3 tags until the final flush
  7127. if (output.frames) {
  7128. return this.pendingMetadata.push(output);
  7129. }
  7130. // Add this track to the list of pending tracks and store
  7131. // important information required for the construction of
  7132. // the final segment
  7133. this.pendingTracks.push(output.track);
  7134. this.pendingBoxes.push(output.boxes);
  7135. this.pendingBytes += output.boxes.byteLength;
  7136. if (output.track.type === 'video') {
  7137. this.videoTrack = output.track;
  7138. }
  7139. if (output.track.type === 'audio') {
  7140. this.audioTrack = output.track;
  7141. }
  7142. };
  7143. };
  7144. CoalesceStream.prototype = new Stream();
  7145. CoalesceStream.prototype.flush = function(flushSource) {
  7146. var
  7147. offset = 0,
  7148. event = {
  7149. captions: [],
  7150. captionStreams: {},
  7151. metadata: [],
  7152. info: {}
  7153. },
  7154. caption,
  7155. id3,
  7156. initSegment,
  7157. timelineStartPts = 0,
  7158. i;
  7159. if (this.pendingTracks.length < this.numberOfTracks) {
  7160. if (flushSource !== 'VideoSegmentStream' &&
  7161. flushSource !== 'AudioSegmentStream') {
  7162. // Return because we haven't received a flush from a data-generating
  7163. // portion of the segment (meaning that we have only recieved meta-data
  7164. // or captions.)
  7165. return;
  7166. } else if (this.remuxTracks) {
  7167. // Return until we have enough tracks from the pipeline to remux (if we
  7168. // are remuxing audio and video into a single MP4)
  7169. return;
  7170. } else if (this.pendingTracks.length === 0) {
  7171. // In the case where we receive a flush without any data having been
  7172. // received we consider it an emitted track for the purposes of coalescing
  7173. // `done` events.
  7174. // We do this for the case where there is an audio and video track in the
  7175. // segment but no audio data. (seen in several playlists with alternate
  7176. // audio tracks and no audio present in the main TS segments.)
  7177. this.emittedTracks++;
  7178. if (this.emittedTracks >= this.numberOfTracks) {
  7179. this.trigger('done');
  7180. this.emittedTracks = 0;
  7181. }
  7182. return;
  7183. }
  7184. }
  7185. if (this.videoTrack) {
  7186. timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  7187. VIDEO_PROPERTIES.forEach(function(prop) {
  7188. event.info[prop] = this.videoTrack[prop];
  7189. }, this);
  7190. } else if (this.audioTrack) {
  7191. timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  7192. AUDIO_PROPERTIES.forEach(function(prop) {
  7193. event.info[prop] = this.audioTrack[prop];
  7194. }, this);
  7195. }
  7196. if (this.pendingTracks.length === 1) {
  7197. event.type = this.pendingTracks[0].type;
  7198. } else {
  7199. event.type = 'combined';
  7200. }
  7201. this.emittedTracks += this.pendingTracks.length;
  7202. initSegment = mp4.initSegment(this.pendingTracks);
  7203. // Create a new typed array to hold the init segment
  7204. event.initSegment = new Uint8Array(initSegment.byteLength);
  7205. // Create an init segment containing a moov
  7206. // and track definitions
  7207. event.initSegment.set(initSegment);
  7208. // Create a new typed array to hold the moof+mdats
  7209. event.data = new Uint8Array(this.pendingBytes);
  7210. // Append each moof+mdat (one per track) together
  7211. for (i = 0; i < this.pendingBoxes.length; i++) {
  7212. event.data.set(this.pendingBoxes[i], offset);
  7213. offset += this.pendingBoxes[i].byteLength;
  7214. }
  7215. // Translate caption PTS times into second offsets into the
  7216. // video timeline for the segment, and add track info
  7217. for (i = 0; i < this.pendingCaptions.length; i++) {
  7218. caption = this.pendingCaptions[i];
  7219. caption.startTime = (caption.startPts - timelineStartPts);
  7220. caption.startTime /= 90e3;
  7221. caption.endTime = (caption.endPts - timelineStartPts);
  7222. caption.endTime /= 90e3;
  7223. event.captionStreams[caption.stream] = true;
  7224. event.captions.push(caption);
  7225. }
  7226. // Translate ID3 frame PTS times into second offsets into the
  7227. // video timeline for the segment
  7228. for (i = 0; i < this.pendingMetadata.length; i++) {
  7229. id3 = this.pendingMetadata[i];
  7230. id3.cueTime = (id3.pts - timelineStartPts);
  7231. id3.cueTime /= 90e3;
  7232. event.metadata.push(id3);
  7233. }
  7234. // We add this to every single emitted segment even though we only need
  7235. // it for the first
  7236. event.metadata.dispatchType = this.metadataStream.dispatchType;
  7237. // Reset stream state
  7238. this.pendingTracks.length = 0;
  7239. this.videoTrack = null;
  7240. this.pendingBoxes.length = 0;
  7241. this.pendingCaptions.length = 0;
  7242. this.pendingBytes = 0;
  7243. this.pendingMetadata.length = 0;
  7244. // Emit the built segment
  7245. this.trigger('data', event);
  7246. // Only emit `done` if all tracks have been flushed and emitted
  7247. if (this.emittedTracks >= this.numberOfTracks) {
  7248. this.trigger('done');
  7249. this.emittedTracks = 0;
  7250. }
  7251. };
  7252. /**
  7253. * A Stream that expects MP2T binary data as input and produces
  7254. * corresponding media segments, suitable for use with Media Source
  7255. * Extension (MSE) implementations that support the ISO BMFF byte
  7256. * stream format, like Chrome.
  7257. */
  7258. Transmuxer = function(options) {
  7259. var
  7260. self = this,
  7261. hasFlushed = true,
  7262. videoTrack,
  7263. audioTrack;
  7264. Transmuxer.prototype.init.call(this);
  7265. options = options || {};
  7266. this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
  7267. this.transmuxPipeline_ = {};
  7268. this.setupAacPipeline = function() {
  7269. var pipeline = {};
  7270. this.transmuxPipeline_ = pipeline;
  7271. pipeline.type = 'aac';
  7272. pipeline.metadataStream = new m2ts.MetadataStream();
  7273. // set up the parsing pipeline
  7274. pipeline.aacStream = new AacStream();
  7275. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  7276. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  7277. pipeline.adtsStream = new AdtsStream();
  7278. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  7279. pipeline.headOfPipeline = pipeline.aacStream;
  7280. pipeline.aacStream
  7281. .pipe(pipeline.audioTimestampRolloverStream)
  7282. .pipe(pipeline.adtsStream);
  7283. pipeline.aacStream
  7284. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  7285. .pipe(pipeline.metadataStream)
  7286. .pipe(pipeline.coalesceStream);
  7287. pipeline.metadataStream.on('timestamp', function(frame) {
  7288. pipeline.aacStream.setTimestamp(frame.timeStamp);
  7289. });
  7290. pipeline.aacStream.on('data', function(data) {
  7291. if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
  7292. audioTrack = audioTrack || {
  7293. timelineStartInfo: {
  7294. baseMediaDecodeTime: self.baseMediaDecodeTime
  7295. },
  7296. codec: 'adts',
  7297. type: 'audio'
  7298. };
  7299. // hook up the audio segment stream to the first track with aac data
  7300. pipeline.coalesceStream.numberOfTracks++;
  7301. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  7302. // Set up the final part of the audio pipeline
  7303. pipeline.adtsStream
  7304. .pipe(pipeline.audioSegmentStream)
  7305. .pipe(pipeline.coalesceStream);
  7306. }
  7307. });
  7308. // Re-emit any data coming from the coalesce stream to the outside world
  7309. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  7310. // Let the consumer know we have finished flushing the entire pipeline
  7311. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  7312. };
  7313. this.setupTsPipeline = function() {
  7314. var pipeline = {};
  7315. this.transmuxPipeline_ = pipeline;
  7316. pipeline.type = 'ts';
  7317. pipeline.metadataStream = new m2ts.MetadataStream();
  7318. // set up the parsing pipeline
  7319. pipeline.packetStream = new m2ts.TransportPacketStream();
  7320. pipeline.parseStream = new m2ts.TransportParseStream();
  7321. pipeline.elementaryStream = new m2ts.ElementaryStream();
  7322. pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
  7323. pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
  7324. pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
  7325. pipeline.adtsStream = new AdtsStream();
  7326. pipeline.h264Stream = new H264Stream();
  7327. pipeline.captionStream = new m2ts.CaptionStream();
  7328. pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
  7329. pipeline.headOfPipeline = pipeline.packetStream;
  7330. // disassemble MPEG2-TS packets into elementary streams
  7331. pipeline.packetStream
  7332. .pipe(pipeline.parseStream)
  7333. .pipe(pipeline.elementaryStream);
  7334. // !!THIS ORDER IS IMPORTANT!!
  7335. // demux the streams
  7336. pipeline.elementaryStream
  7337. .pipe(pipeline.videoTimestampRolloverStream)
  7338. .pipe(pipeline.h264Stream);
  7339. pipeline.elementaryStream
  7340. .pipe(pipeline.audioTimestampRolloverStream)
  7341. .pipe(pipeline.adtsStream);
  7342. pipeline.elementaryStream
  7343. .pipe(pipeline.timedMetadataTimestampRolloverStream)
  7344. .pipe(pipeline.metadataStream)
  7345. .pipe(pipeline.coalesceStream);
  7346. // Hook up CEA-608/708 caption stream
  7347. pipeline.h264Stream.pipe(pipeline.captionStream)
  7348. .pipe(pipeline.coalesceStream);
  7349. pipeline.elementaryStream.on('data', function(data) {
  7350. var i;
  7351. if (data.type === 'metadata') {
  7352. i = data.tracks.length;
  7353. // scan the tracks listed in the metadata
  7354. while (i--) {
  7355. if (!videoTrack && data.tracks[i].type === 'video') {
  7356. videoTrack = data.tracks[i];
  7357. videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  7358. } else if (!audioTrack && data.tracks[i].type === 'audio') {
  7359. audioTrack = data.tracks[i];
  7360. audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
  7361. }
  7362. }
  7363. // hook up the video segment stream to the first track with h264 data
  7364. if (videoTrack && !pipeline.videoSegmentStream) {
  7365. pipeline.coalesceStream.numberOfTracks++;
  7366. pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack, options);
  7367. pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
  7368. // When video emits timelineStartInfo data after a flush, we forward that
  7369. // info to the AudioSegmentStream, if it exists, because video timeline
  7370. // data takes precedence.
  7371. if (audioTrack) {
  7372. audioTrack.timelineStartInfo = timelineStartInfo;
  7373. // On the first segment we trim AAC frames that exist before the
  7374. // very earliest DTS we have seen in video because Chrome will
  7375. // interpret any video track with a baseMediaDecodeTime that is
  7376. // non-zero as a gap.
  7377. pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
  7378. }
  7379. });
  7380. pipeline.videoSegmentStream.on('processedGopsInfo',
  7381. self.trigger.bind(self, 'gopInfo'));
  7382. pipeline.videoSegmentStream.on('baseMediaDecodeTime', function(baseMediaDecodeTime) {
  7383. if (audioTrack) {
  7384. pipeline.audioSegmentStream.setVideoBaseMediaDecodeTime(baseMediaDecodeTime);
  7385. }
  7386. });
  7387. // Set up the final part of the video pipeline
  7388. pipeline.h264Stream
  7389. .pipe(pipeline.videoSegmentStream)
  7390. .pipe(pipeline.coalesceStream);
  7391. }
  7392. if (audioTrack && !pipeline.audioSegmentStream) {
  7393. // hook up the audio segment stream to the first track with aac data
  7394. pipeline.coalesceStream.numberOfTracks++;
  7395. pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
  7396. // Set up the final part of the audio pipeline
  7397. pipeline.adtsStream
  7398. .pipe(pipeline.audioSegmentStream)
  7399. .pipe(pipeline.coalesceStream);
  7400. }
  7401. }
  7402. });
  7403. // Re-emit any data coming from the coalesce stream to the outside world
  7404. pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
  7405. // Let the consumer know we have finished flushing the entire pipeline
  7406. pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  7407. };
  7408. // hook up the segment streams once track metadata is delivered
  7409. this.setBaseMediaDecodeTime = function(baseMediaDecodeTime) {
  7410. var pipeline = this.transmuxPipeline_;
  7411. this.baseMediaDecodeTime = baseMediaDecodeTime;
  7412. if (audioTrack) {
  7413. audioTrack.timelineStartInfo.dts = undefined;
  7414. audioTrack.timelineStartInfo.pts = undefined;
  7415. clearDtsInfo(audioTrack);
  7416. audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  7417. if (pipeline.audioTimestampRolloverStream) {
  7418. pipeline.audioTimestampRolloverStream.discontinuity();
  7419. }
  7420. }
  7421. if (videoTrack) {
  7422. if (pipeline.videoSegmentStream) {
  7423. pipeline.videoSegmentStream.gopCache_ = [];
  7424. pipeline.videoTimestampRolloverStream.discontinuity();
  7425. }
  7426. videoTrack.timelineStartInfo.dts = undefined;
  7427. videoTrack.timelineStartInfo.pts = undefined;
  7428. clearDtsInfo(videoTrack);
  7429. pipeline.captionStream.reset();
  7430. videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
  7431. }
  7432. if (pipeline.timedMetadataTimestampRolloverStream) {
  7433. pipeline.timedMetadataTimestampRolloverStream.discontinuity();
  7434. }
  7435. };
  7436. this.setAudioAppendStart = function(timestamp) {
  7437. if (audioTrack) {
  7438. this.transmuxPipeline_.audioSegmentStream.setAudioAppendStart(timestamp);
  7439. }
  7440. };
  7441. this.alignGopsWith = function(gopsToAlignWith) {
  7442. if (videoTrack && this.transmuxPipeline_.videoSegmentStream) {
  7443. this.transmuxPipeline_.videoSegmentStream.alignGopsWith(gopsToAlignWith);
  7444. }
  7445. };
  7446. // feed incoming data to the front of the parsing pipeline
  7447. this.push = function(data) {
  7448. if (hasFlushed) {
  7449. var isAac = isLikelyAacData(data);
  7450. if (isAac && this.transmuxPipeline_.type !== 'aac') {
  7451. this.setupAacPipeline();
  7452. } else if (!isAac && this.transmuxPipeline_.type !== 'ts') {
  7453. this.setupTsPipeline();
  7454. }
  7455. hasFlushed = false;
  7456. }
  7457. this.transmuxPipeline_.headOfPipeline.push(data);
  7458. };
  7459. // flush any buffered data
  7460. this.flush = function() {
  7461. hasFlushed = true;
  7462. // Start at the top of the pipeline and flush all pending work
  7463. this.transmuxPipeline_.headOfPipeline.flush();
  7464. };
  7465. // Caption data has to be reset when seeking outside buffered range
  7466. this.resetCaptions = function() {
  7467. if (this.transmuxPipeline_.captionStream) {
  7468. this.transmuxPipeline_.captionStream.reset();
  7469. }
  7470. };
  7471. };
  7472. Transmuxer.prototype = new Stream();
  7473. module.exports = {
  7474. Transmuxer: Transmuxer,
  7475. VideoSegmentStream: VideoSegmentStream,
  7476. AudioSegmentStream: AudioSegmentStream,
  7477. AUDIO_PROPERTIES: AUDIO_PROPERTIES,
  7478. VIDEO_PROPERTIES: VIDEO_PROPERTIES
  7479. };
  7480. },{"../aac":18,"../codecs/adts.js":19,"../codecs/h264":20,"../data/silence":21,"../m2ts/m2ts.js":29,"../utils/clock":36,"../utils/stream.js":38,"./mp4-generator.js":34}],36:[function(require,module,exports){
  7481. var
  7482. ONE_SECOND_IN_TS = 90000, // 90kHz clock
  7483. secondsToVideoTs,
  7484. secondsToAudioTs,
  7485. videoTsToSeconds,
  7486. audioTsToSeconds,
  7487. audioTsToVideoTs,
  7488. videoTsToAudioTs;
  7489. secondsToVideoTs = function(seconds) {
  7490. return seconds * ONE_SECOND_IN_TS;
  7491. };
  7492. secondsToAudioTs = function(seconds, sampleRate) {
  7493. return seconds * sampleRate;
  7494. };
  7495. videoTsToSeconds = function(timestamp) {
  7496. return timestamp / ONE_SECOND_IN_TS;
  7497. };
  7498. audioTsToSeconds = function(timestamp, sampleRate) {
  7499. return timestamp / sampleRate;
  7500. };
  7501. audioTsToVideoTs = function(timestamp, sampleRate) {
  7502. return secondsToVideoTs(audioTsToSeconds(timestamp, sampleRate));
  7503. };
  7504. videoTsToAudioTs = function(timestamp, sampleRate) {
  7505. return secondsToAudioTs(videoTsToSeconds(timestamp), sampleRate);
  7506. };
  7507. module.exports = {
  7508. secondsToVideoTs: secondsToVideoTs,
  7509. secondsToAudioTs: secondsToAudioTs,
  7510. videoTsToSeconds: videoTsToSeconds,
  7511. audioTsToSeconds: audioTsToSeconds,
  7512. audioTsToVideoTs: audioTsToVideoTs,
  7513. videoTsToAudioTs: videoTsToAudioTs
  7514. };
  7515. },{}],37:[function(require,module,exports){
  7516. 'use strict';
  7517. var ExpGolomb;
  7518. /**
  7519. * Parser for exponential Golomb codes, a variable-bitwidth number encoding
  7520. * scheme used by h264.
  7521. */
  7522. ExpGolomb = function(workingData) {
  7523. var
  7524. // the number of bytes left to examine in workingData
  7525. workingBytesAvailable = workingData.byteLength,
  7526. // the current word being examined
  7527. workingWord = 0, // :uint
  7528. // the number of bits left to examine in the current word
  7529. workingBitsAvailable = 0; // :uint;
  7530. // ():uint
  7531. this.length = function() {
  7532. return (8 * workingBytesAvailable);
  7533. };
  7534. // ():uint
  7535. this.bitsAvailable = function() {
  7536. return (8 * workingBytesAvailable) + workingBitsAvailable;
  7537. };
  7538. // ():void
  7539. this.loadWord = function() {
  7540. var
  7541. position = workingData.byteLength - workingBytesAvailable,
  7542. workingBytes = new Uint8Array(4),
  7543. availableBytes = Math.min(4, workingBytesAvailable);
  7544. if (availableBytes === 0) {
  7545. throw new Error('no bytes available');
  7546. }
  7547. workingBytes.set(workingData.subarray(position,
  7548. position + availableBytes));
  7549. workingWord = new DataView(workingBytes.buffer).getUint32(0);
  7550. // track the amount of workingData that has been processed
  7551. workingBitsAvailable = availableBytes * 8;
  7552. workingBytesAvailable -= availableBytes;
  7553. };
  7554. // (count:int):void
  7555. this.skipBits = function(count) {
  7556. var skipBytes; // :int
  7557. if (workingBitsAvailable > count) {
  7558. workingWord <<= count;
  7559. workingBitsAvailable -= count;
  7560. } else {
  7561. count -= workingBitsAvailable;
  7562. skipBytes = Math.floor(count / 8);
  7563. count -= (skipBytes * 8);
  7564. workingBytesAvailable -= skipBytes;
  7565. this.loadWord();
  7566. workingWord <<= count;
  7567. workingBitsAvailable -= count;
  7568. }
  7569. };
  7570. // (size:int):uint
  7571. this.readBits = function(size) {
  7572. var
  7573. bits = Math.min(workingBitsAvailable, size), // :uint
  7574. valu = workingWord >>> (32 - bits); // :uint
  7575. // if size > 31, handle error
  7576. workingBitsAvailable -= bits;
  7577. if (workingBitsAvailable > 0) {
  7578. workingWord <<= bits;
  7579. } else if (workingBytesAvailable > 0) {
  7580. this.loadWord();
  7581. }
  7582. bits = size - bits;
  7583. if (bits > 0) {
  7584. return valu << bits | this.readBits(bits);
  7585. }
  7586. return valu;
  7587. };
  7588. // ():uint
  7589. this.skipLeadingZeros = function() {
  7590. var leadingZeroCount; // :uint
  7591. for (leadingZeroCount = 0; leadingZeroCount < workingBitsAvailable; ++leadingZeroCount) {
  7592. if ((workingWord & (0x80000000 >>> leadingZeroCount)) !== 0) {
  7593. // the first bit of working word is 1
  7594. workingWord <<= leadingZeroCount;
  7595. workingBitsAvailable -= leadingZeroCount;
  7596. return leadingZeroCount;
  7597. }
  7598. }
  7599. // we exhausted workingWord and still have not found a 1
  7600. this.loadWord();
  7601. return leadingZeroCount + this.skipLeadingZeros();
  7602. };
  7603. // ():void
  7604. this.skipUnsignedExpGolomb = function() {
  7605. this.skipBits(1 + this.skipLeadingZeros());
  7606. };
  7607. // ():void
  7608. this.skipExpGolomb = function() {
  7609. this.skipBits(1 + this.skipLeadingZeros());
  7610. };
  7611. // ():uint
  7612. this.readUnsignedExpGolomb = function() {
  7613. var clz = this.skipLeadingZeros(); // :uint
  7614. return this.readBits(clz + 1) - 1;
  7615. };
  7616. // ():int
  7617. this.readExpGolomb = function() {
  7618. var valu = this.readUnsignedExpGolomb(); // :int
  7619. if (0x01 & valu) {
  7620. // the number is odd if the low order bit is set
  7621. return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
  7622. }
  7623. return -1 * (valu >>> 1); // divide by two then make it negative
  7624. };
  7625. // Some convenience functions
  7626. // :Boolean
  7627. this.readBoolean = function() {
  7628. return this.readBits(1) === 1;
  7629. };
  7630. // ():int
  7631. this.readUnsignedByte = function() {
  7632. return this.readBits(8);
  7633. };
  7634. this.loadWord();
  7635. };
  7636. module.exports = ExpGolomb;
  7637. },{}],38:[function(require,module,exports){
  7638. /**
  7639. * mux.js
  7640. *
  7641. * Copyright (c) 2014 Brightcove
  7642. * All rights reserved.
  7643. *
  7644. * A lightweight readable stream implemention that handles event dispatching.
  7645. * Objects that inherit from streams should call init in their constructors.
  7646. */
  7647. 'use strict';
  7648. var Stream = function() {
  7649. this.init = function() {
  7650. var listeners = {};
  7651. /**
  7652. * Add a listener for a specified event type.
  7653. * @param type {string} the event name
  7654. * @param listener {function} the callback to be invoked when an event of
  7655. * the specified type occurs
  7656. */
  7657. this.on = function(type, listener) {
  7658. if (!listeners[type]) {
  7659. listeners[type] = [];
  7660. }
  7661. listeners[type] = listeners[type].concat(listener);
  7662. };
  7663. /**
  7664. * Remove a listener for a specified event type.
  7665. * @param type {string} the event name
  7666. * @param listener {function} a function previously registered for this
  7667. * type of event through `on`
  7668. */
  7669. this.off = function(type, listener) {
  7670. var index;
  7671. if (!listeners[type]) {
  7672. return false;
  7673. }
  7674. index = listeners[type].indexOf(listener);
  7675. listeners[type] = listeners[type].slice();
  7676. listeners[type].splice(index, 1);
  7677. return index > -1;
  7678. };
  7679. /**
  7680. * Trigger an event of the specified type on this stream. Any additional
  7681. * arguments to this function are passed as parameters to event listeners.
  7682. * @param type {string} the event name
  7683. */
  7684. this.trigger = function(type) {
  7685. var callbacks, i, length, args;
  7686. callbacks = listeners[type];
  7687. if (!callbacks) {
  7688. return;
  7689. }
  7690. // Slicing the arguments on every invocation of this method
  7691. // can add a significant amount of overhead. Avoid the
  7692. // intermediate object creation for the common case of a
  7693. // single callback argument
  7694. if (arguments.length === 2) {
  7695. length = callbacks.length;
  7696. for (i = 0; i < length; ++i) {
  7697. callbacks[i].call(this, arguments[1]);
  7698. }
  7699. } else {
  7700. args = [];
  7701. i = arguments.length;
  7702. for (i = 1; i < arguments.length; ++i) {
  7703. args.push(arguments[i]);
  7704. }
  7705. length = callbacks.length;
  7706. for (i = 0; i < length; ++i) {
  7707. callbacks[i].apply(this, args);
  7708. }
  7709. }
  7710. };
  7711. /**
  7712. * Destroys the stream and cleans up.
  7713. */
  7714. this.dispose = function() {
  7715. listeners = {};
  7716. };
  7717. };
  7718. };
  7719. /**
  7720. * Forwards all `data` events on this stream to the destination stream. The
  7721. * destination stream should provide a method `push` to receive the data
  7722. * events as they arrive.
  7723. * @param destination {stream} the stream that will receive all `data` events
  7724. * @param autoFlush {boolean} if false, we will not call `flush` on the destination
  7725. * when the current stream emits a 'done' event
  7726. * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  7727. */
  7728. Stream.prototype.pipe = function(destination) {
  7729. this.on('data', function(data) {
  7730. destination.push(data);
  7731. });
  7732. this.on('done', function(flushSource) {
  7733. destination.flush(flushSource);
  7734. });
  7735. return destination;
  7736. };
  7737. // Default stream functions that are expected to be overridden to perform
  7738. // actual work. These are provided by the prototype as a sort of no-op
  7739. // implementation so that we don't have to check for their existence in the
  7740. // `pipe` function above.
  7741. Stream.prototype.push = function(data) {
  7742. this.trigger('data', data);
  7743. };
  7744. Stream.prototype.flush = function(flushSource) {
  7745. this.trigger('done', flushSource);
  7746. };
  7747. module.exports = Stream;
  7748. },{}],39:[function(require,module,exports){
  7749. var trim = require('trim')
  7750. , forEach = require('for-each')
  7751. , isArray = function(arg) {
  7752. return Object.prototype.toString.call(arg) === '[object Array]';
  7753. }
  7754. module.exports = function (headers) {
  7755. if (!headers)
  7756. return {}
  7757. var result = {}
  7758. forEach(
  7759. trim(headers).split('\n')
  7760. , function (row) {
  7761. var index = row.indexOf(':')
  7762. , key = trim(row.slice(0, index)).toLowerCase()
  7763. , value = trim(row.slice(index + 1))
  7764. if (typeof(result[key]) === 'undefined') {
  7765. result[key] = value
  7766. } else if (isArray(result[key])) {
  7767. result[key].push(value)
  7768. } else {
  7769. result[key] = [ result[key], value ]
  7770. }
  7771. }
  7772. )
  7773. return result
  7774. }
  7775. },{"for-each":14,"trim":41}],40:[function(require,module,exports){
  7776. module.exports = SafeParseTuple
  7777. function SafeParseTuple(obj, reviver) {
  7778. var json
  7779. var error = null
  7780. try {
  7781. json = JSON.parse(obj, reviver)
  7782. } catch (err) {
  7783. error = err
  7784. }
  7785. return [error, json]
  7786. }
  7787. },{}],41:[function(require,module,exports){
  7788. exports = module.exports = trim;
  7789. function trim(str){
  7790. return str.replace(/^\s*|\s*$/g, '');
  7791. }
  7792. exports.left = function(str){
  7793. return str.replace(/^\s*/, '');
  7794. };
  7795. exports.right = function(str){
  7796. return str.replace(/\s*$/, '');
  7797. };
  7798. },{}],42:[function(require,module,exports){
  7799. function clean (s) {
  7800. return s.replace(/\n\r?\s*/g, '')
  7801. }
  7802. module.exports = function tsml (sa) {
  7803. var s = ''
  7804. , i = 0
  7805. for (; i < arguments.length; i++)
  7806. s += clean(sa[i]) + (arguments[i + 1] || '')
  7807. return s
  7808. }
  7809. },{}],43:[function(require,module,exports){
  7810. 'use strict';
  7811. exports.__esModule = true;
  7812. var _button = require('./button.js');
  7813. var _button2 = _interopRequireDefault(_button);
  7814. var _component = require('./component.js');
  7815. var _component2 = _interopRequireDefault(_component);
  7816. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7817. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  7818. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  7819. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  7820. * @file big-play-button.js
  7821. */
  7822. /**
  7823. * The initial play button that shows before the video has played. The hiding of the
  7824. * `BigPlayButton` get done via CSS and `Player` states.
  7825. *
  7826. * @extends Button
  7827. */
  7828. var BigPlayButton = function (_Button) {
  7829. _inherits(BigPlayButton, _Button);
  7830. function BigPlayButton(player, options) {
  7831. _classCallCheck(this, BigPlayButton);
  7832. var _this = _possibleConstructorReturn(this, _Button.call(this, player, options));
  7833. _this.mouseused_ = false;
  7834. _this.on('mousedown', _this.handleMouseDown);
  7835. return _this;
  7836. }
  7837. /**
  7838. * Builds the default DOM `className`.
  7839. *
  7840. * @return {string}
  7841. * The DOM `className` for this object. Always returns 'vjs-big-play-button'.
  7842. */
  7843. BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
  7844. return 'vjs-big-play-button';
  7845. };
  7846. /**
  7847. * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
  7848. * for more detailed information on what a click can be.
  7849. *
  7850. * @param {EventTarget~Event} event
  7851. * The `keydown`, `tap`, or `click` event that caused this function to be
  7852. * called.
  7853. *
  7854. * @listens tap
  7855. * @listens click
  7856. */
  7857. BigPlayButton.prototype.handleClick = function handleClick(event) {
  7858. var playPromise = this.player_.play();
  7859. // exit early if clicked via the mouse
  7860. if (this.mouseused_ && event.clientX && event.clientY) {
  7861. return;
  7862. }
  7863. var cb = this.player_.getChild('controlBar');
  7864. var playToggle = cb && cb.getChild('playToggle');
  7865. if (!playToggle) {
  7866. this.player_.focus();
  7867. return;
  7868. }
  7869. var playFocus = function playFocus() {
  7870. return playToggle.focus();
  7871. };
  7872. if (playPromise && playPromise.then) {
  7873. var ignoreRejectedPlayPromise = function ignoreRejectedPlayPromise() {};
  7874. playPromise.then(playFocus, ignoreRejectedPlayPromise);
  7875. } else {
  7876. this.setTimeout(playFocus, 1);
  7877. }
  7878. };
  7879. BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
  7880. this.mouseused_ = false;
  7881. _Button.prototype.handleKeyPress.call(this, event);
  7882. };
  7883. BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
  7884. this.mouseused_ = true;
  7885. };
  7886. return BigPlayButton;
  7887. }(_button2['default']);
  7888. /**
  7889. * The text that should display over the `BigPlayButton`s controls. Added to for localization.
  7890. *
  7891. * @type {string}
  7892. * @private
  7893. */
  7894. BigPlayButton.prototype.controlText_ = 'Play Video';
  7895. _component2['default'].registerComponent('BigPlayButton', BigPlayButton);
  7896. exports['default'] = BigPlayButton;
  7897. },{"./button.js":44,"./component.js":47}],44:[function(require,module,exports){
  7898. 'use strict';
  7899. exports.__esModule = true;
  7900. var _clickableComponent = require('./clickable-component.js');
  7901. var _clickableComponent2 = _interopRequireDefault(_clickableComponent);
  7902. var _component = require('./component');
  7903. var _component2 = _interopRequireDefault(_component);
  7904. var _log = require('./utils/log.js');
  7905. var _log2 = _interopRequireDefault(_log);
  7906. var _obj = require('./utils/obj');
  7907. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  7908. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  7909. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  7910. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  7911. * @file button.js
  7912. */
  7913. /**
  7914. * Base class for all buttons.
  7915. *
  7916. * @extends ClickableComponent
  7917. */
  7918. var Button = function (_ClickableComponent) {
  7919. _inherits(Button, _ClickableComponent);
  7920. function Button() {
  7921. _classCallCheck(this, Button);
  7922. return _possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
  7923. }
  7924. /**
  7925. * Create the `Button`s DOM element.
  7926. *
  7927. * @param {string} [tag=button]
  7928. * Element's node type. e.g. 'button'
  7929. *
  7930. * @param {Object} [props={}]
  7931. * An object of properties that should be set on the element.
  7932. *
  7933. * @param {Object} [attributes={}]
  7934. * An object of attributes that should be set on the element.
  7935. *
  7936. * @return {Element}
  7937. * The element that gets created.
  7938. */
  7939. Button.prototype.createEl = function createEl() {
  7940. var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'button';
  7941. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  7942. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  7943. props = (0, _obj.assign)({
  7944. className: this.buildCSSClass()
  7945. }, props);
  7946. if (tag !== 'button') {
  7947. _log2['default'].warn('Creating a Button with an HTML element of ' + tag + ' is deprecated; use ClickableComponent instead.');
  7948. // Add properties for clickable element which is not a native HTML button
  7949. props = (0, _obj.assign)({
  7950. tabIndex: 0
  7951. }, props);
  7952. // Add ARIA attributes for clickable element which is not a native HTML button
  7953. attributes = (0, _obj.assign)({
  7954. role: 'button'
  7955. }, attributes);
  7956. }
  7957. // Add attributes for button element
  7958. attributes = (0, _obj.assign)({
  7959. // Necessary since the default button type is "submit"
  7960. 'type': 'button',
  7961. // let the screen reader user know that the text of the button may change
  7962. 'aria-live': 'polite'
  7963. }, attributes);
  7964. var el = _component2['default'].prototype.createEl.call(this, tag, props, attributes);
  7965. this.createControlTextEl(el);
  7966. return el;
  7967. };
  7968. /**
  7969. * Add a child `Component` inside of this `Button`.
  7970. *
  7971. * @param {string|Component} child
  7972. * The name or instance of a child to add.
  7973. *
  7974. * @param {Object} [options={}]
  7975. * The key/value store of options that will get passed to children of
  7976. * the child.
  7977. *
  7978. * @return {Component}
  7979. * The `Component` that gets added as a child. When using a string the
  7980. * `Component` will get created by this process.
  7981. *
  7982. * @deprecated since version 5
  7983. */
  7984. Button.prototype.addChild = function addChild(child) {
  7985. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  7986. var className = this.constructor.name;
  7987. _log2['default'].warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
  7988. // Avoid the error message generated by ClickableComponent's addChild method
  7989. return _component2['default'].prototype.addChild.call(this, child, options);
  7990. };
  7991. /**
  7992. * Enable the `Button` element so that it can be activated or clicked. Use this with
  7993. * {@link Button#disable}.
  7994. */
  7995. Button.prototype.enable = function enable() {
  7996. _ClickableComponent.prototype.enable.call(this);
  7997. this.el_.removeAttribute('disabled');
  7998. };
  7999. /**
  8000. * Enable the `Button` element so that it cannot be activated or clicked. Use this with
  8001. * {@link Button#enable}.
  8002. */
  8003. Button.prototype.disable = function disable() {
  8004. _ClickableComponent.prototype.disable.call(this);
  8005. this.el_.setAttribute('disabled', 'disabled');
  8006. };
  8007. /**
  8008. * This gets called when a `Button` has focus and `keydown` is triggered via a key
  8009. * press.
  8010. *
  8011. * @param {EventTarget~Event} event
  8012. * The event that caused this function to get called.
  8013. *
  8014. * @listens keydown
  8015. */
  8016. Button.prototype.handleKeyPress = function handleKeyPress(event) {
  8017. // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
  8018. if (event.which === 32 || event.which === 13) {
  8019. return;
  8020. }
  8021. // Pass keypress handling up for unsupported keys
  8022. _ClickableComponent.prototype.handleKeyPress.call(this, event);
  8023. };
  8024. return Button;
  8025. }(_clickableComponent2['default']);
  8026. _component2['default'].registerComponent('Button', Button);
  8027. exports['default'] = Button;
  8028. },{"./clickable-component.js":45,"./component":47,"./utils/log.js":128,"./utils/obj":130}],45:[function(require,module,exports){
  8029. 'use strict';
  8030. exports.__esModule = true;
  8031. var _component = require('./component');
  8032. var _component2 = _interopRequireDefault(_component);
  8033. var _dom = require('./utils/dom.js');
  8034. var Dom = _interopRequireWildcard(_dom);
  8035. var _events = require('./utils/events.js');
  8036. var Events = _interopRequireWildcard(_events);
  8037. var _fn = require('./utils/fn.js');
  8038. var Fn = _interopRequireWildcard(_fn);
  8039. var _log = require('./utils/log.js');
  8040. var _log2 = _interopRequireDefault(_log);
  8041. var _document = require('global/document');
  8042. var _document2 = _interopRequireDefault(_document);
  8043. var _obj = require('./utils/obj');
  8044. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  8045. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  8046. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  8047. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  8048. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  8049. * @file button.js
  8050. */
  8051. /**
  8052. * Clickable Component which is clickable or keyboard actionable,
  8053. * but is not a native HTML button.
  8054. *
  8055. * @extends Component
  8056. */
  8057. var ClickableComponent = function (_Component) {
  8058. _inherits(ClickableComponent, _Component);
  8059. /**
  8060. * Creates an instance of this class.
  8061. *
  8062. * @param {Player} player
  8063. * The `Player` that this class should be attached to.
  8064. *
  8065. * @param {Object} [options]
  8066. * The key/value store of player options.
  8067. */
  8068. function ClickableComponent(player, options) {
  8069. _classCallCheck(this, ClickableComponent);
  8070. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  8071. _this.emitTapEvents();
  8072. _this.enable();
  8073. return _this;
  8074. }
  8075. /**
  8076. * Create the `Component`s DOM element.
  8077. *
  8078. * @param {string} [tag=div]
  8079. * The element's node type.
  8080. *
  8081. * @param {Object} [props={}]
  8082. * An object of properties that should be set on the element.
  8083. *
  8084. * @param {Object} [attributes={}]
  8085. * An object of attributes that should be set on the element.
  8086. *
  8087. * @return {Element}
  8088. * The element that gets created.
  8089. */
  8090. ClickableComponent.prototype.createEl = function createEl() {
  8091. var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  8092. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  8093. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  8094. props = (0, _obj.assign)({
  8095. className: this.buildCSSClass(),
  8096. tabIndex: 0
  8097. }, props);
  8098. if (tag === 'button') {
  8099. _log2['default'].error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
  8100. }
  8101. // Add ARIA attributes for clickable element which is not a native HTML button
  8102. attributes = (0, _obj.assign)({
  8103. 'role': 'button',
  8104. // let the screen reader user know that the text of the element may change
  8105. 'aria-live': 'polite'
  8106. }, attributes);
  8107. this.tabIndex_ = props.tabIndex;
  8108. var el = _Component.prototype.createEl.call(this, tag, props, attributes);
  8109. this.createControlTextEl(el);
  8110. return el;
  8111. };
  8112. /**
  8113. * Create a control text element on this `Component`
  8114. *
  8115. * @param {Element} [el]
  8116. * Parent element for the control text.
  8117. *
  8118. * @return {Element}
  8119. * The control text element that gets created.
  8120. */
  8121. ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
  8122. this.controlTextEl_ = Dom.createEl('span', {
  8123. className: 'vjs-control-text'
  8124. });
  8125. if (el) {
  8126. el.appendChild(this.controlTextEl_);
  8127. }
  8128. this.controlText(this.controlText_, el);
  8129. return this.controlTextEl_;
  8130. };
  8131. /**
  8132. * Get or set the localize text to use for the controls on the `Component`.
  8133. *
  8134. * @param {string} [text]
  8135. * Control text for element.
  8136. *
  8137. * @param {Element} [el=this.el()]
  8138. * Element to set the title on.
  8139. *
  8140. * @return {string|ClickableComponent}
  8141. * - The control text when getting
  8142. * - Returns itself when setting; method can be chained.
  8143. */
  8144. ClickableComponent.prototype.controlText = function controlText(text) {
  8145. var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
  8146. if (!text) {
  8147. return this.controlText_ || 'Need Text';
  8148. }
  8149. var localizedText = this.localize(text);
  8150. this.controlText_ = text;
  8151. this.controlTextEl_.innerHTML = localizedText;
  8152. if (!this.nonIconControl) {
  8153. // Set title attribute if only an icon is shown
  8154. el.setAttribute('title', localizedText);
  8155. }
  8156. return this;
  8157. };
  8158. /**
  8159. * Builds the default DOM `className`.
  8160. *
  8161. * @return {string}
  8162. * The DOM `className` for this object.
  8163. */
  8164. ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
  8165. return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
  8166. };
  8167. /**
  8168. * Enable this `Component`s element.
  8169. *
  8170. * @return {ClickableComponent}
  8171. * Returns itself; method can be chained.
  8172. */
  8173. ClickableComponent.prototype.enable = function enable() {
  8174. this.removeClass('vjs-disabled');
  8175. this.el_.setAttribute('aria-disabled', 'false');
  8176. if (typeof this.tabIndex_ !== 'undefined') {
  8177. this.el_.setAttribute('tabIndex', this.tabIndex_);
  8178. }
  8179. this.off(['tap', 'click'], this.handleClick);
  8180. this.off('focus', this.handleFocus);
  8181. this.off('blur', this.handleBlur);
  8182. this.on(['tap', 'click'], this.handleClick);
  8183. this.on('focus', this.handleFocus);
  8184. this.on('blur', this.handleBlur);
  8185. return this;
  8186. };
  8187. /**
  8188. * Disable this `Component`s element.
  8189. *
  8190. * @return {ClickableComponent}
  8191. * Returns itself; method can be chained.
  8192. */
  8193. ClickableComponent.prototype.disable = function disable() {
  8194. this.addClass('vjs-disabled');
  8195. this.el_.setAttribute('aria-disabled', 'true');
  8196. if (typeof this.tabIndex_ !== 'undefined') {
  8197. this.el_.removeAttribute('tabIndex');
  8198. }
  8199. this.off(['tap', 'click'], this.handleClick);
  8200. this.off('focus', this.handleFocus);
  8201. this.off('blur', this.handleBlur);
  8202. return this;
  8203. };
  8204. /**
  8205. * This gets called when a `ClickableComponent` gets:
  8206. * - Clicked (via the `click` event, listening starts in the constructor)
  8207. * - Tapped (via the `tap` event, listening starts in the constructor)
  8208. * - The following things happen in order:
  8209. * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
  8210. * `ClickableComponent`.
  8211. * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
  8212. * {@link ClickableComponent#handleKeyPress}.
  8213. * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
  8214. * the space or enter key.
  8215. * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
  8216. * event as a parameter.
  8217. *
  8218. * @param {EventTarget~Event} event
  8219. * The `keydown`, `tap`, or `click` event that caused this function to be
  8220. * called.
  8221. *
  8222. * @listens tap
  8223. * @listens click
  8224. * @abstract
  8225. */
  8226. ClickableComponent.prototype.handleClick = function handleClick(event) {};
  8227. /**
  8228. * This gets called when a `ClickableComponent` gains focus via a `focus` event.
  8229. * Turns on listening for `keydown` events. When they happen it
  8230. * calls `this.handleKeyPress`.
  8231. *
  8232. * @param {EventTarget~Event} event
  8233. * The `focus` event that caused this function to be called.
  8234. *
  8235. * @listens focus
  8236. */
  8237. ClickableComponent.prototype.handleFocus = function handleFocus(event) {
  8238. Events.on(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
  8239. };
  8240. /**
  8241. * Called when this ClickableComponent has focus and a key gets pressed down. By
  8242. * default it will call `this.handleClick` when the key is space or enter.
  8243. *
  8244. * @param {EventTarget~Event} event
  8245. * The `keydown` event that caused this function to be called.
  8246. *
  8247. * @listens keydown
  8248. */
  8249. ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
  8250. // Support Space (32) or Enter (13) key operation to fire a click event
  8251. if (event.which === 32 || event.which === 13) {
  8252. event.preventDefault();
  8253. this.handleClick(event);
  8254. } else if (_Component.prototype.handleKeyPress) {
  8255. // Pass keypress handling up for unsupported keys
  8256. _Component.prototype.handleKeyPress.call(this, event);
  8257. }
  8258. };
  8259. /**
  8260. * Called when a `ClickableComponent` loses focus. Turns off the listener for
  8261. * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
  8262. *
  8263. * @param {EventTarget~Event} event
  8264. * The `blur` event that caused this function to be called.
  8265. *
  8266. * @listens blur
  8267. */
  8268. ClickableComponent.prototype.handleBlur = function handleBlur(event) {
  8269. Events.off(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
  8270. };
  8271. return ClickableComponent;
  8272. }(_component2['default']);
  8273. _component2['default'].registerComponent('ClickableComponent', ClickableComponent);
  8274. exports['default'] = ClickableComponent;
  8275. },{"./component":47,"./utils/dom.js":123,"./utils/events.js":124,"./utils/fn.js":125,"./utils/log.js":128,"./utils/obj":130,"global/document":136}],46:[function(require,module,exports){
  8276. 'use strict';
  8277. exports.__esModule = true;
  8278. var _button = require('./button');
  8279. var _button2 = _interopRequireDefault(_button);
  8280. var _component = require('./component');
  8281. var _component2 = _interopRequireDefault(_component);
  8282. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  8283. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  8284. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  8285. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  8286. * @file close-button.js
  8287. */
  8288. /**
  8289. * The `CloseButton` is a `{@link Button}` that fires a `close` event when
  8290. * it gets clicked.
  8291. *
  8292. * @extends Button
  8293. */
  8294. var CloseButton = function (_Button) {
  8295. _inherits(CloseButton, _Button);
  8296. /**
  8297. * Creates an instance of the this class.
  8298. *
  8299. * @param {Player} player
  8300. * The `Player` that this class should be attached to.
  8301. *
  8302. * @param {Object} [options]
  8303. * The key/value store of player options.
  8304. */
  8305. function CloseButton(player, options) {
  8306. _classCallCheck(this, CloseButton);
  8307. var _this = _possibleConstructorReturn(this, _Button.call(this, player, options));
  8308. _this.controlText(options && options.controlText || _this.localize('Close'));
  8309. return _this;
  8310. }
  8311. /**
  8312. * Builds the default DOM `className`.
  8313. *
  8314. * @return {string}
  8315. * The DOM `className` for this object.
  8316. */
  8317. CloseButton.prototype.buildCSSClass = function buildCSSClass() {
  8318. return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
  8319. };
  8320. /**
  8321. * This gets called when a `CloseButton` gets clicked. See
  8322. * {@link ClickableComponent#handleClick} for more information on when this will be
  8323. * triggered
  8324. *
  8325. * @param {EventTarget~Event} event
  8326. * The `keydown`, `tap`, or `click` event that caused this function to be
  8327. * called.
  8328. *
  8329. * @listens tap
  8330. * @listens click
  8331. * @fires CloseButton#close
  8332. */
  8333. CloseButton.prototype.handleClick = function handleClick(event) {
  8334. /**
  8335. * Triggered when the a `CloseButton` is clicked.
  8336. *
  8337. * @event CloseButton#close
  8338. * @type {EventTarget~Event}
  8339. *
  8340. * @property {boolean} [bubbles=false]
  8341. * set to false so that the close event does not
  8342. * bubble up to parents if there is no listener
  8343. */
  8344. this.trigger({ type: 'close', bubbles: false });
  8345. };
  8346. return CloseButton;
  8347. }(_button2['default']);
  8348. _component2['default'].registerComponent('CloseButton', CloseButton);
  8349. exports['default'] = CloseButton;
  8350. },{"./button":44,"./component":47}],47:[function(require,module,exports){
  8351. 'use strict';
  8352. exports.__esModule = true;
  8353. var _window = require('global/window');
  8354. var _window2 = _interopRequireDefault(_window);
  8355. var _dom = require('./utils/dom.js');
  8356. var Dom = _interopRequireWildcard(_dom);
  8357. var _fn = require('./utils/fn.js');
  8358. var Fn = _interopRequireWildcard(_fn);
  8359. var _guid = require('./utils/guid.js');
  8360. var Guid = _interopRequireWildcard(_guid);
  8361. var _events = require('./utils/events.js');
  8362. var Events = _interopRequireWildcard(_events);
  8363. var _log = require('./utils/log.js');
  8364. var _log2 = _interopRequireDefault(_log);
  8365. var _toTitleCase = require('./utils/to-title-case.js');
  8366. var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
  8367. var _mergeOptions = require('./utils/merge-options.js');
  8368. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  8369. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  8370. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  8371. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /**
  8372. * Player Component - Base class for all UI objects
  8373. *
  8374. * @file component.js
  8375. */
  8376. /**
  8377. * Base class for all UI Components.
  8378. * Components are UI objects which represent both a javascript object and an element
  8379. * in the DOM. They can be children of other components, and can have
  8380. * children themselves.
  8381. *
  8382. * Components can also use methods from {@link EventTarget}
  8383. */
  8384. var Component = function () {
  8385. /**
  8386. * A callback that is called when a component is ready. Does not have any
  8387. * paramters and any callback value will be ignored.
  8388. *
  8389. * @callback Component~ReadyCallback
  8390. * @this Component
  8391. */
  8392. /**
  8393. * Creates an instance of this class.
  8394. *
  8395. * @param {Player} player
  8396. * The `Player` that this class should be attached to.
  8397. *
  8398. * @param {Object} [options]
  8399. * The key/value store of player options.
  8400. #
  8401. * @param {Object[]} [options.children]
  8402. * An array of children objects to intialize this component with. Children objects have
  8403. * a name property that will be used if more than one component of the same type needs to be
  8404. * added.
  8405. *
  8406. * @param {Component~ReadyCallback} [ready]
  8407. * Function that gets called when the `Component` is ready.
  8408. */
  8409. function Component(player, options, ready) {
  8410. _classCallCheck(this, Component);
  8411. // The component might be the player itself and we can't pass `this` to super
  8412. if (!player && this.play) {
  8413. this.player_ = player = this; // eslint-disable-line
  8414. } else {
  8415. this.player_ = player;
  8416. }
  8417. // Make a copy of prototype.options_ to protect against overriding defaults
  8418. this.options_ = (0, _mergeOptions2['default'])({}, this.options_);
  8419. // Updated options with supplied options
  8420. options = this.options_ = (0, _mergeOptions2['default'])(this.options_, options);
  8421. // Get ID from options or options element if one is supplied
  8422. this.id_ = options.id || options.el && options.el.id;
  8423. // If there was no ID from the options, generate one
  8424. if (!this.id_) {
  8425. // Don't require the player ID function in the case of mock players
  8426. var id = player && player.id && player.id() || 'no_player';
  8427. this.id_ = id + '_component_' + Guid.newGUID();
  8428. }
  8429. this.name_ = options.name || null;
  8430. // Create element if one wasn't provided in options
  8431. if (options.el) {
  8432. this.el_ = options.el;
  8433. } else if (options.createEl !== false) {
  8434. this.el_ = this.createEl();
  8435. }
  8436. this.children_ = [];
  8437. this.childIndex_ = {};
  8438. this.childNameIndex_ = {};
  8439. // Add any child components in options
  8440. if (options.initChildren !== false) {
  8441. this.initChildren();
  8442. }
  8443. this.ready(ready);
  8444. // Don't want to trigger ready here or it will before init is actually
  8445. // finished for all children that run this constructor
  8446. if (options.reportTouchActivity !== false) {
  8447. this.enableTouchActivity();
  8448. }
  8449. }
  8450. /**
  8451. * Dispose of the `Component` and all child components.
  8452. *
  8453. * @fires Component#dispose
  8454. */
  8455. Component.prototype.dispose = function dispose() {
  8456. /**
  8457. * Triggered when a `Component` is disposed.
  8458. *
  8459. * @event Component#dispose
  8460. * @type {EventTarget~Event}
  8461. *
  8462. * @property {boolean} [bubbles=false]
  8463. * set to false so that the close event does not
  8464. * bubble up
  8465. */
  8466. this.trigger({ type: 'dispose', bubbles: false });
  8467. // Dispose all children.
  8468. if (this.children_) {
  8469. for (var i = this.children_.length - 1; i >= 0; i--) {
  8470. if (this.children_[i].dispose) {
  8471. this.children_[i].dispose();
  8472. }
  8473. }
  8474. }
  8475. // Delete child references
  8476. this.children_ = null;
  8477. this.childIndex_ = null;
  8478. this.childNameIndex_ = null;
  8479. // Remove all event listeners.
  8480. this.off();
  8481. // Remove element from DOM
  8482. if (this.el_.parentNode) {
  8483. this.el_.parentNode.removeChild(this.el_);
  8484. }
  8485. Dom.removeElData(this.el_);
  8486. this.el_ = null;
  8487. };
  8488. /**
  8489. * Return the {@link Player} that the `Component` has attached to.
  8490. *
  8491. * @return {Player}
  8492. * The player that this `Component` has attached to.
  8493. */
  8494. Component.prototype.player = function player() {
  8495. return this.player_;
  8496. };
  8497. /**
  8498. * Deep merge of options objects with new options.
  8499. * > Note: When both `obj` and `options` contain properties whose values are objects.
  8500. * The two properties get merged using {@link module:mergeOptions}
  8501. *
  8502. * @param {Object} obj
  8503. * The object that contains new options.
  8504. *
  8505. * @return {Object}
  8506. * A new object of `this.options_` and `obj` merged together.
  8507. *
  8508. * @deprecated since version 5
  8509. */
  8510. Component.prototype.options = function options(obj) {
  8511. _log2['default'].warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
  8512. if (!obj) {
  8513. return this.options_;
  8514. }
  8515. this.options_ = (0, _mergeOptions2['default'])(this.options_, obj);
  8516. return this.options_;
  8517. };
  8518. /**
  8519. * Get the `Component`s DOM element
  8520. *
  8521. * @return {Element}
  8522. * The DOM element for this `Component`.
  8523. */
  8524. Component.prototype.el = function el() {
  8525. return this.el_;
  8526. };
  8527. /**
  8528. * Create the `Component`s DOM element.
  8529. *
  8530. * @param {string} [tagName]
  8531. * Element's DOM node type. e.g. 'div'
  8532. *
  8533. * @param {Object} [properties]
  8534. * An object of properties that should be set.
  8535. *
  8536. * @param {Object} [attributes]
  8537. * An object of attributes that should be set.
  8538. *
  8539. * @return {Element}
  8540. * The element that gets created.
  8541. */
  8542. Component.prototype.createEl = function createEl(tagName, properties, attributes) {
  8543. return Dom.createEl(tagName, properties, attributes);
  8544. };
  8545. /**
  8546. * Localize a string given the string in english.
  8547. *
  8548. * @param {string} string
  8549. * The string to localize.
  8550. *
  8551. * @return {string}
  8552. * The localized string or if no localization exists the english string.
  8553. */
  8554. Component.prototype.localize = function localize(string) {
  8555. var code = this.player_.language && this.player_.language();
  8556. var languages = this.player_.languages && this.player_.languages();
  8557. if (!code || !languages) {
  8558. return string;
  8559. }
  8560. var language = languages[code];
  8561. if (language && language[string]) {
  8562. return language[string];
  8563. }
  8564. var primaryCode = code.split('-')[0];
  8565. var primaryLang = languages[primaryCode];
  8566. if (primaryLang && primaryLang[string]) {
  8567. return primaryLang[string];
  8568. }
  8569. return string;
  8570. };
  8571. /**
  8572. * Return the `Component`s DOM element. This is where children get inserted.
  8573. * This will usually be the the same as the element returned in {@link Component#el}.
  8574. *
  8575. * @return {Element}
  8576. * The content element for this `Component`.
  8577. */
  8578. Component.prototype.contentEl = function contentEl() {
  8579. return this.contentEl_ || this.el_;
  8580. };
  8581. /**
  8582. * Get this `Component`s ID
  8583. *
  8584. * @return {string}
  8585. * The id of this `Component`
  8586. */
  8587. Component.prototype.id = function id() {
  8588. return this.id_;
  8589. };
  8590. /**
  8591. * Get the `Component`s name. The name gets used to reference the `Component`
  8592. * and is set during registration.
  8593. *
  8594. * @return {string}
  8595. * The name of this `Component`.
  8596. */
  8597. Component.prototype.name = function name() {
  8598. return this.name_;
  8599. };
  8600. /**
  8601. * Get an array of all child components
  8602. *
  8603. * @return {Array}
  8604. * The children
  8605. */
  8606. Component.prototype.children = function children() {
  8607. return this.children_;
  8608. };
  8609. /**
  8610. * Returns the child `Component` with the given `id`.
  8611. *
  8612. * @param {string} id
  8613. * The id of the child `Component` to get.
  8614. *
  8615. * @return {Component|undefined}
  8616. * The child `Component` with the given `id` or undefined.
  8617. */
  8618. Component.prototype.getChildById = function getChildById(id) {
  8619. return this.childIndex_[id];
  8620. };
  8621. /**
  8622. * Returns the child `Component` with the given `name`.
  8623. *
  8624. * @param {string} name
  8625. * The name of the child `Component` to get.
  8626. *
  8627. * @return {Component|undefined}
  8628. * The child `Component` with the given `name` or undefined.
  8629. */
  8630. Component.prototype.getChild = function getChild(name) {
  8631. if (!name) {
  8632. return;
  8633. }
  8634. name = (0, _toTitleCase2['default'])(name);
  8635. return this.childNameIndex_[name];
  8636. };
  8637. /**
  8638. * Add a child `Component` inside the current `Component`.
  8639. *
  8640. *
  8641. * @param {string|Component} child
  8642. * The name or instance of a child to add.
  8643. *
  8644. * @param {Object} [options={}]
  8645. * The key/value store of options that will get passed to children of
  8646. * the child.
  8647. *
  8648. * @param {number} [index=this.children_.length]
  8649. * The index to attempt to add a child into.
  8650. *
  8651. * @return {Component}
  8652. * The `Component` that gets added as a child. When using a string the
  8653. * `Component` will get created by this process.
  8654. */
  8655. Component.prototype.addChild = function addChild(child) {
  8656. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  8657. var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
  8658. var component = void 0;
  8659. var componentName = void 0;
  8660. // If child is a string, create component with options
  8661. if (typeof child === 'string') {
  8662. componentName = (0, _toTitleCase2['default'])(child);
  8663. // Options can also be specified as a boolean,
  8664. // so convert to an empty object if false.
  8665. if (!options) {
  8666. options = {};
  8667. }
  8668. // Same as above, but true is deprecated so show a warning.
  8669. if (options === true) {
  8670. _log2['default'].warn('Initializing a child component with `true` is deprecated.' + 'Children should be defined in an array when possible, ' + 'but if necessary use an object instead of `true`.');
  8671. options = {};
  8672. }
  8673. var componentClassName = options.componentClass || componentName;
  8674. // Set name through options
  8675. options.name = componentName;
  8676. // Create a new object & element for this controls set
  8677. // If there's no .player_, this is a player
  8678. var ComponentClass = Component.getComponent(componentClassName);
  8679. if (!ComponentClass) {
  8680. throw new Error('Component ' + componentClassName + ' does not exist');
  8681. }
  8682. // data stored directly on the videojs object may be
  8683. // misidentified as a component to retain
  8684. // backwards-compatibility with 4.x. check to make sure the
  8685. // component class can be instantiated.
  8686. if (typeof ComponentClass !== 'function') {
  8687. return null;
  8688. }
  8689. component = new ComponentClass(this.player_ || this, options);
  8690. // child is a component instance
  8691. } else {
  8692. component = child;
  8693. }
  8694. this.children_.splice(index, 0, component);
  8695. if (typeof component.id === 'function') {
  8696. this.childIndex_[component.id()] = component;
  8697. }
  8698. // If a name wasn't used to create the component, check if we can use the
  8699. // name function of the component
  8700. componentName = componentName || component.name && (0, _toTitleCase2['default'])(component.name());
  8701. if (componentName) {
  8702. this.childNameIndex_[componentName] = component;
  8703. }
  8704. // Add the UI object's element to the container div (box)
  8705. // Having an element is not required
  8706. if (typeof component.el === 'function' && component.el()) {
  8707. var childNodes = this.contentEl().children;
  8708. var refNode = childNodes[index] || null;
  8709. this.contentEl().insertBefore(component.el(), refNode);
  8710. }
  8711. // Return so it can stored on parent object if desired.
  8712. return component;
  8713. };
  8714. /**
  8715. * Remove a child `Component` from this `Component`s list of children. Also removes
  8716. * the child `Component`s element from this `Component`s element.
  8717. *
  8718. * @param {Component} component
  8719. * The child `Component` to remove.
  8720. */
  8721. Component.prototype.removeChild = function removeChild(component) {
  8722. if (typeof component === 'string') {
  8723. component = this.getChild(component);
  8724. }
  8725. if (!component || !this.children_) {
  8726. return;
  8727. }
  8728. var childFound = false;
  8729. for (var i = this.children_.length - 1; i >= 0; i--) {
  8730. if (this.children_[i] === component) {
  8731. childFound = true;
  8732. this.children_.splice(i, 1);
  8733. break;
  8734. }
  8735. }
  8736. if (!childFound) {
  8737. return;
  8738. }
  8739. this.childIndex_[component.id()] = null;
  8740. this.childNameIndex_[component.name()] = null;
  8741. var compEl = component.el();
  8742. if (compEl && compEl.parentNode === this.contentEl()) {
  8743. this.contentEl().removeChild(component.el());
  8744. }
  8745. };
  8746. /**
  8747. * Add and initialize default child `Component`s based upon options.
  8748. */
  8749. Component.prototype.initChildren = function initChildren() {
  8750. var _this = this;
  8751. var children = this.options_.children;
  8752. if (children) {
  8753. // `this` is `parent`
  8754. var parentOptions = this.options_;
  8755. var handleAdd = function handleAdd(child) {
  8756. var name = child.name;
  8757. var opts = child.opts;
  8758. // Allow options for children to be set at the parent options
  8759. // e.g. videojs(id, { controlBar: false });
  8760. // instead of videojs(id, { children: { controlBar: false });
  8761. if (parentOptions[name] !== undefined) {
  8762. opts = parentOptions[name];
  8763. }
  8764. // Allow for disabling default components
  8765. // e.g. options['children']['posterImage'] = false
  8766. if (opts === false) {
  8767. return;
  8768. }
  8769. // Allow options to be passed as a simple boolean if no configuration
  8770. // is necessary.
  8771. if (opts === true) {
  8772. opts = {};
  8773. }
  8774. // We also want to pass the original player options
  8775. // to each component as well so they don't need to
  8776. // reach back into the player for options later.
  8777. opts.playerOptions = _this.options_.playerOptions;
  8778. // Create and add the child component.
  8779. // Add a direct reference to the child by name on the parent instance.
  8780. // If two of the same component are used, different names should be supplied
  8781. // for each
  8782. var newChild = _this.addChild(name, opts);
  8783. if (newChild) {
  8784. _this[name] = newChild;
  8785. }
  8786. };
  8787. // Allow for an array of children details to passed in the options
  8788. var workingChildren = void 0;
  8789. var Tech = Component.getComponent('Tech');
  8790. if (Array.isArray(children)) {
  8791. workingChildren = children;
  8792. } else {
  8793. workingChildren = Object.keys(children);
  8794. }
  8795. workingChildren
  8796. // children that are in this.options_ but also in workingChildren would
  8797. // give us extra children we do not want. So, we want to filter them out.
  8798. .concat(Object.keys(this.options_).filter(function (child) {
  8799. return !workingChildren.some(function (wchild) {
  8800. if (typeof wchild === 'string') {
  8801. return child === wchild;
  8802. }
  8803. return child === wchild.name;
  8804. });
  8805. })).map(function (child) {
  8806. var name = void 0;
  8807. var opts = void 0;
  8808. if (typeof child === 'string') {
  8809. name = child;
  8810. opts = children[name] || _this.options_[name] || {};
  8811. } else {
  8812. name = child.name;
  8813. opts = child;
  8814. }
  8815. return { name: name, opts: opts };
  8816. }).filter(function (child) {
  8817. // we have to make sure that child.name isn't in the techOrder since
  8818. // techs are registerd as Components but can't aren't compatible
  8819. // See https://github.com/videojs/video.js/issues/2772
  8820. var c = Component.getComponent(child.opts.componentClass || (0, _toTitleCase2['default'])(child.name));
  8821. return c && !Tech.isTech(c);
  8822. }).forEach(handleAdd);
  8823. }
  8824. };
  8825. /**
  8826. * Builds the default DOM class name. Should be overriden by sub-components.
  8827. *
  8828. * @return {string}
  8829. * The DOM class name for this object.
  8830. *
  8831. * @abstract
  8832. */
  8833. Component.prototype.buildCSSClass = function buildCSSClass() {
  8834. // Child classes can include a function that does:
  8835. // return 'CLASS NAME' + this._super();
  8836. return '';
  8837. };
  8838. /**
  8839. * Add an `event listener` to this `Component`s element.
  8840. *
  8841. * The benefit of using this over the following:
  8842. * - `VjsEvents.on(otherElement, 'eventName', myFunc)`
  8843. * - `otherComponent.on('eventName', myFunc)`
  8844. *
  8845. * 1. Is that the listeners will get cleaned up when either component gets disposed.
  8846. * 1. It will also bind `myComponent` as the context of `myFunc`.
  8847. * > NOTE: If you remove the element from the DOM that has used `on` you need to
  8848. * clean up references using: `myComponent.trigger(el, 'dispose')`
  8849. * This will also allow the browser to garbage collect it. In special
  8850. * cases such as with `window` and `document`, which are both permanent,
  8851. * this is not necessary.
  8852. *
  8853. * @param {string|Component|string[]} [first]
  8854. * The event name, and array of event names, or another `Component`.
  8855. *
  8856. * @param {EventTarget~EventListener|string|string[]} [second]
  8857. * The listener function, an event name, or an Array of events names.
  8858. *
  8859. * @param {EventTarget~EventListener} [third]
  8860. * The event handler if `first` is a `Component` and `second` is an event name
  8861. * or an Array of event names.
  8862. *
  8863. * @return {Component}
  8864. * Returns itself; method can be chained.
  8865. *
  8866. * @listens Component#dispose
  8867. */
  8868. Component.prototype.on = function on(first, second, third) {
  8869. var _this2 = this;
  8870. if (typeof first === 'string' || Array.isArray(first)) {
  8871. Events.on(this.el_, first, Fn.bind(this, second));
  8872. // Targeting another component or element
  8873. } else {
  8874. var target = first;
  8875. var type = second;
  8876. var fn = Fn.bind(this, third);
  8877. // When this component is disposed, remove the listener from the other component
  8878. var removeOnDispose = function removeOnDispose() {
  8879. return _this2.off(target, type, fn);
  8880. };
  8881. // Use the same function ID so we can remove it later it using the ID
  8882. // of the original listener
  8883. removeOnDispose.guid = fn.guid;
  8884. this.on('dispose', removeOnDispose);
  8885. // If the other component is disposed first we need to clean the reference
  8886. // to the other component in this component's removeOnDispose listener
  8887. // Otherwise we create a memory leak.
  8888. var cleanRemover = function cleanRemover() {
  8889. return _this2.off('dispose', removeOnDispose);
  8890. };
  8891. // Add the same function ID so we can easily remove it later
  8892. cleanRemover.guid = fn.guid;
  8893. // Check if this is a DOM node
  8894. if (first.nodeName) {
  8895. // Add the listener to the other element
  8896. Events.on(target, type, fn);
  8897. Events.on(target, 'dispose', cleanRemover);
  8898. // Should be a component
  8899. // Not using `instanceof Component` because it makes mock players difficult
  8900. } else if (typeof first.on === 'function') {
  8901. // Add the listener to the other component
  8902. target.on(type, fn);
  8903. target.on('dispose', cleanRemover);
  8904. }
  8905. }
  8906. return this;
  8907. };
  8908. /**
  8909. * Remove an event listener from this `Component`s element. If the second argument is
  8910. * exluded all listeners for the type passed in as the first argument will be removed.
  8911. *
  8912. * @param {string|Component|string[]} [first]
  8913. * The event name, and array of event names, or another `Component`.
  8914. *
  8915. * @param {EventTarget~EventListener|string|string[]} [second]
  8916. * The listener function, an event name, or an Array of events names.
  8917. *
  8918. * @param {EventTarget~EventListener} [third]
  8919. * The event handler if `first` is a `Component` and `second` is an event name
  8920. * or an Array of event names.
  8921. *
  8922. * @return {Component}
  8923. * Returns itself; method can be chained.
  8924. */
  8925. Component.prototype.off = function off(first, second, third) {
  8926. if (!first || typeof first === 'string' || Array.isArray(first)) {
  8927. Events.off(this.el_, first, second);
  8928. } else {
  8929. var target = first;
  8930. var type = second;
  8931. // Ensure there's at least a guid, even if the function hasn't been used
  8932. var fn = Fn.bind(this, third);
  8933. // Remove the dispose listener on this component,
  8934. // which was given the same guid as the event listener
  8935. this.off('dispose', fn);
  8936. if (first.nodeName) {
  8937. // Remove the listener
  8938. Events.off(target, type, fn);
  8939. // Remove the listener for cleaning the dispose listener
  8940. Events.off(target, 'dispose', fn);
  8941. } else {
  8942. target.off(type, fn);
  8943. target.off('dispose', fn);
  8944. }
  8945. }
  8946. return this;
  8947. };
  8948. /**
  8949. * Add an event listener that gets triggered only once and then gets removed.
  8950. *
  8951. * @param {string|Component|string[]} [first]
  8952. * The event name, and array of event names, or another `Component`.
  8953. *
  8954. * @param {EventTarget~EventListener|string|string[]} [second]
  8955. * The listener function, an event name, or an Array of events names.
  8956. *
  8957. * @param {EventTarget~EventListener} [third]
  8958. * The event handler if `first` is a `Component` and `second` is an event name
  8959. * or an Array of event names.
  8960. *
  8961. * @return {Component}
  8962. * Returns itself; method can be chained.
  8963. */
  8964. Component.prototype.one = function one(first, second, third) {
  8965. var _this3 = this,
  8966. _arguments = arguments;
  8967. if (typeof first === 'string' || Array.isArray(first)) {
  8968. Events.one(this.el_, first, Fn.bind(this, second));
  8969. } else {
  8970. var target = first;
  8971. var type = second;
  8972. var fn = Fn.bind(this, third);
  8973. var newFunc = function newFunc() {
  8974. _this3.off(target, type, newFunc);
  8975. fn.apply(null, _arguments);
  8976. };
  8977. // Keep the same function ID so we can remove it later
  8978. newFunc.guid = fn.guid;
  8979. this.on(target, type, newFunc);
  8980. }
  8981. return this;
  8982. };
  8983. /**
  8984. * Trigger an event on an element.
  8985. *
  8986. * @param {EventTarget~Event|Object|string} event
  8987. * The event name, and Event, or an event-like object with a type attribute
  8988. * set to the event name.
  8989. *
  8990. * @param {Object} [hash]
  8991. * Data hash to pass along with the event
  8992. *
  8993. * @return {Component}
  8994. * Returns itself; method can be chained.
  8995. */
  8996. Component.prototype.trigger = function trigger(event, hash) {
  8997. Events.trigger(this.el_, event, hash);
  8998. return this;
  8999. };
  9000. /**
  9001. * Bind a listener to the component's ready state. If the ready event has already
  9002. * happened it will trigger the function immediately.
  9003. *
  9004. * @param {Component~ReadyCallback} fn
  9005. * A function to call when ready is triggered.
  9006. *
  9007. * @param {boolean} [sync=false]
  9008. * Execute the listener synchronously if `Component` is ready.
  9009. *
  9010. * @return {Component}
  9011. * Returns itself; method can be chained.
  9012. */
  9013. Component.prototype.ready = function ready(fn) {
  9014. var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  9015. if (fn) {
  9016. if (this.isReady_) {
  9017. if (sync) {
  9018. fn.call(this);
  9019. } else {
  9020. // Call the function asynchronously by default for consistency
  9021. this.setTimeout(fn, 1);
  9022. }
  9023. } else {
  9024. this.readyQueue_ = this.readyQueue_ || [];
  9025. this.readyQueue_.push(fn);
  9026. }
  9027. }
  9028. return this;
  9029. };
  9030. /**
  9031. * Trigger all the ready listeners for this `Component`.
  9032. *
  9033. * @fires Component#ready
  9034. */
  9035. Component.prototype.triggerReady = function triggerReady() {
  9036. this.isReady_ = true;
  9037. // Ensure ready is triggerd asynchronously
  9038. this.setTimeout(function () {
  9039. var readyQueue = this.readyQueue_;
  9040. // Reset Ready Queue
  9041. this.readyQueue_ = [];
  9042. if (readyQueue && readyQueue.length > 0) {
  9043. readyQueue.forEach(function (fn) {
  9044. fn.call(this);
  9045. }, this);
  9046. }
  9047. // Allow for using event listeners also
  9048. /**
  9049. * Triggered when a `Component` is ready.
  9050. *
  9051. * @event Component#ready
  9052. * @type {EventTarget~Event}
  9053. */
  9054. this.trigger('ready');
  9055. }, 1);
  9056. };
  9057. /**
  9058. * Find a single DOM element matching a `selector`. This can be within the `Component`s
  9059. * `contentEl()` or another custom context.
  9060. *
  9061. * @param {string} selector
  9062. * A valid CSS selector, which will be passed to `querySelector`.
  9063. *
  9064. * @param {Element|string} [context=this.contentEl()]
  9065. * A DOM element within which to query. Can also be a selector string in
  9066. * which case the first matching element will get used as context. If
  9067. * missing `this.contentEl()` gets used. If `this.contentEl()` returns
  9068. * nothing it falls back to `document`.
  9069. *
  9070. * @return {Element|null}
  9071. * the dom element that was found, or null
  9072. *
  9073. * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
  9074. */
  9075. Component.prototype.$ = function $(selector, context) {
  9076. return Dom.$(selector, context || this.contentEl());
  9077. };
  9078. /**
  9079. * Finds all DOM element matching a `selector`. This can be within the `Component`s
  9080. * `contentEl()` or another custom context.
  9081. *
  9082. * @param {string} selector
  9083. * A valid CSS selector, which will be passed to `querySelectorAll`.
  9084. *
  9085. * @param {Element|string} [context=this.contentEl()]
  9086. * A DOM element within which to query. Can also be a selector string in
  9087. * which case the first matching element will get used as context. If
  9088. * missing `this.contentEl()` gets used. If `this.contentEl()` returns
  9089. * nothing it falls back to `document`.
  9090. *
  9091. * @return {NodeList}
  9092. * a list of dom elements that were found
  9093. *
  9094. * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
  9095. */
  9096. Component.prototype.$$ = function $$(selector, context) {
  9097. return Dom.$$(selector, context || this.contentEl());
  9098. };
  9099. /**
  9100. * Check if a component's element has a CSS class name.
  9101. *
  9102. * @param {string} classToCheck
  9103. * CSS class name to check.
  9104. *
  9105. * @return {boolean}
  9106. * - True if the `Component` has the class.
  9107. * - False if the `Component` does not have the class`
  9108. */
  9109. Component.prototype.hasClass = function hasClass(classToCheck) {
  9110. return Dom.hasElClass(this.el_, classToCheck);
  9111. };
  9112. /**
  9113. * Add a CSS class name to the `Component`s element.
  9114. *
  9115. * @param {string} classToAdd
  9116. * CSS class name to add
  9117. *
  9118. * @return {Component}
  9119. * Returns itself; method can be chained.
  9120. */
  9121. Component.prototype.addClass = function addClass(classToAdd) {
  9122. Dom.addElClass(this.el_, classToAdd);
  9123. return this;
  9124. };
  9125. /**
  9126. * Remove a CSS class name from the `Component`s element.
  9127. *
  9128. * @param {string} classToRemove
  9129. * CSS class name to remove
  9130. *
  9131. * @return {Component}
  9132. * Returns itself; method can be chained.
  9133. */
  9134. Component.prototype.removeClass = function removeClass(classToRemove) {
  9135. Dom.removeElClass(this.el_, classToRemove);
  9136. return this;
  9137. };
  9138. /**
  9139. * Add or remove a CSS class name from the component's element.
  9140. * - `classToToggle` gets added when {@link Component#hasClass} would return false.
  9141. * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
  9142. *
  9143. * @param {string} classToToggle
  9144. * The class to add or remove based on (@link Component#hasClass}
  9145. *
  9146. * @param {boolean|Dom~predicate} [predicate]
  9147. * An {@link Dom~predicate} function or a boolean
  9148. *
  9149. * @return {Component}
  9150. * Returns itself; method can be chained.
  9151. */
  9152. Component.prototype.toggleClass = function toggleClass(classToToggle, predicate) {
  9153. Dom.toggleElClass(this.el_, classToToggle, predicate);
  9154. return this;
  9155. };
  9156. /**
  9157. * Show the `Component`s element if it is hidden by removing the
  9158. * 'vjs-hidden' class name from it.
  9159. *
  9160. * @return {Component}
  9161. * Returns itself; method can be chained.
  9162. */
  9163. Component.prototype.show = function show() {
  9164. this.removeClass('vjs-hidden');
  9165. return this;
  9166. };
  9167. /**
  9168. * Hide the `Component`s element if it is currently showing by adding the
  9169. * 'vjs-hidden` class name to it.
  9170. *
  9171. * @return {Component}
  9172. * Returns itself; method can be chained.
  9173. */
  9174. Component.prototype.hide = function hide() {
  9175. this.addClass('vjs-hidden');
  9176. return this;
  9177. };
  9178. /**
  9179. * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
  9180. * class name to it. Used during fadeIn/fadeOut.
  9181. *
  9182. * @return {Component}
  9183. * Returns itself; method can be chained.
  9184. *
  9185. * @private
  9186. */
  9187. Component.prototype.lockShowing = function lockShowing() {
  9188. this.addClass('vjs-lock-showing');
  9189. return this;
  9190. };
  9191. /**
  9192. * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
  9193. * class name from it. Used during fadeIn/fadeOut.
  9194. *
  9195. * @return {Component}
  9196. * Returns itself; method can be chained.
  9197. *
  9198. * @private
  9199. */
  9200. Component.prototype.unlockShowing = function unlockShowing() {
  9201. this.removeClass('vjs-lock-showing');
  9202. return this;
  9203. };
  9204. /**
  9205. * Get the value of an attribute on the `Component`s element.
  9206. *
  9207. * @param {string} attribute
  9208. * Name of the attribute to get the value from.
  9209. *
  9210. * @return {string|null}
  9211. * - The value of the attribute that was asked for.
  9212. * - Can be an empty string on some browsers if the attribute does not exist
  9213. * or has no value
  9214. * - Most browsers will return null if the attibute does not exist or has
  9215. * no value.
  9216. *
  9217. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
  9218. */
  9219. Component.prototype.getAttribute = function getAttribute(attribute) {
  9220. return Dom.getAttribute(this.el_, attribute);
  9221. };
  9222. /**
  9223. * Set the value of an attribute on the `Component`'s element
  9224. *
  9225. * @param {string} attribute
  9226. * Name of the attribute to set.
  9227. *
  9228. * @param {string} value
  9229. * Value to set the attribute to.
  9230. *
  9231. * @return {Component}
  9232. * Returns itself; method can be chained.
  9233. *
  9234. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
  9235. */
  9236. Component.prototype.setAttribute = function setAttribute(attribute, value) {
  9237. Dom.setAttribute(this.el_, attribute, value);
  9238. return this;
  9239. };
  9240. /**
  9241. * Remove an attribute from the `Component`s element.
  9242. *
  9243. * @param {string} attribute
  9244. * Name of the attribute to remove.
  9245. *
  9246. * @return {Component}
  9247. * Returns itself; method can be chained.
  9248. *
  9249. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
  9250. */
  9251. Component.prototype.removeAttribute = function removeAttribute(attribute) {
  9252. Dom.removeAttribute(this.el_, attribute);
  9253. return this;
  9254. };
  9255. /**
  9256. * Get or set the width of the component based upon the CSS styles.
  9257. * See {@link Component#dimension} for more detailed information.
  9258. *
  9259. * @param {number|string} [num]
  9260. * The width that you want to set postfixed with '%', 'px' or nothing.
  9261. *
  9262. * @param {boolean} [skipListeners]
  9263. * Skip the resize event trigger
  9264. *
  9265. * @return {Component|number|string}
  9266. * - The width when getting, zero if there is no width. Can be a string
  9267. * postpixed with '%' or 'px'.
  9268. * - Returns itself when setting; method can be chained.
  9269. */
  9270. Component.prototype.width = function width(num, skipListeners) {
  9271. return this.dimension('width', num, skipListeners);
  9272. };
  9273. /**
  9274. * Get or set the height of the component based upon the CSS styles.
  9275. * See {@link Component#dimension} for more detailed information.
  9276. *
  9277. * @param {number|string} [num]
  9278. * The height that you want to set postfixed with '%', 'px' or nothing.
  9279. *
  9280. * @param {boolean} [skipListeners]
  9281. * Skip the resize event trigger
  9282. *
  9283. * @return {Component|number|string}
  9284. * - The width when getting, zero if there is no width. Can be a string
  9285. * postpixed with '%' or 'px'.
  9286. * - Returns itself when setting; method can be chained.
  9287. */
  9288. Component.prototype.height = function height(num, skipListeners) {
  9289. return this.dimension('height', num, skipListeners);
  9290. };
  9291. /**
  9292. * Set both the width and height of the `Component` element at the same time.
  9293. *
  9294. * @param {number|string} width
  9295. * Width to set the `Component`s element to.
  9296. *
  9297. * @param {number|string} height
  9298. * Height to set the `Component`s element to.
  9299. *
  9300. * @return {Component}
  9301. * Returns itself; method can be chained.
  9302. */
  9303. Component.prototype.dimensions = function dimensions(width, height) {
  9304. // Skip resize listeners on width for optimization
  9305. return this.width(width, true).height(height);
  9306. };
  9307. /**
  9308. * Get or set width or height of the `Component` element. This is the shared code
  9309. * for the {@link Component#width} and {@link Component#height}.
  9310. *
  9311. * Things to know:
  9312. * - If the width or height in an number this will return the number postfixed with 'px'.
  9313. * - If the width/height is a percent this will return the percent postfixed with '%'
  9314. * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
  9315. * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
  9316. * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
  9317. * for more information
  9318. * - If you want the computed style of the component, use {@link Component#currentWidth}
  9319. * and {@link {Component#currentHeight}
  9320. *
  9321. * @fires Component#resize
  9322. *
  9323. * @param {string} widthOrHeight
  9324. 8 'width' or 'height'
  9325. *
  9326. * @param {number|string} [num]
  9327. 8 New dimension
  9328. *
  9329. * @param {boolean} [skipListeners]
  9330. * Skip resize event trigger
  9331. *
  9332. * @return {Component}
  9333. * - the dimension when getting or 0 if unset
  9334. * - Returns itself when setting; method can be chained.
  9335. */
  9336. Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
  9337. if (num !== undefined) {
  9338. // Set to zero if null or literally NaN (NaN !== NaN)
  9339. if (num === null || num !== num) {
  9340. num = 0;
  9341. }
  9342. // Check if using css width/height (% or px) and adjust
  9343. if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
  9344. this.el_.style[widthOrHeight] = num;
  9345. } else if (num === 'auto') {
  9346. this.el_.style[widthOrHeight] = '';
  9347. } else {
  9348. this.el_.style[widthOrHeight] = num + 'px';
  9349. }
  9350. // skipListeners allows us to avoid triggering the resize event when setting both width and height
  9351. if (!skipListeners) {
  9352. /**
  9353. * Triggered when a component is resized.
  9354. *
  9355. * @event Component#resize
  9356. * @type {EventTarget~Event}
  9357. */
  9358. this.trigger('resize');
  9359. }
  9360. // Return component
  9361. return this;
  9362. }
  9363. // Not setting a value, so getting it
  9364. // Make sure element exists
  9365. if (!this.el_) {
  9366. return 0;
  9367. }
  9368. // Get dimension value from style
  9369. var val = this.el_.style[widthOrHeight];
  9370. var pxIndex = val.indexOf('px');
  9371. if (pxIndex !== -1) {
  9372. // Return the pixel value with no 'px'
  9373. return parseInt(val.slice(0, pxIndex), 10);
  9374. }
  9375. // No px so using % or no style was set, so falling back to offsetWidth/height
  9376. // If component has display:none, offset will return 0
  9377. // TODO: handle display:none and no dimension style using px
  9378. return parseInt(this.el_['offset' + (0, _toTitleCase2['default'])(widthOrHeight)], 10);
  9379. };
  9380. /**
  9381. * Get the width or the height of the `Component` elements computed style. Uses
  9382. * `window.getComputedStyle`.
  9383. *
  9384. * @param {string} widthOrHeight
  9385. * A string containing 'width' or 'height'. Whichever one you want to get.
  9386. *
  9387. * @return {number}
  9388. * The dimension that gets asked for or 0 if nothing was set
  9389. * for that dimension.
  9390. */
  9391. Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
  9392. var computedWidthOrHeight = 0;
  9393. if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
  9394. throw new Error('currentDimension only accepts width or height value');
  9395. }
  9396. if (typeof _window2['default'].getComputedStyle === 'function') {
  9397. var computedStyle = _window2['default'].getComputedStyle(this.el_);
  9398. computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
  9399. }
  9400. // remove 'px' from variable and parse as integer
  9401. computedWidthOrHeight = parseFloat(computedWidthOrHeight);
  9402. // if the computed value is still 0, it's possible that the browser is lying
  9403. // and we want to check the offset values.
  9404. // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
  9405. if (computedWidthOrHeight === 0) {
  9406. var rule = 'offset' + (0, _toTitleCase2['default'])(widthOrHeight);
  9407. computedWidthOrHeight = this.el_[rule];
  9408. }
  9409. return computedWidthOrHeight;
  9410. };
  9411. /**
  9412. * An object that contains width and height values of the `Component`s
  9413. * computed style. Uses `window.getComputedStyle`.
  9414. *
  9415. * @typedef {Object} Component~DimensionObject
  9416. *
  9417. * @property {number} width
  9418. * The width of the `Component`s computed style.
  9419. *
  9420. * @property {number} height
  9421. * The height of the `Component`s computed style.
  9422. */
  9423. /**
  9424. * Get an object that contains width and height values of the `Component`s
  9425. * computed style.
  9426. *
  9427. * @return {Component~DimensionObject}
  9428. * The dimensions of the components element
  9429. */
  9430. Component.prototype.currentDimensions = function currentDimensions() {
  9431. return {
  9432. width: this.currentDimension('width'),
  9433. height: this.currentDimension('height')
  9434. };
  9435. };
  9436. /**
  9437. * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`.
  9438. *
  9439. * @return {number} width
  9440. * The width of the `Component`s computed style.
  9441. */
  9442. Component.prototype.currentWidth = function currentWidth() {
  9443. return this.currentDimension('width');
  9444. };
  9445. /**
  9446. * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`.
  9447. *
  9448. * @return {number} height
  9449. * The height of the `Component`s computed style.
  9450. */
  9451. Component.prototype.currentHeight = function currentHeight() {
  9452. return this.currentDimension('height');
  9453. };
  9454. /**
  9455. * Set the focus to this component
  9456. */
  9457. Component.prototype.focus = function focus() {
  9458. this.el_.focus();
  9459. };
  9460. /**
  9461. * Remove the focus from this component
  9462. */
  9463. Component.prototype.blur = function blur() {
  9464. this.el_.blur();
  9465. };
  9466. /**
  9467. * Emit a 'tap' events when touch event support gets detected. This gets used to
  9468. * support toggling the controls through a tap on the video. They get enabled
  9469. * because every sub-component would have extra overhead otherwise.
  9470. *
  9471. * @private
  9472. * @fires Component#tap
  9473. * @listens Component#touchstart
  9474. * @listens Component#touchmove
  9475. * @listens Component#touchleave
  9476. * @listens Component#touchcancel
  9477. * @listens Component#touchend
  9478. */
  9479. Component.prototype.emitTapEvents = function emitTapEvents() {
  9480. // Track the start time so we can determine how long the touch lasted
  9481. var touchStart = 0;
  9482. var firstTouch = null;
  9483. // Maximum movement allowed during a touch event to still be considered a tap
  9484. // Other popular libs use anywhere from 2 (hammer.js) to 15,
  9485. // so 10 seems like a nice, round number.
  9486. var tapMovementThreshold = 10;
  9487. // The maximum length a touch can be while still being considered a tap
  9488. var touchTimeThreshold = 200;
  9489. var couldBeTap = void 0;
  9490. this.on('touchstart', function (event) {
  9491. // If more than one finger, don't consider treating this as a click
  9492. if (event.touches.length === 1) {
  9493. // Copy pageX/pageY from the object
  9494. firstTouch = {
  9495. pageX: event.touches[0].pageX,
  9496. pageY: event.touches[0].pageY
  9497. };
  9498. // Record start time so we can detect a tap vs. "touch and hold"
  9499. touchStart = new Date().getTime();
  9500. // Reset couldBeTap tracking
  9501. couldBeTap = true;
  9502. }
  9503. });
  9504. this.on('touchmove', function (event) {
  9505. // If more than one finger, don't consider treating this as a click
  9506. if (event.touches.length > 1) {
  9507. couldBeTap = false;
  9508. } else if (firstTouch) {
  9509. // Some devices will throw touchmoves for all but the slightest of taps.
  9510. // So, if we moved only a small distance, this could still be a tap
  9511. var xdiff = event.touches[0].pageX - firstTouch.pageX;
  9512. var ydiff = event.touches[0].pageY - firstTouch.pageY;
  9513. var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
  9514. if (touchDistance > tapMovementThreshold) {
  9515. couldBeTap = false;
  9516. }
  9517. }
  9518. });
  9519. var noTap = function noTap() {
  9520. couldBeTap = false;
  9521. };
  9522. // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
  9523. this.on('touchleave', noTap);
  9524. this.on('touchcancel', noTap);
  9525. // When the touch ends, measure how long it took and trigger the appropriate
  9526. // event
  9527. this.on('touchend', function (event) {
  9528. firstTouch = null;
  9529. // Proceed only if the touchmove/leave/cancel event didn't happen
  9530. if (couldBeTap === true) {
  9531. // Measure how long the touch lasted
  9532. var touchTime = new Date().getTime() - touchStart;
  9533. // Make sure the touch was less than the threshold to be considered a tap
  9534. if (touchTime < touchTimeThreshold) {
  9535. // Don't let browser turn this into a click
  9536. event.preventDefault();
  9537. /**
  9538. * Triggered when a `Component` is tapped.
  9539. *
  9540. * @event Component#tap
  9541. * @type {EventTarget~Event}
  9542. */
  9543. this.trigger('tap');
  9544. // It may be good to copy the touchend event object and change the
  9545. // type to tap, if the other event properties aren't exact after
  9546. // Events.fixEvent runs (e.g. event.target)
  9547. }
  9548. }
  9549. });
  9550. };
  9551. /**
  9552. * This function reports user activity whenever touch events happen. This can get
  9553. * turned off by any sub-components that wants touch events to act another way.
  9554. *
  9555. * Report user touch activity when touch events occur. User activity gets used to
  9556. * determine when controls should show/hide. It is simple when it comes to mouse
  9557. * events, because any mouse event should show the controls. So we capture mouse
  9558. * events that bubble up to the player and report activity when that happens.
  9559. * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
  9560. * controls. So touch events can't help us at the player level either.
  9561. *
  9562. * User activity gets checked asynchronously. So what could happen is a tap event
  9563. * on the video turns the controls off. Then the `touchend` event bubbles up to
  9564. * the player. Which, if it reported user activity, would turn the controls right
  9565. * back on. We also don't want to completely block touch events from bubbling up.
  9566. * Furthermore a `touchmove` event and anything other than a tap, should not turn
  9567. * controls back on.
  9568. *
  9569. * @listens Component#touchstart
  9570. * @listens Component#touchmove
  9571. * @listens Component#touchend
  9572. * @listens Component#touchcancel
  9573. */
  9574. Component.prototype.enableTouchActivity = function enableTouchActivity() {
  9575. // Don't continue if the root player doesn't support reporting user activity
  9576. if (!this.player() || !this.player().reportUserActivity) {
  9577. return;
  9578. }
  9579. // listener for reporting that the user is active
  9580. var report = Fn.bind(this.player(), this.player().reportUserActivity);
  9581. var touchHolding = void 0;
  9582. this.on('touchstart', function () {
  9583. report();
  9584. // For as long as the they are touching the device or have their mouse down,
  9585. // we consider them active even if they're not moving their finger or mouse.
  9586. // So we want to continue to update that they are active
  9587. this.clearInterval(touchHolding);
  9588. // report at the same interval as activityCheck
  9589. touchHolding = this.setInterval(report, 250);
  9590. });
  9591. var touchEnd = function touchEnd(event) {
  9592. report();
  9593. // stop the interval that maintains activity if the touch is holding
  9594. this.clearInterval(touchHolding);
  9595. };
  9596. this.on('touchmove', report);
  9597. this.on('touchend', touchEnd);
  9598. this.on('touchcancel', touchEnd);
  9599. };
  9600. /**
  9601. * A callback that has no parameters and is bound into `Component`s context.
  9602. *
  9603. * @callback Component~GenericCallback
  9604. * @this Component
  9605. */
  9606. /**
  9607. * Creates a function that runs after an `x` millisecond timeout. This function is a
  9608. * wrapper around `window.setTimeout`. There are a few reasons to use this one
  9609. * instead though:
  9610. * 1. It gets cleared via {@link Component#clearTimeout} when
  9611. * {@link Component#dispose} gets called.
  9612. * 2. The function callback will gets turned into a {@link Component~GenericCallback}
  9613. *
  9614. * > Note: You can use `window.clearTimeout` on the id returned by this function. This
  9615. * will cause its dispose listener not to get cleaned up! Please use
  9616. * {@link Component#clearTimeout} or {@link Component#dispose}.
  9617. *
  9618. * @param {Component~GenericCallback} fn
  9619. * The function that will be run after `timeout`.
  9620. *
  9621. * @param {number} timeout
  9622. * Timeout in milliseconds to delay before executing the specified function.
  9623. *
  9624. * @return {number}
  9625. * Returns a timeout ID that gets used to identify the timeout. It can also
  9626. * get used in {@link Component#clearTimeout} to clear the timeout that
  9627. * was set.
  9628. *
  9629. * @listens Component#dispose
  9630. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
  9631. */
  9632. Component.prototype.setTimeout = function setTimeout(fn, timeout) {
  9633. fn = Fn.bind(this, fn);
  9634. var timeoutId = _window2['default'].setTimeout(fn, timeout);
  9635. var disposeFn = function disposeFn() {
  9636. this.clearTimeout(timeoutId);
  9637. };
  9638. disposeFn.guid = 'vjs-timeout-' + timeoutId;
  9639. this.on('dispose', disposeFn);
  9640. return timeoutId;
  9641. };
  9642. /**
  9643. * Clears a timeout that gets created via `window.setTimeout` or
  9644. * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
  9645. * use this function instead of `window.clearTimout`. If you don't your dispose
  9646. * listener will not get cleaned up until {@link Component#dispose}!
  9647. *
  9648. * @param {number} timeoutId
  9649. * The id of the timeout to clear. The return value of
  9650. * {@link Component#setTimeout} or `window.setTimeout`.
  9651. *
  9652. * @return {number}
  9653. * Returns the timeout id that was cleared.
  9654. *
  9655. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
  9656. */
  9657. Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
  9658. _window2['default'].clearTimeout(timeoutId);
  9659. var disposeFn = function disposeFn() {};
  9660. disposeFn.guid = 'vjs-timeout-' + timeoutId;
  9661. this.off('dispose', disposeFn);
  9662. return timeoutId;
  9663. };
  9664. /**
  9665. * Creates a function that gets run every `x` milliseconds. This function is a wrapper
  9666. * around `window.setInterval`. There are a few reasons to use this one instead though.
  9667. * 1. It gets cleared via {@link Component#clearInterval} when
  9668. * {@link Component#dispose} gets called.
  9669. * 2. The function callback will be a {@link Component~GenericCallback}
  9670. *
  9671. * @param {Component~GenericCallback} fn
  9672. * The function to run every `x` seconds.
  9673. *
  9674. * @param {number} interval
  9675. * Execute the specified function every `x` milliseconds.
  9676. *
  9677. * @return {number}
  9678. * Returns an id that can be used to identify the interval. It can also be be used in
  9679. * {@link Component#clearInterval} to clear the interval.
  9680. *
  9681. * @listens Component#dispose
  9682. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
  9683. */
  9684. Component.prototype.setInterval = function setInterval(fn, interval) {
  9685. fn = Fn.bind(this, fn);
  9686. var intervalId = _window2['default'].setInterval(fn, interval);
  9687. var disposeFn = function disposeFn() {
  9688. this.clearInterval(intervalId);
  9689. };
  9690. disposeFn.guid = 'vjs-interval-' + intervalId;
  9691. this.on('dispose', disposeFn);
  9692. return intervalId;
  9693. };
  9694. /**
  9695. * Clears an interval that gets created via `window.setInterval` or
  9696. * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
  9697. * use this function instead of `window.clearInterval`. If you don't your dispose
  9698. * listener will not get cleaned up until {@link Component#dispose}!
  9699. *
  9700. * @param {number} intervalId
  9701. * The id of the interval to clear. The return value of
  9702. * {@link Component#setInterval} or `window.setInterval`.
  9703. *
  9704. * @return {number}
  9705. * Returns the interval id that was cleared.
  9706. *
  9707. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
  9708. */
  9709. Component.prototype.clearInterval = function clearInterval(intervalId) {
  9710. _window2['default'].clearInterval(intervalId);
  9711. var disposeFn = function disposeFn() {};
  9712. disposeFn.guid = 'vjs-interval-' + intervalId;
  9713. this.off('dispose', disposeFn);
  9714. return intervalId;
  9715. };
  9716. /**
  9717. * Register a `Component` with `videojs` given the name and the component.
  9718. *
  9719. * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
  9720. * should be registered using {@link Tech.registerTech} or
  9721. * {@link videojs:videojs.registerTech}.
  9722. *
  9723. * > NOTE: This function can also be seen on videojs as
  9724. * {@link videojs:videojs.registerComponent}.
  9725. *
  9726. * @param {string} name
  9727. * The name of the `Component` to register.
  9728. *
  9729. * @param {Component} comp
  9730. * The `Component` class to register.
  9731. *
  9732. * @return {Component}
  9733. * The `Component` that was registered.
  9734. */
  9735. Component.registerComponent = function registerComponent(name, comp) {
  9736. if (!name) {
  9737. return;
  9738. }
  9739. name = (0, _toTitleCase2['default'])(name);
  9740. if (!Component.components_) {
  9741. Component.components_ = {};
  9742. }
  9743. if (name === 'Player' && Component.components_[name]) {
  9744. var Player = Component.components_[name];
  9745. // If we have players that were disposed, then their name will still be
  9746. // in Players.players. So, we must loop through and verify that the value
  9747. // for each item is not null. This allows registration of the Player component
  9748. // after all players have been disposed or before any were created.
  9749. if (Player.players && Object.keys(Player.players).length > 0 && Object.keys(Player.players).map(function (playerName) {
  9750. return Player.players[playerName];
  9751. }).every(Boolean)) {
  9752. throw new Error('Can not register Player component after player has been created');
  9753. }
  9754. }
  9755. Component.components_[name] = comp;
  9756. return comp;
  9757. };
  9758. /**
  9759. * Get a `Component` based on the name it was registered with.
  9760. *
  9761. * @param {string} name
  9762. * The Name of the component to get.
  9763. *
  9764. * @return {Component}
  9765. * The `Component` that got registered under the given name.
  9766. *
  9767. * @deprecated In `videojs` 6 this will not return `Component`s that were not
  9768. * registered using {@link Component.registerComponent}. Currently we
  9769. * check the global `videojs` object for a `Component` name and
  9770. * return that if it exists.
  9771. */
  9772. Component.getComponent = function getComponent(name) {
  9773. if (!name) {
  9774. return;
  9775. }
  9776. name = (0, _toTitleCase2['default'])(name);
  9777. if (Component.components_ && Component.components_[name]) {
  9778. return Component.components_[name];
  9779. }
  9780. if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) {
  9781. _log2['default'].warn('The ' + name + ' component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)');
  9782. return _window2['default'].videojs[name];
  9783. }
  9784. };
  9785. /**
  9786. * Sets up the constructor using the supplied init method or uses the init of the
  9787. * parent object.
  9788. *
  9789. * @param {Object} [props={}]
  9790. * An object of properties.
  9791. *
  9792. * @return {Object}
  9793. * the extended object.
  9794. *
  9795. * @deprecated since version 5
  9796. */
  9797. Component.extend = function extend(props) {
  9798. props = props || {};
  9799. _log2['default'].warn('Component.extend({}) has been deprecated, ' + ' use videojs.extend(Component, {}) instead');
  9800. // Set up the constructor using the supplied init method
  9801. // or using the init of the parent object
  9802. // Make sure to check the unobfuscated version for external libs
  9803. var init = props.init || props.init || this.prototype.init || this.prototype.init || function () {};
  9804. // In Resig's simple class inheritance (previously used) the constructor
  9805. // is a function that calls `this.init.apply(arguments)`
  9806. // However that would prevent us from using `ParentObject.call(this);`
  9807. // in a Child constructor because the `this` in `this.init`
  9808. // would still refer to the Child and cause an infinite loop.
  9809. // We would instead have to do
  9810. // `ParentObject.prototype.init.apply(this, arguments);`
  9811. // Bleh. We're not creating a _super() function, so it's good to keep
  9812. // the parent constructor reference simple.
  9813. var subObj = function subObj() {
  9814. init.apply(this, arguments);
  9815. };
  9816. // Inherit from this object's prototype
  9817. subObj.prototype = Object.create(this.prototype);
  9818. // Reset the constructor property for subObj otherwise
  9819. // instances of subObj would have the constructor of the parent Object
  9820. subObj.prototype.constructor = subObj;
  9821. // Make the class extendable
  9822. subObj.extend = Component.extend;
  9823. // Extend subObj's prototype with functions and other properties from props
  9824. for (var name in props) {
  9825. if (props.hasOwnProperty(name)) {
  9826. subObj.prototype[name] = props[name];
  9827. }
  9828. }
  9829. return subObj;
  9830. };
  9831. return Component;
  9832. }();
  9833. Component.registerComponent('Component', Component);
  9834. exports['default'] = Component;
  9835. },{"./utils/dom.js":123,"./utils/events.js":124,"./utils/fn.js":125,"./utils/guid.js":127,"./utils/log.js":128,"./utils/merge-options.js":129,"./utils/to-title-case.js":133,"global/window":137}],48:[function(require,module,exports){
  9836. 'use strict';
  9837. exports.__esModule = true;
  9838. var _trackButton = require('../track-button.js');
  9839. var _trackButton2 = _interopRequireDefault(_trackButton);
  9840. var _component = require('../../component.js');
  9841. var _component2 = _interopRequireDefault(_component);
  9842. var _audioTrackMenuItem = require('./audio-track-menu-item.js');
  9843. var _audioTrackMenuItem2 = _interopRequireDefault(_audioTrackMenuItem);
  9844. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  9845. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  9846. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  9847. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  9848. * @file audio-track-button.js
  9849. */
  9850. /**
  9851. * The base class for buttons that toggle specific {@link AudioTrack} types.
  9852. *
  9853. * @extends TrackButton
  9854. */
  9855. var AudioTrackButton = function (_TrackButton) {
  9856. _inherits(AudioTrackButton, _TrackButton);
  9857. /**
  9858. * Creates an instance of this class.
  9859. *
  9860. * @param {Player} player
  9861. * The `Player` that this class should be attached to.
  9862. *
  9863. * @param {Object} [options={}]
  9864. * The key/value store of player options.
  9865. */
  9866. function AudioTrackButton(player) {
  9867. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  9868. _classCallCheck(this, AudioTrackButton);
  9869. options.tracks = player.audioTracks && player.audioTracks();
  9870. var _this = _possibleConstructorReturn(this, _TrackButton.call(this, player, options));
  9871. _this.el_.setAttribute('aria-label', 'Audio Menu');
  9872. return _this;
  9873. }
  9874. /**
  9875. * Builds the default DOM `className`.
  9876. *
  9877. * @return {string}
  9878. * The DOM `className` for this object.
  9879. */
  9880. AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
  9881. return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
  9882. };
  9883. /**
  9884. * Create a menu item for each audio track
  9885. *
  9886. * @param {AudioTrackMenuItem[]} [items=[]]
  9887. * An array of existing menu items to use.
  9888. *
  9889. * @return {AudioTrackMenuItem[]}
  9890. * An array of menu items
  9891. */
  9892. AudioTrackButton.prototype.createItems = function createItems() {
  9893. var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  9894. // if there's only one audio track, there no point in showing it
  9895. this.hideThreshold_ = 1;
  9896. var tracks = this.player_.audioTracks && this.player_.audioTracks();
  9897. if (!tracks) {
  9898. return items;
  9899. }
  9900. for (var i = 0; i < tracks.length; i++) {
  9901. var track = tracks[i];
  9902. items.push(new _audioTrackMenuItem2['default'](this.player_, {
  9903. track: track,
  9904. // MenuItem is selectable
  9905. selectable: true
  9906. }));
  9907. }
  9908. return items;
  9909. };
  9910. return AudioTrackButton;
  9911. }(_trackButton2['default']);
  9912. /**
  9913. * The text that should display over the `AudioTrackButton`s controls. Added for localization.
  9914. *
  9915. * @type {string}
  9916. * @private
  9917. */
  9918. AudioTrackButton.prototype.controlText_ = 'Audio Track';
  9919. _component2['default'].registerComponent('AudioTrackButton', AudioTrackButton);
  9920. exports['default'] = AudioTrackButton;
  9921. },{"../../component.js":47,"../track-button.js":78,"./audio-track-menu-item.js":49}],49:[function(require,module,exports){
  9922. 'use strict';
  9923. exports.__esModule = true;
  9924. var _menuItem = require('../../menu/menu-item.js');
  9925. var _menuItem2 = _interopRequireDefault(_menuItem);
  9926. var _component = require('../../component.js');
  9927. var _component2 = _interopRequireDefault(_component);
  9928. var _fn = require('../../utils/fn.js');
  9929. var Fn = _interopRequireWildcard(_fn);
  9930. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  9931. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  9932. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  9933. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  9934. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  9935. * @file audio-track-menu-item.js
  9936. */
  9937. /**
  9938. * An {@link AudioTrack} {@link MenuItem}
  9939. *
  9940. * @extends MenuItem
  9941. */
  9942. var AudioTrackMenuItem = function (_MenuItem) {
  9943. _inherits(AudioTrackMenuItem, _MenuItem);
  9944. /**
  9945. * Creates an instance of this class.
  9946. *
  9947. * @param {Player} player
  9948. * The `Player` that this class should be attached to.
  9949. *
  9950. * @param {Object} [options]
  9951. * The key/value store of player options.
  9952. */
  9953. function AudioTrackMenuItem(player, options) {
  9954. _classCallCheck(this, AudioTrackMenuItem);
  9955. var track = options.track;
  9956. var tracks = player.audioTracks();
  9957. // Modify options for parent MenuItem class's init.
  9958. options.label = track.label || track.language || 'Unknown';
  9959. options.selected = track.enabled;
  9960. var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  9961. _this.track = track;
  9962. if (tracks) {
  9963. var changeHandler = Fn.bind(_this, _this.handleTracksChange);
  9964. tracks.addEventListener('change', changeHandler);
  9965. _this.on('dispose', function () {
  9966. tracks.removeEventListener('change', changeHandler);
  9967. });
  9968. }
  9969. return _this;
  9970. }
  9971. /**
  9972. * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
  9973. * for more detailed information on what a click can be.
  9974. *
  9975. * @param {EventTarget~Event} [event]
  9976. * The `keydown`, `tap`, or `click` event that caused this function to be
  9977. * called.
  9978. *
  9979. * @listens tap
  9980. * @listens click
  9981. */
  9982. AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
  9983. var tracks = this.player_.audioTracks();
  9984. _MenuItem.prototype.handleClick.call(this, event);
  9985. if (!tracks) {
  9986. return;
  9987. }
  9988. for (var i = 0; i < tracks.length; i++) {
  9989. var track = tracks[i];
  9990. track.enabled = track === this.track;
  9991. }
  9992. };
  9993. /**
  9994. * Handle any {@link AudioTrack} change.
  9995. *
  9996. * @param {EventTarget~Event} [event]
  9997. * The {@link AudioTrackList#change} event that caused this to run.
  9998. *
  9999. * @listens AudioTrackList#change
  10000. */
  10001. AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  10002. this.selected(this.track.enabled);
  10003. };
  10004. return AudioTrackMenuItem;
  10005. }(_menuItem2['default']);
  10006. _component2['default'].registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
  10007. exports['default'] = AudioTrackMenuItem;
  10008. },{"../../component.js":47,"../../menu/menu-item.js":90,"../../utils/fn.js":125}],50:[function(require,module,exports){
  10009. 'use strict';
  10010. exports.__esModule = true;
  10011. var _component = require('../component.js');
  10012. var _component2 = _interopRequireDefault(_component);
  10013. require('./play-toggle.js');
  10014. require('./time-controls/current-time-display.js');
  10015. require('./time-controls/duration-display.js');
  10016. require('./time-controls/time-divider.js');
  10017. require('./time-controls/remaining-time-display.js');
  10018. require('./live-display.js');
  10019. require('./progress-control/progress-control.js');
  10020. require('./fullscreen-toggle.js');
  10021. require('./volume-control/volume-control.js');
  10022. require('./volume-menu-button.js');
  10023. require('./mute-toggle.js');
  10024. require('./text-track-controls/chapters-button.js');
  10025. require('./text-track-controls/descriptions-button.js');
  10026. require('./text-track-controls/subtitles-button.js');
  10027. require('./text-track-controls/captions-button.js');
  10028. require('./audio-track-controls/audio-track-button.js');
  10029. require('./playback-rate-menu/playback-rate-menu-button.js');
  10030. require('./spacer-controls/custom-control-spacer.js');
  10031. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10032. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10033. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10034. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10035. * @file control-bar.js
  10036. */
  10037. // Required children
  10038. /**
  10039. * Container of main controls.
  10040. *
  10041. * @extends Component
  10042. */
  10043. var ControlBar = function (_Component) {
  10044. _inherits(ControlBar, _Component);
  10045. function ControlBar() {
  10046. _classCallCheck(this, ControlBar);
  10047. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  10048. }
  10049. /**
  10050. * Create the `Component`'s DOM element
  10051. *
  10052. * @return {Element}
  10053. * The element that was created.
  10054. */
  10055. ControlBar.prototype.createEl = function createEl() {
  10056. return _Component.prototype.createEl.call(this, 'div', {
  10057. className: 'vjs-control-bar',
  10058. dir: 'ltr'
  10059. }, {
  10060. // The control bar is a group, so it can contain menuitems
  10061. role: 'group'
  10062. });
  10063. };
  10064. return ControlBar;
  10065. }(_component2['default']);
  10066. /**
  10067. * Default options for `ControlBar`
  10068. *
  10069. * @type {Object}
  10070. * @private
  10071. */
  10072. ControlBar.prototype.options_ = {
  10073. children: ['playToggle', 'volumeMenuButton', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subtitlesButton', 'captionsButton', 'audioTrackButton', 'fullscreenToggle']
  10074. };
  10075. _component2['default'].registerComponent('ControlBar', ControlBar);
  10076. exports['default'] = ControlBar;
  10077. },{"../component.js":47,"./audio-track-controls/audio-track-button.js":48,"./fullscreen-toggle.js":51,"./live-display.js":52,"./mute-toggle.js":53,"./play-toggle.js":54,"./playback-rate-menu/playback-rate-menu-button.js":55,"./progress-control/progress-control.js":60,"./spacer-controls/custom-control-spacer.js":63,"./text-track-controls/captions-button.js":66,"./text-track-controls/chapters-button.js":67,"./text-track-controls/descriptions-button.js":69,"./text-track-controls/subtitles-button.js":71,"./time-controls/current-time-display.js":74,"./time-controls/duration-display.js":75,"./time-controls/remaining-time-display.js":76,"./time-controls/time-divider.js":77,"./volume-control/volume-control.js":80,"./volume-menu-button.js":82}],51:[function(require,module,exports){
  10078. 'use strict';
  10079. exports.__esModule = true;
  10080. var _button = require('../button.js');
  10081. var _button2 = _interopRequireDefault(_button);
  10082. var _component = require('../component.js');
  10083. var _component2 = _interopRequireDefault(_component);
  10084. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10085. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10086. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10087. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10088. * @file fullscreen-toggle.js
  10089. */
  10090. /**
  10091. * Toggle fullscreen video
  10092. *
  10093. * @extends Button
  10094. */
  10095. var FullscreenToggle = function (_Button) {
  10096. _inherits(FullscreenToggle, _Button);
  10097. /**
  10098. * Creates an instance of this class.
  10099. *
  10100. * @param {Player} player
  10101. * The `Player` that this class should be attached to.
  10102. *
  10103. * @param {Object} [options]
  10104. * The key/value store of player options.
  10105. */
  10106. function FullscreenToggle(player, options) {
  10107. _classCallCheck(this, FullscreenToggle);
  10108. var _this = _possibleConstructorReturn(this, _Button.call(this, player, options));
  10109. _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
  10110. return _this;
  10111. }
  10112. /**
  10113. * Builds the default DOM `className`.
  10114. *
  10115. * @return {string}
  10116. * The DOM `className` for this object.
  10117. */
  10118. FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
  10119. return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
  10120. };
  10121. /**
  10122. * Handles fullscreenchange on the player and change control text accordingly.
  10123. *
  10124. * @param {EventTarget~Event} [event]
  10125. * The {@link Player#fullscreenchange} event that caused this function to be
  10126. * called.
  10127. *
  10128. * @listens Player#fullscreenchange
  10129. */
  10130. FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
  10131. if (this.player_.isFullscreen()) {
  10132. this.controlText('Non-Fullscreen');
  10133. } else {
  10134. this.controlText('Fullscreen');
  10135. }
  10136. };
  10137. /**
  10138. * This gets called when an `FullscreenToggle` is "clicked". See
  10139. * {@link ClickableComponent} for more detailed information on what a click can be.
  10140. *
  10141. * @param {EventTarget~Event} [event]
  10142. * The `keydown`, `tap`, or `click` event that caused this function to be
  10143. * called.
  10144. *
  10145. * @listens tap
  10146. * @listens click
  10147. */
  10148. FullscreenToggle.prototype.handleClick = function handleClick(event) {
  10149. if (!this.player_.isFullscreen()) {
  10150. this.player_.requestFullscreen();
  10151. } else {
  10152. this.player_.exitFullscreen();
  10153. }
  10154. };
  10155. return FullscreenToggle;
  10156. }(_button2['default']);
  10157. /**
  10158. * The text that should display over the `FullscreenToggle`s controls. Added for localization.
  10159. *
  10160. * @type {string}
  10161. * @private
  10162. */
  10163. FullscreenToggle.prototype.controlText_ = 'Fullscreen';
  10164. _component2['default'].registerComponent('FullscreenToggle', FullscreenToggle);
  10165. exports['default'] = FullscreenToggle;
  10166. },{"../button.js":44,"../component.js":47}],52:[function(require,module,exports){
  10167. 'use strict';
  10168. exports.__esModule = true;
  10169. var _component = require('../component');
  10170. var _component2 = _interopRequireDefault(_component);
  10171. var _dom = require('../utils/dom.js');
  10172. var Dom = _interopRequireWildcard(_dom);
  10173. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  10174. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10175. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10176. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10177. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10178. * @file live-display.js
  10179. */
  10180. // TODO - Future make it click to snap to live
  10181. /**
  10182. * Displays the live indicator when duration is Infinity.
  10183. *
  10184. * @extends Component
  10185. */
  10186. var LiveDisplay = function (_Component) {
  10187. _inherits(LiveDisplay, _Component);
  10188. /**
  10189. * Creates an instance of this class.
  10190. *
  10191. * @param {Player} player
  10192. * The `Player` that this class should be attached to.
  10193. *
  10194. * @param {Object} [options]
  10195. * The key/value store of player options.
  10196. */
  10197. function LiveDisplay(player, options) {
  10198. _classCallCheck(this, LiveDisplay);
  10199. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  10200. _this.updateShowing();
  10201. _this.on(_this.player(), 'durationchange', _this.updateShowing);
  10202. return _this;
  10203. }
  10204. /**
  10205. * Create the `Component`'s DOM element
  10206. *
  10207. * @return {Element}
  10208. * The element that was created.
  10209. */
  10210. LiveDisplay.prototype.createEl = function createEl() {
  10211. var el = _Component.prototype.createEl.call(this, 'div', {
  10212. className: 'vjs-live-control vjs-control'
  10213. });
  10214. this.contentEl_ = Dom.createEl('div', {
  10215. className: 'vjs-live-display',
  10216. innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE')
  10217. }, {
  10218. 'aria-live': 'off'
  10219. });
  10220. el.appendChild(this.contentEl_);
  10221. return el;
  10222. };
  10223. /**
  10224. * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
  10225. * it accordingly
  10226. *
  10227. * @param {EventTarget~Event} [event]
  10228. * The {@link Player#durationchange} event that caused this function to run.
  10229. *
  10230. * @listens Player#durationchange
  10231. */
  10232. LiveDisplay.prototype.updateShowing = function updateShowing(event) {
  10233. if (this.player().duration() === Infinity) {
  10234. this.show();
  10235. } else {
  10236. this.hide();
  10237. }
  10238. };
  10239. return LiveDisplay;
  10240. }(_component2['default']);
  10241. _component2['default'].registerComponent('LiveDisplay', LiveDisplay);
  10242. exports['default'] = LiveDisplay;
  10243. },{"../component":47,"../utils/dom.js":123}],53:[function(require,module,exports){
  10244. 'use strict';
  10245. exports.__esModule = true;
  10246. var _button = require('../button');
  10247. var _button2 = _interopRequireDefault(_button);
  10248. var _component = require('../component');
  10249. var _component2 = _interopRequireDefault(_component);
  10250. var _dom = require('../utils/dom.js');
  10251. var Dom = _interopRequireWildcard(_dom);
  10252. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  10253. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10254. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10255. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10256. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10257. * @file mute-toggle.js
  10258. */
  10259. /**
  10260. * A button component for muting the audio.
  10261. *
  10262. * @extends Button
  10263. */
  10264. var MuteToggle = function (_Button) {
  10265. _inherits(MuteToggle, _Button);
  10266. /**
  10267. * Creates an instance of this class.
  10268. *
  10269. * @param {Player} player
  10270. * The `Player` that this class should be attached to.
  10271. *
  10272. * @param {Object} [options]
  10273. * The key/value store of player options.
  10274. */
  10275. function MuteToggle(player, options) {
  10276. _classCallCheck(this, MuteToggle);
  10277. var _this = _possibleConstructorReturn(this, _Button.call(this, player, options));
  10278. _this.on(player, 'volumechange', _this.update);
  10279. // hide mute toggle if the current tech doesn't support volume control
  10280. if (player.tech_ && player.tech_.featuresVolumeControl === false) {
  10281. _this.addClass('vjs-hidden');
  10282. }
  10283. _this.on(player, 'loadstart', function () {
  10284. // We need to update the button to account for a default muted state.
  10285. this.update();
  10286. if (player.tech_.featuresVolumeControl === false) {
  10287. this.addClass('vjs-hidden');
  10288. } else {
  10289. this.removeClass('vjs-hidden');
  10290. }
  10291. });
  10292. return _this;
  10293. }
  10294. /**
  10295. * Builds the default DOM `className`.
  10296. *
  10297. * @return {string}
  10298. * The DOM `className` for this object.
  10299. */
  10300. MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
  10301. return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
  10302. };
  10303. /**
  10304. * This gets called when an `MuteToggle` is "clicked". See
  10305. * {@link ClickableComponent} for more detailed information on what a click can be.
  10306. *
  10307. * @param {EventTarget~Event} [event]
  10308. * The `keydown`, `tap`, or `click` event that caused this function to be
  10309. * called.
  10310. *
  10311. * @listens tap
  10312. * @listens click
  10313. */
  10314. MuteToggle.prototype.handleClick = function handleClick(event) {
  10315. this.player_.muted(this.player_.muted() ? false : true);
  10316. };
  10317. /**
  10318. * Update the state of volume.
  10319. *
  10320. * @param {EventTarget~Event} [event]
  10321. * The {@link Player#loadstart} event if this function was called through an
  10322. * event.
  10323. *
  10324. * @listens Player#loadstart
  10325. */
  10326. MuteToggle.prototype.update = function update(event) {
  10327. var vol = this.player_.volume();
  10328. var level = 3;
  10329. if (this.player_.muted()) {
  10330. level = 0;
  10331. } else if (vol < 0.33) {
  10332. level = 1;
  10333. } else if (vol < 0.67) {
  10334. level = 2;
  10335. }
  10336. // Don't rewrite the button text if the actual text doesn't change.
  10337. // This causes unnecessary and confusing information for screen reader users.
  10338. // This check is needed because this function gets called every time the volume level is changed.
  10339. var toMute = this.player_.muted() ? 'Unmute' : 'Mute';
  10340. if (this.controlText() !== toMute) {
  10341. this.controlText(toMute);
  10342. }
  10343. // TODO improve muted icon classes
  10344. for (var i = 0; i < 4; i++) {
  10345. Dom.removeElClass(this.el_, 'vjs-vol-' + i);
  10346. }
  10347. Dom.addElClass(this.el_, 'vjs-vol-' + level);
  10348. };
  10349. return MuteToggle;
  10350. }(_button2['default']);
  10351. /**
  10352. * The text that should display over the `MuteToggle`s controls. Added for localization.
  10353. *
  10354. * @type {string}
  10355. * @private
  10356. */
  10357. MuteToggle.prototype.controlText_ = 'Mute';
  10358. _component2['default'].registerComponent('MuteToggle', MuteToggle);
  10359. exports['default'] = MuteToggle;
  10360. },{"../button":44,"../component":47,"../utils/dom.js":123}],54:[function(require,module,exports){
  10361. 'use strict';
  10362. exports.__esModule = true;
  10363. var _button = require('../button.js');
  10364. var _button2 = _interopRequireDefault(_button);
  10365. var _component = require('../component.js');
  10366. var _component2 = _interopRequireDefault(_component);
  10367. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10368. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10369. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10370. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10371. * @file play-toggle.js
  10372. */
  10373. /**
  10374. * Button to toggle between play and pause.
  10375. *
  10376. * @extends Button
  10377. */
  10378. var PlayToggle = function (_Button) {
  10379. _inherits(PlayToggle, _Button);
  10380. /**
  10381. * Creates an instance of this class.
  10382. *
  10383. * @param {Player} player
  10384. * The `Player` that this class should be attached to.
  10385. *
  10386. * @param {Object} [options]
  10387. * The key/value store of player options.
  10388. */
  10389. function PlayToggle(player, options) {
  10390. _classCallCheck(this, PlayToggle);
  10391. var _this = _possibleConstructorReturn(this, _Button.call(this, player, options));
  10392. _this.on(player, 'play', _this.handlePlay);
  10393. _this.on(player, 'pause', _this.handlePause);
  10394. return _this;
  10395. }
  10396. /**
  10397. * Builds the default DOM `className`.
  10398. *
  10399. * @return {string}
  10400. * The DOM `className` for this object.
  10401. */
  10402. PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
  10403. return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
  10404. };
  10405. /**
  10406. * This gets called when an `PlayToggle` is "clicked". See
  10407. * {@link ClickableComponent} for more detailed information on what a click can be.
  10408. *
  10409. * @param {EventTarget~Event} [event]
  10410. * The `keydown`, `tap`, or `click` event that caused this function to be
  10411. * called.
  10412. *
  10413. * @listens tap
  10414. * @listens click
  10415. */
  10416. PlayToggle.prototype.handleClick = function handleClick(event) {
  10417. if (this.player_.paused()) {
  10418. this.player_.play();
  10419. } else {
  10420. this.player_.pause();
  10421. }
  10422. };
  10423. /**
  10424. * Add the vjs-playing class to the element so it can change appearance.
  10425. *
  10426. * @param {EventTarget~Event} [event]
  10427. * The event that caused this function to run.
  10428. *
  10429. * @listens Player#play
  10430. */
  10431. PlayToggle.prototype.handlePlay = function handlePlay(event) {
  10432. this.removeClass('vjs-paused');
  10433. this.addClass('vjs-playing');
  10434. // change the button text to "Pause"
  10435. this.controlText('Pause');
  10436. };
  10437. /**
  10438. * Add the vjs-paused class to the element so it can change appearance.
  10439. *
  10440. * @param {EventTarget~Event} [event]
  10441. * The event that caused this function to run.
  10442. *
  10443. * @listens Player#pause
  10444. */
  10445. PlayToggle.prototype.handlePause = function handlePause(event) {
  10446. this.removeClass('vjs-playing');
  10447. this.addClass('vjs-paused');
  10448. // change the button text to "Play"
  10449. this.controlText('Play');
  10450. };
  10451. return PlayToggle;
  10452. }(_button2['default']);
  10453. /**
  10454. * The text that should display over the `PlayToggle`s controls. Added for localization.
  10455. *
  10456. * @type {string}
  10457. * @private
  10458. */
  10459. PlayToggle.prototype.controlText_ = 'Play';
  10460. _component2['default'].registerComponent('PlayToggle', PlayToggle);
  10461. exports['default'] = PlayToggle;
  10462. },{"../button.js":44,"../component.js":47}],55:[function(require,module,exports){
  10463. 'use strict';
  10464. exports.__esModule = true;
  10465. var _menuButton = require('../../menu/menu-button.js');
  10466. var _menuButton2 = _interopRequireDefault(_menuButton);
  10467. var _menu = require('../../menu/menu.js');
  10468. var _menu2 = _interopRequireDefault(_menu);
  10469. var _playbackRateMenuItem = require('./playback-rate-menu-item.js');
  10470. var _playbackRateMenuItem2 = _interopRequireDefault(_playbackRateMenuItem);
  10471. var _component = require('../../component.js');
  10472. var _component2 = _interopRequireDefault(_component);
  10473. var _dom = require('../../utils/dom.js');
  10474. var Dom = _interopRequireWildcard(_dom);
  10475. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  10476. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10477. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10478. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10479. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10480. * @file playback-rate-menu-button.js
  10481. */
  10482. /**
  10483. * The component for controlling the playback rate.
  10484. *
  10485. * @extends MenuButton
  10486. */
  10487. var PlaybackRateMenuButton = function (_MenuButton) {
  10488. _inherits(PlaybackRateMenuButton, _MenuButton);
  10489. /**
  10490. * Creates an instance of this class.
  10491. *
  10492. * @param {Player} player
  10493. * The `Player` that this class should be attached to.
  10494. *
  10495. * @param {Object} [options]
  10496. * The key/value store of player options.
  10497. */
  10498. function PlaybackRateMenuButton(player, options) {
  10499. _classCallCheck(this, PlaybackRateMenuButton);
  10500. var _this = _possibleConstructorReturn(this, _MenuButton.call(this, player, options));
  10501. _this.updateVisibility();
  10502. _this.updateLabel();
  10503. _this.on(player, 'loadstart', _this.updateVisibility);
  10504. _this.on(player, 'ratechange', _this.updateLabel);
  10505. return _this;
  10506. }
  10507. /**
  10508. * Create the `Component`'s DOM element
  10509. *
  10510. * @return {Element}
  10511. * The element that was created.
  10512. */
  10513. PlaybackRateMenuButton.prototype.createEl = function createEl() {
  10514. var el = _MenuButton.prototype.createEl.call(this);
  10515. this.labelEl_ = Dom.createEl('div', {
  10516. className: 'vjs-playback-rate-value',
  10517. innerHTML: 1.0
  10518. });
  10519. el.appendChild(this.labelEl_);
  10520. return el;
  10521. };
  10522. /**
  10523. * Builds the default DOM `className`.
  10524. *
  10525. * @return {string}
  10526. * The DOM `className` for this object.
  10527. */
  10528. PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
  10529. return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
  10530. };
  10531. /**
  10532. * Create the playback rate menu
  10533. *
  10534. * @return {Menu}
  10535. * Menu object populated with {@link PlaybackRateMenuItem}s
  10536. */
  10537. PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
  10538. var menu = new _menu2['default'](this.player());
  10539. var rates = this.playbackRates();
  10540. if (rates) {
  10541. for (var i = rates.length - 1; i >= 0; i--) {
  10542. menu.addChild(new _playbackRateMenuItem2['default'](this.player(), { rate: rates[i] + 'x' }));
  10543. }
  10544. }
  10545. return menu;
  10546. };
  10547. /**
  10548. * Updates ARIA accessibility attributes
  10549. */
  10550. PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
  10551. // Current playback rate
  10552. this.el().setAttribute('aria-valuenow', this.player().playbackRate());
  10553. };
  10554. /**
  10555. * This gets called when an `PlaybackRateMenuButton` is "clicked". See
  10556. * {@link ClickableComponent} for more detailed information on what a click can be.
  10557. *
  10558. * @param {EventTarget~Event} [event]
  10559. * The `keydown`, `tap`, or `click` event that caused this function to be
  10560. * called.
  10561. *
  10562. * @listens tap
  10563. * @listens click
  10564. */
  10565. PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
  10566. // select next rate option
  10567. var currentRate = this.player().playbackRate();
  10568. var rates = this.playbackRates();
  10569. // this will select first one if the last one currently selected
  10570. var newRate = rates[0];
  10571. for (var i = 0; i < rates.length; i++) {
  10572. if (rates[i] > currentRate) {
  10573. newRate = rates[i];
  10574. break;
  10575. }
  10576. }
  10577. this.player().playbackRate(newRate);
  10578. };
  10579. /**
  10580. * Get possible playback rates
  10581. *
  10582. * @return {Array}
  10583. * All possible playback rates
  10584. */
  10585. PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
  10586. return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
  10587. };
  10588. /**
  10589. * Get whether playback rates is supported by the tech
  10590. * and an array of playback rates exists
  10591. *
  10592. * @return {boolean}
  10593. * Whether changing playback rate is supported
  10594. */
  10595. PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
  10596. return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
  10597. };
  10598. /**
  10599. * Hide playback rate controls when they're no playback rate options to select
  10600. *
  10601. * @param {EventTarget~Event} [event]
  10602. * The event that caused this function to run.
  10603. *
  10604. * @listens Player#loadstart
  10605. */
  10606. PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
  10607. if (this.playbackRateSupported()) {
  10608. this.removeClass('vjs-hidden');
  10609. } else {
  10610. this.addClass('vjs-hidden');
  10611. }
  10612. };
  10613. /**
  10614. * Update button label when rate changed
  10615. *
  10616. * @param {EventTarget~Event} [event]
  10617. * The event that caused this function to run.
  10618. *
  10619. * @listens Player#ratechange
  10620. */
  10621. PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
  10622. if (this.playbackRateSupported()) {
  10623. this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
  10624. }
  10625. };
  10626. return PlaybackRateMenuButton;
  10627. }(_menuButton2['default']);
  10628. /**
  10629. * The text that should display over the `FullscreenToggle`s controls. Added for localization.
  10630. *
  10631. * @type {string}
  10632. * @private
  10633. */
  10634. PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
  10635. _component2['default'].registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
  10636. exports['default'] = PlaybackRateMenuButton;
  10637. },{"../../component.js":47,"../../menu/menu-button.js":89,"../../menu/menu.js":91,"../../utils/dom.js":123,"./playback-rate-menu-item.js":56}],56:[function(require,module,exports){
  10638. 'use strict';
  10639. exports.__esModule = true;
  10640. var _menuItem = require('../../menu/menu-item.js');
  10641. var _menuItem2 = _interopRequireDefault(_menuItem);
  10642. var _component = require('../../component.js');
  10643. var _component2 = _interopRequireDefault(_component);
  10644. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10645. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10646. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10647. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10648. * @file playback-rate-menu-item.js
  10649. */
  10650. /**
  10651. * The specific menu item type for selecting a playback rate.
  10652. *
  10653. * @extends MenuItem
  10654. */
  10655. var PlaybackRateMenuItem = function (_MenuItem) {
  10656. _inherits(PlaybackRateMenuItem, _MenuItem);
  10657. /**
  10658. * Creates an instance of this class.
  10659. *
  10660. * @param {Player} player
  10661. * The `Player` that this class should be attached to.
  10662. *
  10663. * @param {Object} [options]
  10664. * The key/value store of player options.
  10665. */
  10666. function PlaybackRateMenuItem(player, options) {
  10667. _classCallCheck(this, PlaybackRateMenuItem);
  10668. var label = options.rate;
  10669. var rate = parseFloat(label, 10);
  10670. // Modify options for parent MenuItem class's init.
  10671. options.label = label;
  10672. options.selected = rate === 1;
  10673. options.selectable = true;
  10674. var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  10675. _this.label = label;
  10676. _this.rate = rate;
  10677. _this.on(player, 'ratechange', _this.update);
  10678. return _this;
  10679. }
  10680. /**
  10681. * This gets called when an `PlaybackRateMenuItem` is "clicked". See
  10682. * {@link ClickableComponent} for more detailed information on what a click can be.
  10683. *
  10684. * @param {EventTarget~Event} [event]
  10685. * The `keydown`, `tap`, or `click` event that caused this function to be
  10686. * called.
  10687. *
  10688. * @listens tap
  10689. * @listens click
  10690. */
  10691. PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
  10692. _MenuItem.prototype.handleClick.call(this);
  10693. this.player().playbackRate(this.rate);
  10694. };
  10695. /**
  10696. * Update the PlaybackRateMenuItem when the playbackrate changes.
  10697. *
  10698. * @param {EventTarget~Event} [event]
  10699. * The `ratechange` event that caused this function to run.
  10700. *
  10701. * @listens Player#ratechange
  10702. */
  10703. PlaybackRateMenuItem.prototype.update = function update(event) {
  10704. this.selected(this.player().playbackRate() === this.rate);
  10705. };
  10706. return PlaybackRateMenuItem;
  10707. }(_menuItem2['default']);
  10708. /**
  10709. * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
  10710. *
  10711. * @type {string}
  10712. * @private
  10713. */
  10714. PlaybackRateMenuItem.prototype.contentElType = 'button';
  10715. _component2['default'].registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
  10716. exports['default'] = PlaybackRateMenuItem;
  10717. },{"../../component.js":47,"../../menu/menu-item.js":90}],57:[function(require,module,exports){
  10718. 'use strict';
  10719. exports.__esModule = true;
  10720. var _component = require('../../component.js');
  10721. var _component2 = _interopRequireDefault(_component);
  10722. var _dom = require('../../utils/dom.js');
  10723. var Dom = _interopRequireWildcard(_dom);
  10724. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  10725. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10726. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10727. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10728. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10729. * @file load-progress-bar.js
  10730. */
  10731. /**
  10732. * Shows loading progress
  10733. *
  10734. * @extends Component
  10735. */
  10736. var LoadProgressBar = function (_Component) {
  10737. _inherits(LoadProgressBar, _Component);
  10738. /**
  10739. * Creates an instance of this class.
  10740. *
  10741. * @param {Player} player
  10742. * The `Player` that this class should be attached to.
  10743. *
  10744. * @param {Object} [options]
  10745. * The key/value store of player options.
  10746. */
  10747. function LoadProgressBar(player, options) {
  10748. _classCallCheck(this, LoadProgressBar);
  10749. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  10750. _this.partEls_ = [];
  10751. _this.on(player, 'progress', _this.update);
  10752. return _this;
  10753. }
  10754. /**
  10755. * Create the `Component`'s DOM element
  10756. *
  10757. * @return {Element}
  10758. * The element that was created.
  10759. */
  10760. LoadProgressBar.prototype.createEl = function createEl() {
  10761. return _Component.prototype.createEl.call(this, 'div', {
  10762. className: 'vjs-load-progress',
  10763. innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
  10764. });
  10765. };
  10766. /**
  10767. * Update progress bar
  10768. *
  10769. * @param {EventTarget~Event} [event]
  10770. * The `progress` event that caused this function to run.
  10771. *
  10772. * @listens Player#progress
  10773. */
  10774. LoadProgressBar.prototype.update = function update(event) {
  10775. var buffered = this.player_.buffered();
  10776. var duration = this.player_.duration();
  10777. var bufferedEnd = this.player_.bufferedEnd();
  10778. var children = this.partEls_;
  10779. // get the percent width of a time compared to the total end
  10780. var percentify = function percentify(time, end) {
  10781. // no NaN
  10782. var percent = time / end || 0;
  10783. return (percent >= 1 ? 1 : percent) * 100 + '%';
  10784. };
  10785. // update the width of the progress bar
  10786. this.el_.style.width = percentify(bufferedEnd, duration);
  10787. // add child elements to represent the individual buffered time ranges
  10788. for (var i = 0; i < buffered.length; i++) {
  10789. var start = buffered.start(i);
  10790. var end = buffered.end(i);
  10791. var part = children[i];
  10792. if (!part) {
  10793. part = this.el_.appendChild(Dom.createEl());
  10794. children[i] = part;
  10795. }
  10796. // set the percent based on the width of the progress bar (bufferedEnd)
  10797. part.style.left = percentify(start, bufferedEnd);
  10798. part.style.width = percentify(end - start, bufferedEnd);
  10799. }
  10800. // remove unused buffered range elements
  10801. for (var _i = children.length; _i > buffered.length; _i--) {
  10802. this.el_.removeChild(children[_i - 1]);
  10803. }
  10804. children.length = buffered.length;
  10805. };
  10806. return LoadProgressBar;
  10807. }(_component2['default']);
  10808. _component2['default'].registerComponent('LoadProgressBar', LoadProgressBar);
  10809. exports['default'] = LoadProgressBar;
  10810. },{"../../component.js":47,"../../utils/dom.js":123}],58:[function(require,module,exports){
  10811. 'use strict';
  10812. exports.__esModule = true;
  10813. var _component = require('../../component.js');
  10814. var _component2 = _interopRequireDefault(_component);
  10815. var _dom = require('../../utils/dom.js');
  10816. var Dom = _interopRequireWildcard(_dom);
  10817. var _fn = require('../../utils/fn.js');
  10818. var Fn = _interopRequireWildcard(_fn);
  10819. var _formatTime = require('../../utils/format-time.js');
  10820. var _formatTime2 = _interopRequireDefault(_formatTime);
  10821. var _computedStyle = require('../../utils/computed-style.js');
  10822. var _computedStyle2 = _interopRequireDefault(_computedStyle);
  10823. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  10824. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10825. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10826. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10827. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10828. * @file mouse-time-display.js
  10829. */
  10830. /**
  10831. * The Mouse Time Display component shows the time you will seek to
  10832. * when hovering over the progress bar
  10833. *
  10834. * @extends Component
  10835. */
  10836. var MouseTimeDisplay = function (_Component) {
  10837. _inherits(MouseTimeDisplay, _Component);
  10838. /**
  10839. * Creates an instance of this class.
  10840. *
  10841. * @param {Player} player
  10842. * The `Player` that this class should be attached to.
  10843. *
  10844. * @param {Object} [options]
  10845. * The key/value store of player options.
  10846. */
  10847. function MouseTimeDisplay(player, options) {
  10848. _classCallCheck(this, MouseTimeDisplay);
  10849. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  10850. if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
  10851. _this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
  10852. }
  10853. if (_this.keepTooltipsInside) {
  10854. _this.tooltip = Dom.createEl('div', { className: 'vjs-time-tooltip' });
  10855. _this.el().appendChild(_this.tooltip);
  10856. _this.addClass('vjs-keep-tooltips-inside');
  10857. }
  10858. _this.update(0, 0);
  10859. player.on('ready', function () {
  10860. _this.on(player.controlBar.progressControl.el(), 'mousemove', Fn.throttle(Fn.bind(_this, _this.handleMouseMove), 25));
  10861. });
  10862. return _this;
  10863. }
  10864. /**
  10865. * Create the `Component`'s DOM element
  10866. *
  10867. * @return {Element}
  10868. * The element that was created.
  10869. */
  10870. MouseTimeDisplay.prototype.createEl = function createEl() {
  10871. return _Component.prototype.createEl.call(this, 'div', {
  10872. className: 'vjs-mouse-display'
  10873. });
  10874. };
  10875. /**
  10876. * Handle the mouse move event on the `MouseTimeDisplay`.
  10877. *
  10878. * @param {EventTarget~Event} event
  10879. * The `mousemove` event that caused this to event to run.
  10880. *
  10881. * @listen mousemove
  10882. */
  10883. MouseTimeDisplay.prototype.handleMouseMove = function handleMouseMove(event) {
  10884. var duration = this.player_.duration();
  10885. var newTime = this.calculateDistance(event) * duration;
  10886. var position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
  10887. this.update(newTime, position);
  10888. };
  10889. /**
  10890. * Update the time and posistion of the `MouseTimeDisplay`.
  10891. *
  10892. * @param {number} newTime
  10893. * Time to change the `MouseTimeDisplay` to.
  10894. *
  10895. * @param {nubmer} position
  10896. * Postion from the left of the in pixels.
  10897. */
  10898. MouseTimeDisplay.prototype.update = function update(newTime, position) {
  10899. var time = (0, _formatTime2['default'])(newTime, this.player_.duration());
  10900. this.el().style.left = position + 'px';
  10901. this.el().setAttribute('data-current-time', time);
  10902. if (this.keepTooltipsInside) {
  10903. var clampedPosition = this.clampPosition_(position);
  10904. var difference = position - clampedPosition + 1;
  10905. var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltip, 'width'));
  10906. var tooltipWidthHalf = tooltipWidth / 2;
  10907. this.tooltip.innerHTML = time;
  10908. this.tooltip.style.right = '-' + (tooltipWidthHalf - difference) + 'px';
  10909. }
  10910. };
  10911. /**
  10912. * Get the mouse pointers x coordinate in pixels.
  10913. *
  10914. * @param {EventTarget~Event} [event]
  10915. * The `mousemove` event that was passed to this function by
  10916. * {@link MouseTimeDisplay#handleMouseMove}
  10917. *
  10918. * @return {number}
  10919. * THe x position in pixels of the mouse pointer.
  10920. */
  10921. MouseTimeDisplay.prototype.calculateDistance = function calculateDistance(event) {
  10922. return Dom.getPointerPosition(this.el().parentNode, event).x;
  10923. };
  10924. /**
  10925. * This takes in a horizontal position for the bar and returns a clamped position.
  10926. * Clamped position means that it will keep the position greater than half the width
  10927. * of the tooltip and smaller than the player width minus half the width o the tooltip.
  10928. * It will only clamp the position if `keepTooltipsInside` option is set.
  10929. *
  10930. * @param {number} position
  10931. * The position the bar wants to be
  10932. *
  10933. * @return {number}
  10934. * The (potentially) new clamped position.
  10935. *
  10936. * @private
  10937. */
  10938. MouseTimeDisplay.prototype.clampPosition_ = function clampPosition_(position) {
  10939. if (!this.keepTooltipsInside) {
  10940. return position;
  10941. }
  10942. var playerWidth = parseFloat((0, _computedStyle2['default'])(this.player().el(), 'width'));
  10943. var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltip, 'width'));
  10944. var tooltipWidthHalf = tooltipWidth / 2;
  10945. var actualPosition = position;
  10946. if (position < tooltipWidthHalf) {
  10947. actualPosition = Math.ceil(tooltipWidthHalf);
  10948. } else if (position > playerWidth - tooltipWidthHalf) {
  10949. actualPosition = Math.floor(playerWidth - tooltipWidthHalf);
  10950. }
  10951. return actualPosition;
  10952. };
  10953. return MouseTimeDisplay;
  10954. }(_component2['default']);
  10955. _component2['default'].registerComponent('MouseTimeDisplay', MouseTimeDisplay);
  10956. exports['default'] = MouseTimeDisplay;
  10957. },{"../../component.js":47,"../../utils/computed-style.js":122,"../../utils/dom.js":123,"../../utils/fn.js":125,"../../utils/format-time.js":126}],59:[function(require,module,exports){
  10958. 'use strict';
  10959. exports.__esModule = true;
  10960. var _component = require('../../component.js');
  10961. var _component2 = _interopRequireDefault(_component);
  10962. var _fn = require('../../utils/fn.js');
  10963. var Fn = _interopRequireWildcard(_fn);
  10964. var _formatTime = require('../../utils/format-time.js');
  10965. var _formatTime2 = _interopRequireDefault(_formatTime);
  10966. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  10967. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  10968. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  10969. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  10970. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  10971. * @file play-progress-bar.js
  10972. */
  10973. /**
  10974. * Shows play progress
  10975. *
  10976. * @extends Component
  10977. */
  10978. var PlayProgressBar = function (_Component) {
  10979. _inherits(PlayProgressBar, _Component);
  10980. /**
  10981. * Creates an instance of this class.
  10982. *
  10983. * @param {Player} player
  10984. * The `Player` that this class should be attached to.
  10985. *
  10986. * @param {Object} [options]
  10987. * The key/value store of player options.
  10988. */
  10989. function PlayProgressBar(player, options) {
  10990. _classCallCheck(this, PlayProgressBar);
  10991. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  10992. _this.updateDataAttr();
  10993. _this.on(player, 'timeupdate', _this.updateDataAttr);
  10994. player.ready(Fn.bind(_this, _this.updateDataAttr));
  10995. if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
  10996. _this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
  10997. }
  10998. if (_this.keepTooltipsInside) {
  10999. _this.addClass('vjs-keep-tooltips-inside');
  11000. }
  11001. return _this;
  11002. }
  11003. /**
  11004. * Create the `Component`'s DOM element
  11005. *
  11006. * @return {Element}
  11007. * The element that was created.
  11008. */
  11009. PlayProgressBar.prototype.createEl = function createEl() {
  11010. return _Component.prototype.createEl.call(this, 'div', {
  11011. className: 'vjs-play-progress vjs-slider-bar',
  11012. innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
  11013. });
  11014. };
  11015. /**
  11016. * Update the data-current-time attribute on the `PlayProgressBar`.
  11017. *
  11018. * @param {EventTarget~Event} [event]
  11019. * The `timeupdate` event that caused this to run.
  11020. *
  11021. * @listens Player#timeupdate
  11022. */
  11023. PlayProgressBar.prototype.updateDataAttr = function updateDataAttr(event) {
  11024. var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  11025. this.el_.setAttribute('data-current-time', (0, _formatTime2['default'])(time, this.player_.duration()));
  11026. };
  11027. return PlayProgressBar;
  11028. }(_component2['default']);
  11029. _component2['default'].registerComponent('PlayProgressBar', PlayProgressBar);
  11030. exports['default'] = PlayProgressBar;
  11031. },{"../../component.js":47,"../../utils/fn.js":125,"../../utils/format-time.js":126}],60:[function(require,module,exports){
  11032. 'use strict';
  11033. exports.__esModule = true;
  11034. var _component = require('../../component.js');
  11035. var _component2 = _interopRequireDefault(_component);
  11036. require('./seek-bar.js');
  11037. require('./mouse-time-display.js');
  11038. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11039. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11040. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11041. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11042. * @file progress-control.js
  11043. */
  11044. /**
  11045. * The Progress Control component contains the seek bar, load progress,
  11046. * and play progress.
  11047. *
  11048. * @extends Component
  11049. */
  11050. var ProgressControl = function (_Component) {
  11051. _inherits(ProgressControl, _Component);
  11052. function ProgressControl() {
  11053. _classCallCheck(this, ProgressControl);
  11054. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  11055. }
  11056. /**
  11057. * Create the `Component`'s DOM element
  11058. *
  11059. * @return {Element}
  11060. * The element that was created.
  11061. */
  11062. ProgressControl.prototype.createEl = function createEl() {
  11063. return _Component.prototype.createEl.call(this, 'div', {
  11064. className: 'vjs-progress-control vjs-control'
  11065. });
  11066. };
  11067. return ProgressControl;
  11068. }(_component2['default']);
  11069. /**
  11070. * Default options for `ProgressControl`
  11071. *
  11072. * @type {Object}
  11073. * @private
  11074. */
  11075. ProgressControl.prototype.options_ = {
  11076. children: ['seekBar']
  11077. };
  11078. _component2['default'].registerComponent('ProgressControl', ProgressControl);
  11079. exports['default'] = ProgressControl;
  11080. },{"../../component.js":47,"./mouse-time-display.js":58,"./seek-bar.js":61}],61:[function(require,module,exports){
  11081. 'use strict';
  11082. exports.__esModule = true;
  11083. var _slider = require('../../slider/slider.js');
  11084. var _slider2 = _interopRequireDefault(_slider);
  11085. var _component = require('../../component.js');
  11086. var _component2 = _interopRequireDefault(_component);
  11087. var _fn = require('../../utils/fn.js');
  11088. var Fn = _interopRequireWildcard(_fn);
  11089. var _formatTime = require('../../utils/format-time.js');
  11090. var _formatTime2 = _interopRequireDefault(_formatTime);
  11091. var _computedStyle = require('../../utils/computed-style.js');
  11092. var _computedStyle2 = _interopRequireDefault(_computedStyle);
  11093. require('./load-progress-bar.js');
  11094. require('./play-progress-bar.js');
  11095. require('./tooltip-progress-bar.js');
  11096. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  11097. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11098. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11099. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11100. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11101. * @file seek-bar.js
  11102. */
  11103. /**
  11104. * Seek Bar and holder for the progress bars
  11105. *
  11106. * @extends Slider
  11107. */
  11108. var SeekBar = function (_Slider) {
  11109. _inherits(SeekBar, _Slider);
  11110. /**
  11111. * Creates an instance of this class.
  11112. *
  11113. * @param {Player} player
  11114. * The `Player` that this class should be attached to.
  11115. *
  11116. * @param {Object} [options]
  11117. * The key/value store of player options.
  11118. */
  11119. function SeekBar(player, options) {
  11120. _classCallCheck(this, SeekBar);
  11121. var _this = _possibleConstructorReturn(this, _Slider.call(this, player, options));
  11122. _this.on(player, 'timeupdate', _this.updateProgress);
  11123. _this.on(player, 'ended', _this.updateProgress);
  11124. player.ready(Fn.bind(_this, _this.updateProgress));
  11125. if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
  11126. _this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
  11127. }
  11128. if (_this.keepTooltipsInside) {
  11129. _this.tooltipProgressBar = _this.addChild('TooltipProgressBar');
  11130. }
  11131. return _this;
  11132. }
  11133. /**
  11134. * Create the `Component`'s DOM element
  11135. *
  11136. * @return {Element}
  11137. * The element that was created.
  11138. */
  11139. SeekBar.prototype.createEl = function createEl() {
  11140. return _Slider.prototype.createEl.call(this, 'div', {
  11141. className: 'vjs-progress-holder'
  11142. }, {
  11143. 'aria-label': 'progress bar'
  11144. });
  11145. };
  11146. /**
  11147. * Update the seek bars tooltip and width.
  11148. *
  11149. * @param {EventTarget~Event} [event]
  11150. * The `timeupdate` or `ended` event that caused this to run.
  11151. *
  11152. * @listens Player#timeupdate
  11153. * @listens Player#ended
  11154. */
  11155. SeekBar.prototype.updateProgress = function updateProgress(event) {
  11156. this.updateAriaAttributes(this.el_);
  11157. if (this.keepTooltipsInside) {
  11158. this.updateAriaAttributes(this.tooltipProgressBar.el_);
  11159. this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width;
  11160. var playerWidth = parseFloat((0, _computedStyle2['default'])(this.player().el(), 'width'));
  11161. var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltipProgressBar.tooltip, 'width'));
  11162. var tooltipStyle = this.tooltipProgressBar.el().style;
  11163. tooltipStyle.maxWidth = Math.floor(playerWidth - tooltipWidth / 2) + 'px';
  11164. tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px';
  11165. tooltipStyle.right = '-' + tooltipWidth / 2 + 'px';
  11166. }
  11167. };
  11168. /**
  11169. * Update ARIA accessibility attributes
  11170. *
  11171. * @param {Element} el
  11172. * The element to update with aria accessibility attributes.
  11173. */
  11174. SeekBar.prototype.updateAriaAttributes = function updateAriaAttributes(el) {
  11175. // Allows for smooth scrubbing, when player can't keep up.
  11176. var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  11177. // machine readable value of progress bar (percentage complete)
  11178. el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2));
  11179. // human readable value of progress bar (time complete)
  11180. el.setAttribute('aria-valuetext', (0, _formatTime2['default'])(time, this.player_.duration()));
  11181. };
  11182. /**
  11183. * Get percentage of video played
  11184. *
  11185. * @return {number}
  11186. * The percentage played
  11187. */
  11188. SeekBar.prototype.getPercent = function getPercent() {
  11189. var percent = this.player_.currentTime() / this.player_.duration();
  11190. return percent >= 1 ? 1 : percent;
  11191. };
  11192. /**
  11193. * Handle mouse down on seek bar
  11194. *
  11195. * @param {EventTarget~Event} event
  11196. * The `mousedown` event that caused this to run.
  11197. *
  11198. * @listens mousedown
  11199. */
  11200. SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
  11201. this.player_.scrubbing(true);
  11202. this.videoWasPlaying = !this.player_.paused();
  11203. this.player_.pause();
  11204. _Slider.prototype.handleMouseDown.call(this, event);
  11205. };
  11206. /**
  11207. * Handle mouse move on seek bar
  11208. *
  11209. * @param {EventTarget~Event} event
  11210. * The `mousemove` event that caused this to run.
  11211. *
  11212. * @listens mousemove
  11213. */
  11214. SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
  11215. var newTime = this.calculateDistance(event) * this.player_.duration();
  11216. // Don't let video end while scrubbing.
  11217. if (newTime === this.player_.duration()) {
  11218. newTime = newTime - 0.1;
  11219. }
  11220. // Set new time (tell player to seek to new time)
  11221. this.player_.currentTime(newTime);
  11222. };
  11223. /**
  11224. * Handle mouse up on seek bar
  11225. *
  11226. * @param {EventTarget~Event} event
  11227. * The `mouseup` event that caused this to run.
  11228. *
  11229. * @listens mouseup
  11230. */
  11231. SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
  11232. _Slider.prototype.handleMouseUp.call(this, event);
  11233. this.player_.scrubbing(false);
  11234. if (this.videoWasPlaying) {
  11235. this.player_.play();
  11236. }
  11237. };
  11238. /**
  11239. * Move more quickly fast forward for keyboard-only users
  11240. */
  11241. SeekBar.prototype.stepForward = function stepForward() {
  11242. // more quickly fast forward for keyboard-only users
  11243. this.player_.currentTime(this.player_.currentTime() + 5);
  11244. };
  11245. /**
  11246. * Move more quickly rewind for keyboard-only users
  11247. */
  11248. SeekBar.prototype.stepBack = function stepBack() {
  11249. // more quickly rewind for keyboard-only users
  11250. this.player_.currentTime(this.player_.currentTime() - 5);
  11251. };
  11252. return SeekBar;
  11253. }(_slider2['default']);
  11254. /**
  11255. * Default options for the `SeekBar`
  11256. *
  11257. * @type {Object}
  11258. * @private
  11259. */
  11260. SeekBar.prototype.options_ = {
  11261. children: ['loadProgressBar', 'mouseTimeDisplay', 'playProgressBar'],
  11262. barName: 'playProgressBar'
  11263. };
  11264. /**
  11265. * Call the update event for this Slider when this event happens on the player.
  11266. *
  11267. * @type {string}
  11268. */
  11269. SeekBar.prototype.playerEvent = 'timeupdate';
  11270. _component2['default'].registerComponent('SeekBar', SeekBar);
  11271. exports['default'] = SeekBar;
  11272. },{"../../component.js":47,"../../slider/slider.js":99,"../../utils/computed-style.js":122,"../../utils/fn.js":125,"../../utils/format-time.js":126,"./load-progress-bar.js":57,"./play-progress-bar.js":59,"./tooltip-progress-bar.js":62}],62:[function(require,module,exports){
  11273. 'use strict';
  11274. exports.__esModule = true;
  11275. var _component = require('../../component.js');
  11276. var _component2 = _interopRequireDefault(_component);
  11277. var _fn = require('../../utils/fn.js');
  11278. var Fn = _interopRequireWildcard(_fn);
  11279. var _formatTime = require('../../utils/format-time.js');
  11280. var _formatTime2 = _interopRequireDefault(_formatTime);
  11281. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  11282. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11283. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11284. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11285. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11286. * @file play-progress-bar.js
  11287. */
  11288. /**
  11289. * Shows play progress
  11290. *
  11291. * @extends Component
  11292. */
  11293. var TooltipProgressBar = function (_Component) {
  11294. _inherits(TooltipProgressBar, _Component);
  11295. /**
  11296. * Creates an instance of this class.
  11297. *
  11298. * @param {Player} player
  11299. * The `Player` that this class should be attached to.
  11300. *
  11301. * @param {Object} [options]
  11302. * The key/value store of player options.
  11303. */
  11304. function TooltipProgressBar(player, options) {
  11305. _classCallCheck(this, TooltipProgressBar);
  11306. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  11307. _this.updateDataAttr();
  11308. _this.on(player, 'timeupdate', _this.updateDataAttr);
  11309. player.ready(Fn.bind(_this, _this.updateDataAttr));
  11310. return _this;
  11311. }
  11312. /**
  11313. * Create the `Component`'s DOM element
  11314. *
  11315. * @return {Element}
  11316. * The element that was created.
  11317. */
  11318. TooltipProgressBar.prototype.createEl = function createEl() {
  11319. var el = _Component.prototype.createEl.call(this, 'div', {
  11320. className: 'vjs-tooltip-progress-bar vjs-slider-bar',
  11321. innerHTML: '<div class="vjs-time-tooltip"></div>\n <span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
  11322. });
  11323. this.tooltip = el.querySelector('.vjs-time-tooltip');
  11324. return el;
  11325. };
  11326. /**
  11327. * Updatet the data-current-time attribute for TooltipProgressBar
  11328. *
  11329. * @param {EventTarget~Event} [event]
  11330. * The `timeupdate` event that caused this function to run.
  11331. *
  11332. * @listens Player#timeupdate
  11333. */
  11334. TooltipProgressBar.prototype.updateDataAttr = function updateDataAttr(event) {
  11335. var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  11336. var formattedTime = (0, _formatTime2['default'])(time, this.player_.duration());
  11337. this.el_.setAttribute('data-current-time', formattedTime);
  11338. this.tooltip.innerHTML = formattedTime;
  11339. };
  11340. return TooltipProgressBar;
  11341. }(_component2['default']);
  11342. _component2['default'].registerComponent('TooltipProgressBar', TooltipProgressBar);
  11343. exports['default'] = TooltipProgressBar;
  11344. },{"../../component.js":47,"../../utils/fn.js":125,"../../utils/format-time.js":126}],63:[function(require,module,exports){
  11345. 'use strict';
  11346. exports.__esModule = true;
  11347. var _spacer = require('./spacer.js');
  11348. var _spacer2 = _interopRequireDefault(_spacer);
  11349. var _component = require('../../component.js');
  11350. var _component2 = _interopRequireDefault(_component);
  11351. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11352. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11353. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11354. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11355. * @file custom-control-spacer.js
  11356. */
  11357. /**
  11358. * Spacer specifically meant to be used as an insertion point for new plugins, etc.
  11359. *
  11360. * @extends Spacer
  11361. */
  11362. var CustomControlSpacer = function (_Spacer) {
  11363. _inherits(CustomControlSpacer, _Spacer);
  11364. function CustomControlSpacer() {
  11365. _classCallCheck(this, CustomControlSpacer);
  11366. return _possibleConstructorReturn(this, _Spacer.apply(this, arguments));
  11367. }
  11368. /**
  11369. * Builds the default DOM `className`.
  11370. *
  11371. * @return {string}
  11372. * The DOM `className` for this object.
  11373. */
  11374. CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
  11375. return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
  11376. };
  11377. /**
  11378. * Create the `Component`'s DOM element
  11379. *
  11380. * @return {Element}
  11381. * The element that was created.
  11382. */
  11383. CustomControlSpacer.prototype.createEl = function createEl() {
  11384. var el = _Spacer.prototype.createEl.call(this, {
  11385. className: this.buildCSSClass()
  11386. });
  11387. // No-flex/table-cell mode requires there be some content
  11388. // in the cell to fill the remaining space of the table.
  11389. el.innerHTML = '&nbsp;';
  11390. return el;
  11391. };
  11392. return CustomControlSpacer;
  11393. }(_spacer2['default']);
  11394. _component2['default'].registerComponent('CustomControlSpacer', CustomControlSpacer);
  11395. exports['default'] = CustomControlSpacer;
  11396. },{"../../component.js":47,"./spacer.js":64}],64:[function(require,module,exports){
  11397. 'use strict';
  11398. exports.__esModule = true;
  11399. var _component = require('../../component.js');
  11400. var _component2 = _interopRequireDefault(_component);
  11401. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11402. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11403. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11404. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11405. * @file spacer.js
  11406. */
  11407. /**
  11408. * Just an empty spacer element that can be used as an append point for plugins, etc.
  11409. * Also can be used to create space between elements when necessary.
  11410. *
  11411. * @extends Component
  11412. */
  11413. var Spacer = function (_Component) {
  11414. _inherits(Spacer, _Component);
  11415. function Spacer() {
  11416. _classCallCheck(this, Spacer);
  11417. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  11418. }
  11419. /**
  11420. * Builds the default DOM `className`.
  11421. *
  11422. * @return {string}
  11423. * The DOM `className` for this object.
  11424. */
  11425. Spacer.prototype.buildCSSClass = function buildCSSClass() {
  11426. return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
  11427. };
  11428. /**
  11429. * Create the `Component`'s DOM element
  11430. *
  11431. * @return {Element}
  11432. * The element that was created.
  11433. */
  11434. Spacer.prototype.createEl = function createEl() {
  11435. return _Component.prototype.createEl.call(this, 'div', {
  11436. className: this.buildCSSClass()
  11437. });
  11438. };
  11439. return Spacer;
  11440. }(_component2['default']);
  11441. _component2['default'].registerComponent('Spacer', Spacer);
  11442. exports['default'] = Spacer;
  11443. },{"../../component.js":47}],65:[function(require,module,exports){
  11444. 'use strict';
  11445. exports.__esModule = true;
  11446. var _textTrackMenuItem = require('./text-track-menu-item.js');
  11447. var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem);
  11448. var _component = require('../../component.js');
  11449. var _component2 = _interopRequireDefault(_component);
  11450. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11451. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11452. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11453. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11454. * @file caption-settings-menu-item.js
  11455. */
  11456. /**
  11457. * The menu item for caption track settings menu
  11458. *
  11459. * @extends TextTrackMenuItem
  11460. */
  11461. var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
  11462. _inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
  11463. /**
  11464. * Creates an instance of this class.
  11465. *
  11466. * @param {Player} player
  11467. * The `Player` that this class should be attached to.
  11468. *
  11469. * @param {Object} [options]
  11470. * The key/value store of player options.
  11471. */
  11472. function CaptionSettingsMenuItem(player, options) {
  11473. _classCallCheck(this, CaptionSettingsMenuItem);
  11474. options.track = {
  11475. player: player,
  11476. kind: options.kind,
  11477. label: options.kind + ' settings',
  11478. selectable: false,
  11479. 'default': false,
  11480. mode: 'disabled'
  11481. };
  11482. // CaptionSettingsMenuItem has no concept of 'selected'
  11483. options.selectable = false;
  11484. var _this = _possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
  11485. _this.addClass('vjs-texttrack-settings');
  11486. _this.controlText(', opens ' + options.kind + ' settings dialog');
  11487. return _this;
  11488. }
  11489. /**
  11490. * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
  11491. * {@link ClickableComponent} for more detailed information on what a click can be.
  11492. *
  11493. * @param {EventTarget~Event} [event]
  11494. * The `keydown`, `tap`, or `click` event that caused this function to be
  11495. * called.
  11496. *
  11497. * @listens tap
  11498. * @listens click
  11499. */
  11500. CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
  11501. this.player().getChild('textTrackSettings').show();
  11502. this.player().getChild('textTrackSettings').el_.focus();
  11503. };
  11504. return CaptionSettingsMenuItem;
  11505. }(_textTrackMenuItem2['default']);
  11506. _component2['default'].registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
  11507. exports['default'] = CaptionSettingsMenuItem;
  11508. },{"../../component.js":47,"./text-track-menu-item.js":73}],66:[function(require,module,exports){
  11509. 'use strict';
  11510. exports.__esModule = true;
  11511. var _textTrackButton = require('./text-track-button.js');
  11512. var _textTrackButton2 = _interopRequireDefault(_textTrackButton);
  11513. var _component = require('../../component.js');
  11514. var _component2 = _interopRequireDefault(_component);
  11515. var _captionSettingsMenuItem = require('./caption-settings-menu-item.js');
  11516. var _captionSettingsMenuItem2 = _interopRequireDefault(_captionSettingsMenuItem);
  11517. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11518. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11519. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11520. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11521. * @file captions-button.js
  11522. */
  11523. /**
  11524. * The button component for toggling and selecting captions
  11525. *
  11526. * @extends TextTrackButton
  11527. */
  11528. var CaptionsButton = function (_TextTrackButton) {
  11529. _inherits(CaptionsButton, _TextTrackButton);
  11530. /**
  11531. * Creates an instance of this class.
  11532. *
  11533. * @param {Player} player
  11534. * The `Player` that this class should be attached to.
  11535. *
  11536. * @param {Object} [options]
  11537. * The key/value store of player options.
  11538. *
  11539. * @param {Component~ReadyCallback} [ready]
  11540. * The function to call when this component is ready.
  11541. */
  11542. function CaptionsButton(player, options, ready) {
  11543. _classCallCheck(this, CaptionsButton);
  11544. var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  11545. _this.el_.setAttribute('aria-label', 'Captions Menu');
  11546. return _this;
  11547. }
  11548. /**
  11549. * Builds the default DOM `className`.
  11550. *
  11551. * @return {string}
  11552. * The DOM `className` for this object.
  11553. */
  11554. CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
  11555. return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  11556. };
  11557. /**
  11558. * Create caption menu items
  11559. *
  11560. * @return {CaptionSettingsMenuItem[]}
  11561. * The array of current menu items.
  11562. */
  11563. CaptionsButton.prototype.createItems = function createItems() {
  11564. var items = [];
  11565. if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
  11566. items.push(new _captionSettingsMenuItem2['default'](this.player_, { kind: this.kind_ }));
  11567. this.hideThreshold_ += 1;
  11568. }
  11569. return _TextTrackButton.prototype.createItems.call(this, items);
  11570. };
  11571. return CaptionsButton;
  11572. }(_textTrackButton2['default']);
  11573. /**
  11574. * `kind` of TextTrack to look for to associate it with this menu.
  11575. *
  11576. * @type {string}
  11577. * @private
  11578. */
  11579. CaptionsButton.prototype.kind_ = 'captions';
  11580. /**
  11581. * The text that should display over the `CaptionsButton`s controls. Added for localization.
  11582. *
  11583. * @type {string}
  11584. * @private
  11585. */
  11586. CaptionsButton.prototype.controlText_ = 'Captions';
  11587. _component2['default'].registerComponent('CaptionsButton', CaptionsButton);
  11588. exports['default'] = CaptionsButton;
  11589. },{"../../component.js":47,"./caption-settings-menu-item.js":65,"./text-track-button.js":72}],67:[function(require,module,exports){
  11590. 'use strict';
  11591. exports.__esModule = true;
  11592. var _textTrackButton = require('./text-track-button.js');
  11593. var _textTrackButton2 = _interopRequireDefault(_textTrackButton);
  11594. var _component = require('../../component.js');
  11595. var _component2 = _interopRequireDefault(_component);
  11596. var _chaptersTrackMenuItem = require('./chapters-track-menu-item.js');
  11597. var _chaptersTrackMenuItem2 = _interopRequireDefault(_chaptersTrackMenuItem);
  11598. var _toTitleCase = require('../../utils/to-title-case.js');
  11599. var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
  11600. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11601. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11602. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11603. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11604. * @file chapters-button.js
  11605. */
  11606. /**
  11607. * The button component for toggling and selecting chapters
  11608. * Chapters act much differently than other text tracks
  11609. * Cues are navigation vs. other tracks of alternative languages
  11610. *
  11611. * @extends TextTrackButton
  11612. */
  11613. var ChaptersButton = function (_TextTrackButton) {
  11614. _inherits(ChaptersButton, _TextTrackButton);
  11615. /**
  11616. * Creates an instance of this class.
  11617. *
  11618. * @param {Player} player
  11619. * The `Player` that this class should be attached to.
  11620. *
  11621. * @param {Object} [options]
  11622. * The key/value store of player options.
  11623. *
  11624. * @param {Component~ReadyCallback} [ready]
  11625. * The function to call when this function is ready.
  11626. */
  11627. function ChaptersButton(player, options, ready) {
  11628. _classCallCheck(this, ChaptersButton);
  11629. var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  11630. _this.el_.setAttribute('aria-label', 'Chapters Menu');
  11631. return _this;
  11632. }
  11633. /**
  11634. * Builds the default DOM `className`.
  11635. *
  11636. * @return {string}
  11637. * The DOM `className` for this object.
  11638. */
  11639. ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
  11640. return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  11641. };
  11642. /**
  11643. * Update the menu based on the current state of its items.
  11644. *
  11645. * @param {EventTarget~Event} [event]
  11646. * An event that triggered this function to run.
  11647. *
  11648. * @listens TextTrackList#addtrack
  11649. * @listens TextTrackList#removetrack
  11650. * @listens TextTrackList#change
  11651. */
  11652. ChaptersButton.prototype.update = function update(event) {
  11653. if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
  11654. this.setTrack(this.findChaptersTrack());
  11655. }
  11656. _TextTrackButton.prototype.update.call(this);
  11657. };
  11658. /**
  11659. * Set the currently selected track for the chapters button.
  11660. *
  11661. * @param {TextTrack} track
  11662. * The new track to select. Nothing will change if this is the currently selected
  11663. * track.
  11664. */
  11665. ChaptersButton.prototype.setTrack = function setTrack(track) {
  11666. if (this.track_ === track) {
  11667. return;
  11668. }
  11669. if (!this.updateHandler_) {
  11670. this.updateHandler_ = this.update.bind(this);
  11671. }
  11672. // here this.track_ refers to the old track instance
  11673. if (this.track_) {
  11674. var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
  11675. if (remoteTextTrackEl) {
  11676. remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
  11677. }
  11678. this.track_ = null;
  11679. }
  11680. this.track_ = track;
  11681. // here this.track_ refers to the new track instance
  11682. if (this.track_) {
  11683. this.track_.mode = 'hidden';
  11684. var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
  11685. if (_remoteTextTrackEl) {
  11686. _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
  11687. }
  11688. }
  11689. };
  11690. /**
  11691. * Find the track object that is currently in use by this ChaptersButton
  11692. *
  11693. * @return {TextTrack|undefined}
  11694. * The current track or undefined if none was found.
  11695. */
  11696. ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
  11697. var tracks = this.player_.textTracks() || [];
  11698. for (var i = tracks.length - 1; i >= 0; i--) {
  11699. // We will always choose the last track as our chaptersTrack
  11700. var track = tracks[i];
  11701. if (track.kind === this.kind_) {
  11702. return track;
  11703. }
  11704. }
  11705. };
  11706. /**
  11707. * Get the caption for the ChaptersButton based on the track label. This will also
  11708. * use the current tracks localized kind as a fallback if a label does not exist.
  11709. *
  11710. * @return {string}
  11711. * The tracks current label or the localized track kind.
  11712. */
  11713. ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
  11714. if (this.track_ && this.track_.label) {
  11715. return this.track_.label;
  11716. }
  11717. return this.localize((0, _toTitleCase2['default'])(this.kind_));
  11718. };
  11719. /**
  11720. * Create menu from chapter track
  11721. *
  11722. * @return {Menu}
  11723. * New menu for the chapter buttons
  11724. */
  11725. ChaptersButton.prototype.createMenu = function createMenu() {
  11726. this.options_.title = this.getMenuCaption();
  11727. return _TextTrackButton.prototype.createMenu.call(this);
  11728. };
  11729. /**
  11730. * Create a menu item for each text track
  11731. *
  11732. * @return {TextTrackMenuItem[]}
  11733. * Array of menu items
  11734. */
  11735. ChaptersButton.prototype.createItems = function createItems() {
  11736. var items = [];
  11737. if (!this.track_) {
  11738. return items;
  11739. }
  11740. var cues = this.track_.cues;
  11741. if (!cues) {
  11742. return items;
  11743. }
  11744. for (var i = 0, l = cues.length; i < l; i++) {
  11745. var cue = cues[i];
  11746. var mi = new _chaptersTrackMenuItem2['default'](this.player_, { track: this.track_, cue: cue });
  11747. items.push(mi);
  11748. }
  11749. return items;
  11750. };
  11751. return ChaptersButton;
  11752. }(_textTrackButton2['default']);
  11753. /**
  11754. * `kind` of TextTrack to look for to associate it with this menu.
  11755. *
  11756. * @type {string}
  11757. * @private
  11758. */
  11759. ChaptersButton.prototype.kind_ = 'chapters';
  11760. /**
  11761. * The text that should display over the `ChaptersButton`s controls. Added for localization.
  11762. *
  11763. * @type {string}
  11764. * @private
  11765. */
  11766. ChaptersButton.prototype.controlText_ = 'Chapters';
  11767. _component2['default'].registerComponent('ChaptersButton', ChaptersButton);
  11768. exports['default'] = ChaptersButton;
  11769. },{"../../component.js":47,"../../utils/to-title-case.js":133,"./chapters-track-menu-item.js":68,"./text-track-button.js":72}],68:[function(require,module,exports){
  11770. 'use strict';
  11771. exports.__esModule = true;
  11772. var _menuItem = require('../../menu/menu-item.js');
  11773. var _menuItem2 = _interopRequireDefault(_menuItem);
  11774. var _component = require('../../component.js');
  11775. var _component2 = _interopRequireDefault(_component);
  11776. var _fn = require('../../utils/fn.js');
  11777. var Fn = _interopRequireWildcard(_fn);
  11778. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  11779. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11780. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11781. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11782. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11783. * @file chapters-track-menu-item.js
  11784. */
  11785. /**
  11786. * The chapter track menu item
  11787. *
  11788. * @extends MenuItem
  11789. */
  11790. var ChaptersTrackMenuItem = function (_MenuItem) {
  11791. _inherits(ChaptersTrackMenuItem, _MenuItem);
  11792. /**
  11793. * Creates an instance of this class.
  11794. *
  11795. * @param {Player} player
  11796. * The `Player` that this class should be attached to.
  11797. *
  11798. * @param {Object} [options]
  11799. * The key/value store of player options.
  11800. */
  11801. function ChaptersTrackMenuItem(player, options) {
  11802. _classCallCheck(this, ChaptersTrackMenuItem);
  11803. var track = options.track;
  11804. var cue = options.cue;
  11805. var currentTime = player.currentTime();
  11806. // Modify options for parent MenuItem class's init.
  11807. options.selectable = true;
  11808. options.label = cue.text;
  11809. options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
  11810. var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  11811. _this.track = track;
  11812. _this.cue = cue;
  11813. track.addEventListener('cuechange', Fn.bind(_this, _this.update));
  11814. return _this;
  11815. }
  11816. /**
  11817. * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
  11818. * {@link ClickableComponent} for more detailed information on what a click can be.
  11819. *
  11820. * @param {EventTarget~Event} [event]
  11821. * The `keydown`, `tap`, or `click` event that caused this function to be
  11822. * called.
  11823. *
  11824. * @listens tap
  11825. * @listens click
  11826. */
  11827. ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
  11828. _MenuItem.prototype.handleClick.call(this);
  11829. this.player_.currentTime(this.cue.startTime);
  11830. this.update(this.cue.startTime);
  11831. };
  11832. /**
  11833. * Update chapter menu item
  11834. *
  11835. * @param {EventTarget~Event} [event]
  11836. * The `cuechange` event that caused this function to run.
  11837. *
  11838. * @listens TextTrack#cuechange
  11839. */
  11840. ChaptersTrackMenuItem.prototype.update = function update(event) {
  11841. var cue = this.cue;
  11842. var currentTime = this.player_.currentTime();
  11843. // vjs.log(currentTime, cue.startTime);
  11844. this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
  11845. };
  11846. return ChaptersTrackMenuItem;
  11847. }(_menuItem2['default']);
  11848. _component2['default'].registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
  11849. exports['default'] = ChaptersTrackMenuItem;
  11850. },{"../../component.js":47,"../../menu/menu-item.js":90,"../../utils/fn.js":125}],69:[function(require,module,exports){
  11851. 'use strict';
  11852. exports.__esModule = true;
  11853. var _textTrackButton = require('./text-track-button.js');
  11854. var _textTrackButton2 = _interopRequireDefault(_textTrackButton);
  11855. var _component = require('../../component.js');
  11856. var _component2 = _interopRequireDefault(_component);
  11857. var _fn = require('../../utils/fn.js');
  11858. var Fn = _interopRequireWildcard(_fn);
  11859. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  11860. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11861. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11862. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11863. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11864. * @file descriptions-button.js
  11865. */
  11866. /**
  11867. * The button component for toggling and selecting descriptions
  11868. *
  11869. * @extends TextTrackButton
  11870. */
  11871. var DescriptionsButton = function (_TextTrackButton) {
  11872. _inherits(DescriptionsButton, _TextTrackButton);
  11873. /**
  11874. * Creates an instance of this class.
  11875. *
  11876. * @param {Player} player
  11877. * The `Player` that this class should be attached to.
  11878. *
  11879. * @param {Object} [options]
  11880. * The key/value store of player options.
  11881. *
  11882. * @param {Component~ReadyCallback} [ready]
  11883. * The function to call when this component is ready.
  11884. */
  11885. function DescriptionsButton(player, options, ready) {
  11886. _classCallCheck(this, DescriptionsButton);
  11887. var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  11888. _this.el_.setAttribute('aria-label', 'Descriptions Menu');
  11889. var tracks = player.textTracks();
  11890. if (tracks) {
  11891. var changeHandler = Fn.bind(_this, _this.handleTracksChange);
  11892. tracks.addEventListener('change', changeHandler);
  11893. _this.on('dispose', function () {
  11894. tracks.removeEventListener('change', changeHandler);
  11895. });
  11896. }
  11897. return _this;
  11898. }
  11899. /**
  11900. * Handle text track change
  11901. *
  11902. * @param {EventTarget~Event} event
  11903. * The event that caused this function to run
  11904. *
  11905. * @listens TextTrackList#change
  11906. */
  11907. DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
  11908. var tracks = this.player().textTracks();
  11909. var disabled = false;
  11910. // Check whether a track of a different kind is showing
  11911. for (var i = 0, l = tracks.length; i < l; i++) {
  11912. var track = tracks[i];
  11913. if (track.kind !== this.kind_ && track.mode === 'showing') {
  11914. disabled = true;
  11915. break;
  11916. }
  11917. }
  11918. // If another track is showing, disable this menu button
  11919. if (disabled) {
  11920. this.disable();
  11921. } else {
  11922. this.enable();
  11923. }
  11924. };
  11925. /**
  11926. * Builds the default DOM `className`.
  11927. *
  11928. * @return {string}
  11929. * The DOM `className` for this object.
  11930. */
  11931. DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
  11932. return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  11933. };
  11934. return DescriptionsButton;
  11935. }(_textTrackButton2['default']);
  11936. /**
  11937. * `kind` of TextTrack to look for to associate it with this menu.
  11938. *
  11939. * @type {string}
  11940. * @private
  11941. */
  11942. DescriptionsButton.prototype.kind_ = 'descriptions';
  11943. /**
  11944. * The text that should display over the `DescriptionsButton`s controls. Added for localization.
  11945. *
  11946. * @type {string}
  11947. * @private
  11948. */
  11949. DescriptionsButton.prototype.controlText_ = 'Descriptions';
  11950. _component2['default'].registerComponent('DescriptionsButton', DescriptionsButton);
  11951. exports['default'] = DescriptionsButton;
  11952. },{"../../component.js":47,"../../utils/fn.js":125,"./text-track-button.js":72}],70:[function(require,module,exports){
  11953. 'use strict';
  11954. exports.__esModule = true;
  11955. var _textTrackMenuItem = require('./text-track-menu-item.js');
  11956. var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem);
  11957. var _component = require('../../component.js');
  11958. var _component2 = _interopRequireDefault(_component);
  11959. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  11960. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  11961. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  11962. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  11963. * @file off-text-track-menu-item.js
  11964. */
  11965. /**
  11966. * A special menu item for turning of a specific type of text track
  11967. *
  11968. * @extends TextTrackMenuItem
  11969. */
  11970. var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
  11971. _inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
  11972. /**
  11973. * Creates an instance of this class.
  11974. *
  11975. * @param {Player} player
  11976. * The `Player` that this class should be attached to.
  11977. *
  11978. * @param {Object} [options]
  11979. * The key/value store of player options.
  11980. */
  11981. function OffTextTrackMenuItem(player, options) {
  11982. _classCallCheck(this, OffTextTrackMenuItem);
  11983. // Create pseudo track info
  11984. // Requires options['kind']
  11985. options.track = {
  11986. player: player,
  11987. kind: options.kind,
  11988. label: options.kind + ' off',
  11989. 'default': false,
  11990. mode: 'disabled'
  11991. };
  11992. // MenuItem is selectable
  11993. options.selectable = true;
  11994. var _this = _possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
  11995. _this.selected(true);
  11996. return _this;
  11997. }
  11998. /**
  11999. * Handle text track change
  12000. *
  12001. * @param {EventTarget~Event} event
  12002. * The event that caused this function to run
  12003. */
  12004. OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  12005. var tracks = this.player().textTracks();
  12006. var selected = true;
  12007. for (var i = 0, l = tracks.length; i < l; i++) {
  12008. var track = tracks[i];
  12009. if (track.kind === this.track.kind && track.mode === 'showing') {
  12010. selected = false;
  12011. break;
  12012. }
  12013. }
  12014. this.selected(selected);
  12015. };
  12016. return OffTextTrackMenuItem;
  12017. }(_textTrackMenuItem2['default']);
  12018. _component2['default'].registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
  12019. exports['default'] = OffTextTrackMenuItem;
  12020. },{"../../component.js":47,"./text-track-menu-item.js":73}],71:[function(require,module,exports){
  12021. 'use strict';
  12022. exports.__esModule = true;
  12023. var _textTrackButton = require('./text-track-button.js');
  12024. var _textTrackButton2 = _interopRequireDefault(_textTrackButton);
  12025. var _component = require('../../component.js');
  12026. var _component2 = _interopRequireDefault(_component);
  12027. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12028. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12029. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12030. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12031. * @file subtitles-button.js
  12032. */
  12033. /**
  12034. * The button component for toggling and selecting subtitles
  12035. *
  12036. * @extends TextTrackButton
  12037. */
  12038. var SubtitlesButton = function (_TextTrackButton) {
  12039. _inherits(SubtitlesButton, _TextTrackButton);
  12040. /**
  12041. * Creates an instance of this class.
  12042. *
  12043. * @param {Player} player
  12044. * The `Player` that this class should be attached to.
  12045. *
  12046. * @param {Object} [options]
  12047. * The key/value store of player options.
  12048. *
  12049. * @param {Component~ReadyCallback} [ready]
  12050. * The function to call when this component is ready.
  12051. */
  12052. function SubtitlesButton(player, options, ready) {
  12053. _classCallCheck(this, SubtitlesButton);
  12054. var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  12055. _this.el_.setAttribute('aria-label', 'Subtitles Menu');
  12056. return _this;
  12057. }
  12058. /**
  12059. * Builds the default DOM `className`.
  12060. *
  12061. * @return {string}
  12062. * The DOM `className` for this object.
  12063. */
  12064. SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
  12065. return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  12066. };
  12067. return SubtitlesButton;
  12068. }(_textTrackButton2['default']);
  12069. /**
  12070. * `kind` of TextTrack to look for to associate it with this menu.
  12071. *
  12072. * @type {string}
  12073. * @private
  12074. */
  12075. SubtitlesButton.prototype.kind_ = 'subtitles';
  12076. /**
  12077. * The text that should display over the `SubtitlesButton`s controls. Added for localization.
  12078. *
  12079. * @type {string}
  12080. * @private
  12081. */
  12082. SubtitlesButton.prototype.controlText_ = 'Subtitles';
  12083. _component2['default'].registerComponent('SubtitlesButton', SubtitlesButton);
  12084. exports['default'] = SubtitlesButton;
  12085. },{"../../component.js":47,"./text-track-button.js":72}],72:[function(require,module,exports){
  12086. 'use strict';
  12087. exports.__esModule = true;
  12088. var _trackButton = require('../track-button.js');
  12089. var _trackButton2 = _interopRequireDefault(_trackButton);
  12090. var _component = require('../../component.js');
  12091. var _component2 = _interopRequireDefault(_component);
  12092. var _textTrackMenuItem = require('./text-track-menu-item.js');
  12093. var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem);
  12094. var _offTextTrackMenuItem = require('./off-text-track-menu-item.js');
  12095. var _offTextTrackMenuItem2 = _interopRequireDefault(_offTextTrackMenuItem);
  12096. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12097. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12098. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12099. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12100. * @file text-track-button.js
  12101. */
  12102. /**
  12103. * The base class for buttons that toggle specific text track types (e.g. subtitles)
  12104. *
  12105. * @extends MenuButton
  12106. */
  12107. var TextTrackButton = function (_TrackButton) {
  12108. _inherits(TextTrackButton, _TrackButton);
  12109. /**
  12110. * Creates an instance of this class.
  12111. *
  12112. * @param {Player} player
  12113. * The `Player` that this class should be attached to.
  12114. *
  12115. * @param {Object} [options={}]
  12116. * The key/value store of player options.
  12117. */
  12118. function TextTrackButton(player) {
  12119. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  12120. _classCallCheck(this, TextTrackButton);
  12121. options.tracks = player.textTracks();
  12122. return _possibleConstructorReturn(this, _TrackButton.call(this, player, options));
  12123. }
  12124. /**
  12125. * Create a menu item for each text track
  12126. *
  12127. * @param {TextTrackMenuItem[]} [items=[]]
  12128. * Existing array of items to use during creation
  12129. *
  12130. * @return {TextTrackMenuItem[]}
  12131. * Array of menu items that were created
  12132. */
  12133. TextTrackButton.prototype.createItems = function createItems() {
  12134. var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  12135. // Add an OFF menu item to turn all tracks off
  12136. items.push(new _offTextTrackMenuItem2['default'](this.player_, { kind: this.kind_ }));
  12137. this.hideThreshold_ += 1;
  12138. var tracks = this.player_.textTracks();
  12139. if (!tracks) {
  12140. return items;
  12141. }
  12142. for (var i = 0; i < tracks.length; i++) {
  12143. var track = tracks[i];
  12144. // only add tracks that are of the appropriate kind and have a label
  12145. if (track.kind === this.kind_) {
  12146. items.push(new _textTrackMenuItem2['default'](this.player_, {
  12147. track: track,
  12148. // MenuItem is selectable
  12149. selectable: true
  12150. }));
  12151. }
  12152. }
  12153. return items;
  12154. };
  12155. return TextTrackButton;
  12156. }(_trackButton2['default']);
  12157. _component2['default'].registerComponent('TextTrackButton', TextTrackButton);
  12158. exports['default'] = TextTrackButton;
  12159. },{"../../component.js":47,"../track-button.js":78,"./off-text-track-menu-item.js":70,"./text-track-menu-item.js":73}],73:[function(require,module,exports){
  12160. 'use strict';
  12161. exports.__esModule = true;
  12162. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  12163. var _menuItem = require('../../menu/menu-item.js');
  12164. var _menuItem2 = _interopRequireDefault(_menuItem);
  12165. var _component = require('../../component.js');
  12166. var _component2 = _interopRequireDefault(_component);
  12167. var _fn = require('../../utils/fn.js');
  12168. var Fn = _interopRequireWildcard(_fn);
  12169. var _window = require('global/window');
  12170. var _window2 = _interopRequireDefault(_window);
  12171. var _document = require('global/document');
  12172. var _document2 = _interopRequireDefault(_document);
  12173. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12174. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12175. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12176. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12177. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12178. * @file text-track-menu-item.js
  12179. */
  12180. /**
  12181. * The specific menu item type for selecting a language within a text track kind
  12182. *
  12183. * @extends MenuItem
  12184. */
  12185. var TextTrackMenuItem = function (_MenuItem) {
  12186. _inherits(TextTrackMenuItem, _MenuItem);
  12187. /**
  12188. * Creates an instance of this class.
  12189. *
  12190. * @param {Player} player
  12191. * The `Player` that this class should be attached to.
  12192. *
  12193. * @param {Object} [options]
  12194. * The key/value store of player options.
  12195. */
  12196. function TextTrackMenuItem(player, options) {
  12197. _classCallCheck(this, TextTrackMenuItem);
  12198. var track = options.track;
  12199. var tracks = player.textTracks();
  12200. // Modify options for parent MenuItem class's init.
  12201. options.label = track.label || track.language || 'Unknown';
  12202. options.selected = track['default'] || track.mode === 'showing';
  12203. var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  12204. _this.track = track;
  12205. if (tracks) {
  12206. var changeHandler = Fn.bind(_this, _this.handleTracksChange);
  12207. player.on(['loadstart', 'texttrackchange'], changeHandler);
  12208. tracks.addEventListener('change', changeHandler);
  12209. _this.on('dispose', function () {
  12210. tracks.removeEventListener('change', changeHandler);
  12211. });
  12212. }
  12213. // iOS7 doesn't dispatch change events to TextTrackLists when an
  12214. // associated track's mode changes. Without something like
  12215. // Object.observe() (also not present on iOS7), it's not
  12216. // possible to detect changes to the mode attribute and polyfill
  12217. // the change event. As a poor substitute, we manually dispatch
  12218. // change events whenever the controls modify the mode.
  12219. if (tracks && tracks.onchange === undefined) {
  12220. var event = void 0;
  12221. _this.on(['tap', 'click'], function () {
  12222. if (_typeof(_window2['default'].Event) !== 'object') {
  12223. // Android 2.3 throws an Illegal Constructor error for window.Event
  12224. try {
  12225. event = new _window2['default'].Event('change');
  12226. } catch (err) {
  12227. // continue regardless of error
  12228. }
  12229. }
  12230. if (!event) {
  12231. event = _document2['default'].createEvent('Event');
  12232. event.initEvent('change', true, true);
  12233. }
  12234. tracks.dispatchEvent(event);
  12235. });
  12236. }
  12237. return _this;
  12238. }
  12239. /**
  12240. * This gets called when an `TextTrackMenuItem` is "clicked". See
  12241. * {@link ClickableComponent} for more detailed information on what a click can be.
  12242. *
  12243. * @param {EventTarget~Event} event
  12244. * The `keydown`, `tap`, or `click` event that caused this function to be
  12245. * called.
  12246. *
  12247. * @listens tap
  12248. * @listens click
  12249. */
  12250. TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
  12251. var kind = this.track.kind;
  12252. var tracks = this.player_.textTracks();
  12253. _MenuItem.prototype.handleClick.call(this, event);
  12254. if (!tracks) {
  12255. return;
  12256. }
  12257. for (var i = 0; i < tracks.length; i++) {
  12258. var track = tracks[i];
  12259. if (track.kind !== kind) {
  12260. continue;
  12261. }
  12262. if (track === this.track) {
  12263. if (track.mode !== 'showing') {
  12264. track.mode = 'showing';
  12265. }
  12266. } else if (track.mode !== 'disabled') {
  12267. track.mode = 'disabled';
  12268. }
  12269. }
  12270. };
  12271. /**
  12272. * Handle text track list change
  12273. *
  12274. * @param {EventTarget~Event} event
  12275. * The `change` event that caused this function to be called.
  12276. *
  12277. * @listens TextTrackList#change
  12278. */
  12279. TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  12280. this.selected(this.track.mode === 'showing');
  12281. };
  12282. return TextTrackMenuItem;
  12283. }(_menuItem2['default']);
  12284. _component2['default'].registerComponent('TextTrackMenuItem', TextTrackMenuItem);
  12285. exports['default'] = TextTrackMenuItem;
  12286. },{"../../component.js":47,"../../menu/menu-item.js":90,"../../utils/fn.js":125,"global/document":136,"global/window":137}],74:[function(require,module,exports){
  12287. 'use strict';
  12288. exports.__esModule = true;
  12289. var _component = require('../../component.js');
  12290. var _component2 = _interopRequireDefault(_component);
  12291. var _dom = require('../../utils/dom.js');
  12292. var Dom = _interopRequireWildcard(_dom);
  12293. var _formatTime = require('../../utils/format-time.js');
  12294. var _formatTime2 = _interopRequireDefault(_formatTime);
  12295. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12296. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12297. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12298. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12299. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12300. * @file current-time-display.js
  12301. */
  12302. /**
  12303. * Displays the current time
  12304. *
  12305. * @extends Component
  12306. */
  12307. var CurrentTimeDisplay = function (_Component) {
  12308. _inherits(CurrentTimeDisplay, _Component);
  12309. /**
  12310. * Creates an instance of this class.
  12311. *
  12312. * @param {Player} player
  12313. * The `Player` that this class should be attached to.
  12314. *
  12315. * @param {Object} [options]
  12316. * The key/value store of player options.
  12317. */
  12318. function CurrentTimeDisplay(player, options) {
  12319. _classCallCheck(this, CurrentTimeDisplay);
  12320. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  12321. _this.on(player, 'timeupdate', _this.updateContent);
  12322. return _this;
  12323. }
  12324. /**
  12325. * Create the `Component`'s DOM element
  12326. *
  12327. * @return {Element}
  12328. * The element that was created.
  12329. */
  12330. CurrentTimeDisplay.prototype.createEl = function createEl() {
  12331. var el = _Component.prototype.createEl.call(this, 'div', {
  12332. className: 'vjs-current-time vjs-time-control vjs-control'
  12333. });
  12334. this.contentEl_ = Dom.createEl('div', {
  12335. className: 'vjs-current-time-display',
  12336. // label the current time for screen reader users
  12337. innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00'
  12338. }, {
  12339. // tell screen readers not to automatically read the time as it changes
  12340. 'aria-live': 'off'
  12341. });
  12342. el.appendChild(this.contentEl_);
  12343. return el;
  12344. };
  12345. /**
  12346. * Update current time display
  12347. *
  12348. * @param {EventTarget~Event} [event]
  12349. * The `timeupdate` event that caused this function to run.
  12350. *
  12351. * @listens Player#timeupdate
  12352. */
  12353. CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
  12354. // Allows for smooth scrubbing, when player can't keep up.
  12355. var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  12356. var localizedText = this.localize('Current Time');
  12357. var formattedTime = (0, _formatTime2['default'])(time, this.player_.duration());
  12358. if (formattedTime !== this.formattedTime_) {
  12359. this.formattedTime_ = formattedTime;
  12360. this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> ' + formattedTime;
  12361. }
  12362. };
  12363. return CurrentTimeDisplay;
  12364. }(_component2['default']);
  12365. _component2['default'].registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
  12366. exports['default'] = CurrentTimeDisplay;
  12367. },{"../../component.js":47,"../../utils/dom.js":123,"../../utils/format-time.js":126}],75:[function(require,module,exports){
  12368. 'use strict';
  12369. exports.__esModule = true;
  12370. var _component = require('../../component.js');
  12371. var _component2 = _interopRequireDefault(_component);
  12372. var _dom = require('../../utils/dom.js');
  12373. var Dom = _interopRequireWildcard(_dom);
  12374. var _formatTime = require('../../utils/format-time.js');
  12375. var _formatTime2 = _interopRequireDefault(_formatTime);
  12376. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12377. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12378. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12379. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12380. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12381. * @file duration-display.js
  12382. */
  12383. /**
  12384. * Displays the duration
  12385. *
  12386. * @extends Component
  12387. */
  12388. var DurationDisplay = function (_Component) {
  12389. _inherits(DurationDisplay, _Component);
  12390. /**
  12391. * Creates an instance of this class.
  12392. *
  12393. * @param {Player} player
  12394. * The `Player` that this class should be attached to.
  12395. *
  12396. * @param {Object} [options]
  12397. * The key/value store of player options.
  12398. */
  12399. function DurationDisplay(player, options) {
  12400. _classCallCheck(this, DurationDisplay);
  12401. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  12402. _this.on(player, 'durationchange', _this.updateContent);
  12403. // Also listen for timeupdate and loadedmetadata because removing those
  12404. // listeners could have broken dependent applications/libraries. These
  12405. // can likely be removed for 6.0.
  12406. _this.on(player, 'timeupdate', _this.updateContent);
  12407. _this.on(player, 'loadedmetadata', _this.updateContent);
  12408. return _this;
  12409. }
  12410. /**
  12411. * Create the `Component`'s DOM element
  12412. *
  12413. * @return {Element}
  12414. * The element that was created.
  12415. */
  12416. DurationDisplay.prototype.createEl = function createEl() {
  12417. var el = _Component.prototype.createEl.call(this, 'div', {
  12418. className: 'vjs-duration vjs-time-control vjs-control'
  12419. });
  12420. this.contentEl_ = Dom.createEl('div', {
  12421. className: 'vjs-duration-display',
  12422. // label the duration time for screen reader users
  12423. innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> 0:00'
  12424. }, {
  12425. // tell screen readers not to automatically read the time as it changes
  12426. 'aria-live': 'off'
  12427. });
  12428. el.appendChild(this.contentEl_);
  12429. return el;
  12430. };
  12431. /**
  12432. * Update duration time display.
  12433. *
  12434. * @param {EventTarget~Event} [event]
  12435. * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
  12436. * this function to be called.
  12437. *
  12438. * @listens Player#durationchange
  12439. * @listens Player#timeupdate
  12440. * @listens Player#loadedmetadata
  12441. */
  12442. DurationDisplay.prototype.updateContent = function updateContent(event) {
  12443. var duration = this.player_.duration();
  12444. if (duration && this.duration_ !== duration) {
  12445. this.duration_ = duration;
  12446. var localizedText = this.localize('Duration Time');
  12447. var formattedTime = (0, _formatTime2['default'])(duration);
  12448. // label the duration time for screen reader users
  12449. this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> ' + formattedTime;
  12450. }
  12451. };
  12452. return DurationDisplay;
  12453. }(_component2['default']);
  12454. _component2['default'].registerComponent('DurationDisplay', DurationDisplay);
  12455. exports['default'] = DurationDisplay;
  12456. },{"../../component.js":47,"../../utils/dom.js":123,"../../utils/format-time.js":126}],76:[function(require,module,exports){
  12457. 'use strict';
  12458. exports.__esModule = true;
  12459. var _component = require('../../component.js');
  12460. var _component2 = _interopRequireDefault(_component);
  12461. var _dom = require('../../utils/dom.js');
  12462. var Dom = _interopRequireWildcard(_dom);
  12463. var _formatTime = require('../../utils/format-time.js');
  12464. var _formatTime2 = _interopRequireDefault(_formatTime);
  12465. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12466. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12467. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12468. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12469. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12470. * @file remaining-time-display.js
  12471. */
  12472. /**
  12473. * Displays the time left in the video
  12474. *
  12475. * @extends Component
  12476. */
  12477. var RemainingTimeDisplay = function (_Component) {
  12478. _inherits(RemainingTimeDisplay, _Component);
  12479. /**
  12480. * Creates an instance of this class.
  12481. *
  12482. * @param {Player} player
  12483. * The `Player` that this class should be attached to.
  12484. *
  12485. * @param {Object} [options]
  12486. * The key/value store of player options.
  12487. */
  12488. function RemainingTimeDisplay(player, options) {
  12489. _classCallCheck(this, RemainingTimeDisplay);
  12490. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  12491. _this.on(player, 'timeupdate', _this.updateContent);
  12492. _this.on(player, 'durationchange', _this.updateContent);
  12493. return _this;
  12494. }
  12495. /**
  12496. * Create the `Component`'s DOM element
  12497. *
  12498. * @return {Element}
  12499. * The element that was created.
  12500. */
  12501. RemainingTimeDisplay.prototype.createEl = function createEl() {
  12502. var el = _Component.prototype.createEl.call(this, 'div', {
  12503. className: 'vjs-remaining-time vjs-time-control vjs-control'
  12504. });
  12505. this.contentEl_ = Dom.createEl('div', {
  12506. className: 'vjs-remaining-time-display',
  12507. // label the remaining time for screen reader users
  12508. innerHTML: '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> -0:00'
  12509. }, {
  12510. // tell screen readers not to automatically read the time as it changes
  12511. 'aria-live': 'off'
  12512. });
  12513. el.appendChild(this.contentEl_);
  12514. return el;
  12515. };
  12516. /**
  12517. * Update remaining time display.
  12518. *
  12519. * @param {EventTarget~Event} [event]
  12520. * The `timeupdate` or `durationchange` event that caused this to run.
  12521. *
  12522. * @listens Player#timeupdate
  12523. * @listens Player#durationchange
  12524. */
  12525. RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
  12526. if (this.player_.duration()) {
  12527. var localizedText = this.localize('Remaining Time');
  12528. var formattedTime = (0, _formatTime2['default'])(this.player_.remainingTime());
  12529. if (formattedTime !== this.formattedTime_) {
  12530. this.formattedTime_ = formattedTime;
  12531. this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> -' + formattedTime;
  12532. }
  12533. }
  12534. // Allows for smooth scrubbing, when player can't keep up.
  12535. // var time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
  12536. // this.contentEl_.innerHTML = vjs.formatTime(time, this.player_.duration());
  12537. };
  12538. return RemainingTimeDisplay;
  12539. }(_component2['default']);
  12540. _component2['default'].registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
  12541. exports['default'] = RemainingTimeDisplay;
  12542. },{"../../component.js":47,"../../utils/dom.js":123,"../../utils/format-time.js":126}],77:[function(require,module,exports){
  12543. 'use strict';
  12544. exports.__esModule = true;
  12545. var _component = require('../../component.js');
  12546. var _component2 = _interopRequireDefault(_component);
  12547. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12548. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12549. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12550. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12551. * @file time-divider.js
  12552. */
  12553. /**
  12554. * The separator between the current time and duration.
  12555. * Can be hidden if it's not needed in the design.
  12556. *
  12557. * @extends Component
  12558. */
  12559. var TimeDivider = function (_Component) {
  12560. _inherits(TimeDivider, _Component);
  12561. function TimeDivider() {
  12562. _classCallCheck(this, TimeDivider);
  12563. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  12564. }
  12565. /**
  12566. * Create the component's DOM element
  12567. *
  12568. * @return {Element}
  12569. * The element that was created.
  12570. */
  12571. TimeDivider.prototype.createEl = function createEl() {
  12572. return _Component.prototype.createEl.call(this, 'div', {
  12573. className: 'vjs-time-control vjs-time-divider',
  12574. innerHTML: '<div><span>/</span></div>'
  12575. });
  12576. };
  12577. return TimeDivider;
  12578. }(_component2['default']);
  12579. _component2['default'].registerComponent('TimeDivider', TimeDivider);
  12580. exports['default'] = TimeDivider;
  12581. },{"../../component.js":47}],78:[function(require,module,exports){
  12582. 'use strict';
  12583. exports.__esModule = true;
  12584. var _menuButton = require('../menu/menu-button.js');
  12585. var _menuButton2 = _interopRequireDefault(_menuButton);
  12586. var _component = require('../component.js');
  12587. var _component2 = _interopRequireDefault(_component);
  12588. var _fn = require('../utils/fn.js');
  12589. var Fn = _interopRequireWildcard(_fn);
  12590. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12591. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12592. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12593. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12594. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12595. * @file track-button.js
  12596. */
  12597. /**
  12598. * The base class for buttons that toggle specific track types (e.g. subtitles).
  12599. *
  12600. * @extends MenuButton
  12601. */
  12602. var TrackButton = function (_MenuButton) {
  12603. _inherits(TrackButton, _MenuButton);
  12604. /**
  12605. * Creates an instance of this class.
  12606. *
  12607. * @param {Player} player
  12608. * The `Player` that this class should be attached to.
  12609. *
  12610. * @param {Object} [options]
  12611. * The key/value store of player options.
  12612. */
  12613. function TrackButton(player, options) {
  12614. _classCallCheck(this, TrackButton);
  12615. var tracks = options.tracks;
  12616. var _this = _possibleConstructorReturn(this, _MenuButton.call(this, player, options));
  12617. if (_this.items.length <= 1) {
  12618. _this.hide();
  12619. }
  12620. if (!tracks) {
  12621. return _possibleConstructorReturn(_this);
  12622. }
  12623. var updateHandler = Fn.bind(_this, _this.update);
  12624. tracks.addEventListener('removetrack', updateHandler);
  12625. tracks.addEventListener('addtrack', updateHandler);
  12626. _this.player_.on('dispose', function () {
  12627. tracks.removeEventListener('removetrack', updateHandler);
  12628. tracks.removeEventListener('addtrack', updateHandler);
  12629. });
  12630. return _this;
  12631. }
  12632. return TrackButton;
  12633. }(_menuButton2['default']);
  12634. _component2['default'].registerComponent('TrackButton', TrackButton);
  12635. exports['default'] = TrackButton;
  12636. },{"../component.js":47,"../menu/menu-button.js":89,"../utils/fn.js":125}],79:[function(require,module,exports){
  12637. 'use strict';
  12638. exports.__esModule = true;
  12639. var _slider = require('../../slider/slider.js');
  12640. var _slider2 = _interopRequireDefault(_slider);
  12641. var _component = require('../../component.js');
  12642. var _component2 = _interopRequireDefault(_component);
  12643. var _fn = require('../../utils/fn.js');
  12644. var Fn = _interopRequireWildcard(_fn);
  12645. require('./volume-level.js');
  12646. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12647. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12648. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12649. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12650. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12651. * @file volume-bar.js
  12652. */
  12653. // Required children
  12654. /**
  12655. * The bar that contains the volume level and can be clicked on to adjust the level
  12656. *
  12657. * @extends Slider
  12658. */
  12659. var VolumeBar = function (_Slider) {
  12660. _inherits(VolumeBar, _Slider);
  12661. /**
  12662. * Creates an instance of this class.
  12663. *
  12664. * @param {Player} player
  12665. * The `Player` that this class should be attached to.
  12666. *
  12667. * @param {Object} [options]
  12668. * The key/value store of player options.
  12669. */
  12670. function VolumeBar(player, options) {
  12671. _classCallCheck(this, VolumeBar);
  12672. var _this = _possibleConstructorReturn(this, _Slider.call(this, player, options));
  12673. _this.on(player, 'volumechange', _this.updateARIAAttributes);
  12674. player.ready(Fn.bind(_this, _this.updateARIAAttributes));
  12675. return _this;
  12676. }
  12677. /**
  12678. * Create the `Component`'s DOM element
  12679. *
  12680. * @return {Element}
  12681. * The element that was created.
  12682. */
  12683. VolumeBar.prototype.createEl = function createEl() {
  12684. return _Slider.prototype.createEl.call(this, 'div', {
  12685. className: 'vjs-volume-bar vjs-slider-bar'
  12686. }, {
  12687. 'aria-label': 'volume level'
  12688. });
  12689. };
  12690. /**
  12691. * Handle movement events on the {@link VolumeMenuButton}.
  12692. *
  12693. * @param {EventTarget~Event} event
  12694. * The event that caused this function to run.
  12695. *
  12696. * @listens mousemove
  12697. */
  12698. VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
  12699. this.checkMuted();
  12700. this.player_.volume(this.calculateDistance(event));
  12701. };
  12702. /**
  12703. * If the player is muted unmute it.
  12704. */
  12705. VolumeBar.prototype.checkMuted = function checkMuted() {
  12706. if (this.player_.muted()) {
  12707. this.player_.muted(false);
  12708. }
  12709. };
  12710. /**
  12711. * Get percent of volume level
  12712. *
  12713. * @return {number}
  12714. * Volume level percent as a decimal number.
  12715. */
  12716. VolumeBar.prototype.getPercent = function getPercent() {
  12717. if (this.player_.muted()) {
  12718. return 0;
  12719. }
  12720. return this.player_.volume();
  12721. };
  12722. /**
  12723. * Increase volume level for keyboard users
  12724. */
  12725. VolumeBar.prototype.stepForward = function stepForward() {
  12726. this.checkMuted();
  12727. this.player_.volume(this.player_.volume() + 0.1);
  12728. };
  12729. /**
  12730. * Decrease volume level for keyboard users
  12731. */
  12732. VolumeBar.prototype.stepBack = function stepBack() {
  12733. this.checkMuted();
  12734. this.player_.volume(this.player_.volume() - 0.1);
  12735. };
  12736. /**
  12737. * Update ARIA accessibility attributes
  12738. *
  12739. * @param {EventTarget~Event} [event]
  12740. * The `volumechange` event that caused this function to run.
  12741. *
  12742. * @listens Player#volumechange
  12743. */
  12744. VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
  12745. // Current value of volume bar as a percentage
  12746. var volume = (this.player_.volume() * 100).toFixed(2);
  12747. this.el_.setAttribute('aria-valuenow', volume);
  12748. this.el_.setAttribute('aria-valuetext', volume + '%');
  12749. };
  12750. return VolumeBar;
  12751. }(_slider2['default']);
  12752. /**
  12753. * Default options for the `VolumeBar`
  12754. *
  12755. * @type {Object}
  12756. * @private
  12757. */
  12758. VolumeBar.prototype.options_ = {
  12759. children: ['volumeLevel'],
  12760. barName: 'volumeLevel'
  12761. };
  12762. /**
  12763. * Call the update event for this Slider when this event happens on the player.
  12764. *
  12765. * @type {string}
  12766. */
  12767. VolumeBar.prototype.playerEvent = 'volumechange';
  12768. _component2['default'].registerComponent('VolumeBar', VolumeBar);
  12769. exports['default'] = VolumeBar;
  12770. },{"../../component.js":47,"../../slider/slider.js":99,"../../utils/fn.js":125,"./volume-level.js":81}],80:[function(require,module,exports){
  12771. 'use strict';
  12772. exports.__esModule = true;
  12773. var _component = require('../../component.js');
  12774. var _component2 = _interopRequireDefault(_component);
  12775. require('./volume-bar.js');
  12776. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12777. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12778. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12779. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12780. * @file volume-control.js
  12781. */
  12782. // Required children
  12783. /**
  12784. * The component for controlling the volume level
  12785. *
  12786. * @extends Component
  12787. */
  12788. var VolumeControl = function (_Component) {
  12789. _inherits(VolumeControl, _Component);
  12790. /**
  12791. * Creates an instance of this class.
  12792. *
  12793. * @param {Player} player
  12794. * The `Player` that this class should be attached to.
  12795. *
  12796. * @param {Object} [options={}]
  12797. * The key/value store of player options.
  12798. */
  12799. function VolumeControl(player, options) {
  12800. _classCallCheck(this, VolumeControl);
  12801. // hide volume controls when they're not supported by the current tech
  12802. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  12803. if (player.tech_ && player.tech_.featuresVolumeControl === false) {
  12804. _this.addClass('vjs-hidden');
  12805. }
  12806. _this.on(player, 'loadstart', function () {
  12807. if (player.tech_.featuresVolumeControl === false) {
  12808. this.addClass('vjs-hidden');
  12809. } else {
  12810. this.removeClass('vjs-hidden');
  12811. }
  12812. });
  12813. return _this;
  12814. }
  12815. /**
  12816. * Create the `Component`'s DOM element
  12817. *
  12818. * @return {Element}
  12819. * The element that was created.
  12820. */
  12821. VolumeControl.prototype.createEl = function createEl() {
  12822. return _Component.prototype.createEl.call(this, 'div', {
  12823. className: 'vjs-volume-control vjs-control'
  12824. });
  12825. };
  12826. return VolumeControl;
  12827. }(_component2['default']);
  12828. /**
  12829. * Default options for the `VolumeControl`
  12830. *
  12831. * @type {Object}
  12832. * @private
  12833. */
  12834. VolumeControl.prototype.options_ = {
  12835. children: ['volumeBar']
  12836. };
  12837. _component2['default'].registerComponent('VolumeControl', VolumeControl);
  12838. exports['default'] = VolumeControl;
  12839. },{"../../component.js":47,"./volume-bar.js":79}],81:[function(require,module,exports){
  12840. 'use strict';
  12841. exports.__esModule = true;
  12842. var _component = require('../../component.js');
  12843. var _component2 = _interopRequireDefault(_component);
  12844. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12845. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12846. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12847. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12848. * @file volume-level.js
  12849. */
  12850. /**
  12851. * Shows volume level
  12852. *
  12853. * @extends Component
  12854. */
  12855. var VolumeLevel = function (_Component) {
  12856. _inherits(VolumeLevel, _Component);
  12857. function VolumeLevel() {
  12858. _classCallCheck(this, VolumeLevel);
  12859. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  12860. }
  12861. /**
  12862. * Create the `Component`'s DOM element
  12863. *
  12864. * @return {Element}
  12865. * The element that was created.
  12866. */
  12867. VolumeLevel.prototype.createEl = function createEl() {
  12868. return _Component.prototype.createEl.call(this, 'div', {
  12869. className: 'vjs-volume-level',
  12870. innerHTML: '<span class="vjs-control-text"></span>'
  12871. });
  12872. };
  12873. return VolumeLevel;
  12874. }(_component2['default']);
  12875. _component2['default'].registerComponent('VolumeLevel', VolumeLevel);
  12876. exports['default'] = VolumeLevel;
  12877. },{"../../component.js":47}],82:[function(require,module,exports){
  12878. 'use strict';
  12879. exports.__esModule = true;
  12880. var _fn = require('../utils/fn.js');
  12881. var Fn = _interopRequireWildcard(_fn);
  12882. var _component = require('../component.js');
  12883. var _component2 = _interopRequireDefault(_component);
  12884. var _popup = require('../popup/popup.js');
  12885. var _popup2 = _interopRequireDefault(_popup);
  12886. var _popupButton = require('../popup/popup-button.js');
  12887. var _popupButton2 = _interopRequireDefault(_popupButton);
  12888. var _muteToggle = require('./mute-toggle.js');
  12889. var _muteToggle2 = _interopRequireDefault(_muteToggle);
  12890. var _volumeBar = require('./volume-control/volume-bar.js');
  12891. var _volumeBar2 = _interopRequireDefault(_volumeBar);
  12892. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  12893. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  12894. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  12895. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  12896. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  12897. * @file volume-menu-button.js
  12898. */
  12899. /**
  12900. * Button for volume popup
  12901. *
  12902. * @extends PopupButton
  12903. */
  12904. var VolumeMenuButton = function (_PopupButton) {
  12905. _inherits(VolumeMenuButton, _PopupButton);
  12906. /**
  12907. * Creates an instance of this class.
  12908. *
  12909. * @param {Player} player
  12910. * The `Player` that this class should be attached to.
  12911. *
  12912. * @param {Object} [options={}]
  12913. * The key/value store of player options.
  12914. */
  12915. function VolumeMenuButton(player) {
  12916. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  12917. _classCallCheck(this, VolumeMenuButton);
  12918. // Default to inline
  12919. if (options.inline === undefined) {
  12920. options.inline = true;
  12921. }
  12922. // If the vertical option isn't passed at all, default to true.
  12923. if (options.vertical === undefined) {
  12924. // If an inline volumeMenuButton is used, we should default to using
  12925. // a horizontal slider for obvious reasons.
  12926. if (options.inline) {
  12927. options.vertical = false;
  12928. } else {
  12929. options.vertical = true;
  12930. }
  12931. }
  12932. // The vertical option needs to be set on the volumeBar as well,
  12933. // since that will need to be passed along to the VolumeBar constructor
  12934. options.volumeBar = options.volumeBar || {};
  12935. options.volumeBar.vertical = !!options.vertical;
  12936. // Same listeners as MuteToggle
  12937. var _this = _possibleConstructorReturn(this, _PopupButton.call(this, player, options));
  12938. _this.on(player, 'volumechange', _this.volumeUpdate);
  12939. _this.on(player, 'loadstart', _this.volumeUpdate);
  12940. // hide mute toggle if the current tech doesn't support volume control
  12941. function updateVisibility() {
  12942. if (player.tech_ && player.tech_.featuresVolumeControl === false) {
  12943. this.addClass('vjs-hidden');
  12944. } else {
  12945. this.removeClass('vjs-hidden');
  12946. }
  12947. }
  12948. updateVisibility.call(_this);
  12949. _this.on(player, 'loadstart', updateVisibility);
  12950. _this.on(_this.volumeBar, ['slideractive', 'focus'], function () {
  12951. this.addClass('vjs-slider-active');
  12952. });
  12953. _this.on(_this.volumeBar, ['sliderinactive', 'blur'], function () {
  12954. this.removeClass('vjs-slider-active');
  12955. });
  12956. _this.on(_this.volumeBar, ['focus'], function () {
  12957. this.addClass('vjs-lock-showing');
  12958. });
  12959. _this.on(_this.volumeBar, ['blur'], function () {
  12960. this.removeClass('vjs-lock-showing');
  12961. });
  12962. return _this;
  12963. }
  12964. /**
  12965. * Builds the default DOM `className`.
  12966. *
  12967. * @return {string}
  12968. * The DOM `className` for this object.
  12969. */
  12970. VolumeMenuButton.prototype.buildCSSClass = function buildCSSClass() {
  12971. var orientationClass = '';
  12972. if (this.options_.vertical) {
  12973. orientationClass = 'vjs-volume-menu-button-vertical';
  12974. } else {
  12975. orientationClass = 'vjs-volume-menu-button-horizontal';
  12976. }
  12977. return 'vjs-volume-menu-button ' + _PopupButton.prototype.buildCSSClass.call(this) + ' ' + orientationClass;
  12978. };
  12979. /**
  12980. * Create the VolumeMenuButton popup
  12981. *
  12982. * @return {Popup}
  12983. * The popup that was created
  12984. */
  12985. VolumeMenuButton.prototype.createPopup = function createPopup() {
  12986. var popup = new _popup2['default'](this.player_, {
  12987. contentElType: 'div'
  12988. });
  12989. var vb = new _volumeBar2['default'](this.player_, this.options_.volumeBar);
  12990. popup.addChild(vb);
  12991. this.menuContent = popup;
  12992. this.volumeBar = vb;
  12993. this.attachVolumeBarEvents();
  12994. return popup;
  12995. };
  12996. /**
  12997. * This gets called when an `VolumeMenuButton` is "clicked". See
  12998. * {@link ClickableComponent} for more detailed information on what a click can be.
  12999. *
  13000. * @param {EventTarget~Event} [event]
  13001. * The `keydown`, `tap`, or `click` event that caused this function to be
  13002. * called.
  13003. *
  13004. * @listens tap
  13005. * @listens click
  13006. */
  13007. VolumeMenuButton.prototype.handleClick = function handleClick(event) {
  13008. _muteToggle2['default'].prototype.handleClick.call(this);
  13009. _PopupButton.prototype.handleClick.call(this);
  13010. };
  13011. /**
  13012. * Add events listeners to the created `VolumeBar`.
  13013. */
  13014. VolumeMenuButton.prototype.attachVolumeBarEvents = function attachVolumeBarEvents() {
  13015. this.menuContent.on(['mousedown', 'touchdown'], Fn.bind(this, this.handleMouseDown));
  13016. };
  13017. /**
  13018. * Handle the `mousedown` and `touchdown` events on the `VolumeBar`
  13019. *
  13020. * @param {EventTarget~Event} [event]
  13021. * The `mousedown` or `touchdown` event that caused this to run.
  13022. *
  13023. * @listens mousedown
  13024. * @listens touchdown
  13025. */
  13026. VolumeMenuButton.prototype.handleMouseDown = function handleMouseDown(event) {
  13027. this.on(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove));
  13028. this.on(this.el_.ownerDocument, ['mouseup', 'touchend'], this.handleMouseUp);
  13029. };
  13030. /**
  13031. * Handle the `mouseup` and `touchend` events on the `VolumeBar`
  13032. *
  13033. * @param {EventTarget~Event} [event]
  13034. * The `mouseup` or `touchend` event that caused this to run.
  13035. *
  13036. * @listens mouseup
  13037. * @listens touchend
  13038. */
  13039. VolumeMenuButton.prototype.handleMouseUp = function handleMouseUp(event) {
  13040. this.off(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove));
  13041. };
  13042. return VolumeMenuButton;
  13043. }(_popupButton2['default']);
  13044. /**
  13045. * @borrows MuteToggle#update as VolumeMenuButton#volumeUpdate
  13046. */
  13047. VolumeMenuButton.prototype.volumeUpdate = _muteToggle2['default'].prototype.update;
  13048. /**
  13049. * The text that should display over the `VolumeMenuButton`s controls. Added for localization.
  13050. *
  13051. * @type {string}
  13052. * @private
  13053. */
  13054. VolumeMenuButton.prototype.controlText_ = 'Mute';
  13055. _component2['default'].registerComponent('VolumeMenuButton', VolumeMenuButton);
  13056. exports['default'] = VolumeMenuButton;
  13057. },{"../component.js":47,"../popup/popup-button.js":95,"../popup/popup.js":96,"../utils/fn.js":125,"./mute-toggle.js":53,"./volume-control/volume-bar.js":79}],83:[function(require,module,exports){
  13058. 'use strict';
  13059. exports.__esModule = true;
  13060. var _component = require('./component');
  13061. var _component2 = _interopRequireDefault(_component);
  13062. var _modalDialog = require('./modal-dialog');
  13063. var _modalDialog2 = _interopRequireDefault(_modalDialog);
  13064. var _mergeOptions = require('./utils/merge-options');
  13065. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  13066. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13067. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  13068. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  13069. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  13070. * @file error-display.js
  13071. */
  13072. /**
  13073. * A display that indicates an error has occurred. This means that the video
  13074. * is unplayable.
  13075. *
  13076. * @extends ModalDialog
  13077. */
  13078. var ErrorDisplay = function (_ModalDialog) {
  13079. _inherits(ErrorDisplay, _ModalDialog);
  13080. /**
  13081. * Creates an instance of this class.
  13082. *
  13083. * @param {Player} player
  13084. * The `Player` that this class should be attached to.
  13085. *
  13086. * @param {Object} [options]
  13087. * The key/value store of player options.
  13088. */
  13089. function ErrorDisplay(player, options) {
  13090. _classCallCheck(this, ErrorDisplay);
  13091. var _this = _possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
  13092. _this.on(player, 'error', _this.open);
  13093. return _this;
  13094. }
  13095. /**
  13096. * Builds the default DOM `className`.
  13097. *
  13098. * @return {string}
  13099. * The DOM `className` for this object.
  13100. *
  13101. * @deprecated Since version 5.
  13102. */
  13103. ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
  13104. return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
  13105. };
  13106. /**
  13107. * Gets the localized error message based on the `Player`s error.
  13108. *
  13109. * @return {string}
  13110. * The `Player`s error message localized or an empty string.
  13111. */
  13112. ErrorDisplay.prototype.content = function content() {
  13113. var error = this.player().error();
  13114. return error ? this.localize(error.message) : '';
  13115. };
  13116. return ErrorDisplay;
  13117. }(_modalDialog2['default']);
  13118. /**
  13119. * The default options for an `ErrorDisplay`.
  13120. *
  13121. * @private
  13122. */
  13123. ErrorDisplay.prototype.options_ = (0, _mergeOptions2['default'])(_modalDialog2['default'].prototype.options_, {
  13124. pauseOnOpen: false,
  13125. fillAlways: true,
  13126. temporary: false,
  13127. uncloseable: true
  13128. });
  13129. _component2['default'].registerComponent('ErrorDisplay', ErrorDisplay);
  13130. exports['default'] = ErrorDisplay;
  13131. },{"./component":47,"./modal-dialog":92,"./utils/merge-options":129}],84:[function(require,module,exports){
  13132. 'use strict';
  13133. exports.__esModule = true;
  13134. var _events = require('./utils/events.js');
  13135. var Events = _interopRequireWildcard(_events);
  13136. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  13137. /**
  13138. * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
  13139. * adds shorthand functions that wrap around lengthy functions. For example:
  13140. * the `on` function is a wrapper around `addEventListener`.
  13141. *
  13142. * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
  13143. * @class EventTarget
  13144. */
  13145. var EventTarget = function EventTarget() {};
  13146. /**
  13147. * A Custom DOM event.
  13148. *
  13149. * @typedef {Object} EventTarget~Event
  13150. * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
  13151. */
  13152. /**
  13153. * All event listeners should follow the following format.
  13154. *
  13155. * @callback EventTarget~EventListener
  13156. * @this {EventTarget}
  13157. *
  13158. * @param {EventTarget~Event} event
  13159. * the event that triggered this function
  13160. *
  13161. * @param {Object} [hash]
  13162. * hash of data sent during the event
  13163. */
  13164. /**
  13165. * An object containing event names as keys and booleans as values.
  13166. *
  13167. * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
  13168. * will have extra functionality. See that function for more information.
  13169. *
  13170. * @property EventTarget.prototype.allowedEvents_
  13171. * @private
  13172. */
  13173. /**
  13174. * @file src/js/event-target.js
  13175. */
  13176. EventTarget.prototype.allowedEvents_ = {};
  13177. /**
  13178. * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
  13179. * function that will get called when an event with a certain name gets triggered.
  13180. *
  13181. * @param {string|string[]} type
  13182. * An event name or an array of event names.
  13183. *
  13184. * @param {EventTarget~EventListener} fn
  13185. * The function to call with `EventTarget`s
  13186. */
  13187. EventTarget.prototype.on = function (type, fn) {
  13188. // Remove the addEventListener alias before calling Events.on
  13189. // so we don't get into an infinite type loop
  13190. var ael = this.addEventListener;
  13191. this.addEventListener = function () {};
  13192. Events.on(this, type, fn);
  13193. this.addEventListener = ael;
  13194. };
  13195. /**
  13196. * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
  13197. * the standard DOM API.
  13198. *
  13199. * @function
  13200. * @see {@link EventTarget#on}
  13201. */
  13202. EventTarget.prototype.addEventListener = EventTarget.prototype.on;
  13203. /**
  13204. * Removes an `event listener` for a specific event from an instance of `EventTarget`.
  13205. * This makes it so that the `event listener` will no longer get called when the
  13206. * named event happens.
  13207. *
  13208. * @param {string|string[]} type
  13209. * An event name or an array of event names.
  13210. *
  13211. * @param {EventTarget~EventListener} fn
  13212. * The function to remove.
  13213. */
  13214. EventTarget.prototype.off = function (type, fn) {
  13215. Events.off(this, type, fn);
  13216. };
  13217. /**
  13218. * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
  13219. * the standard DOM API.
  13220. *
  13221. * @function
  13222. * @see {@link EventTarget#off}
  13223. */
  13224. EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
  13225. /**
  13226. * This function will add an `event listener` that gets triggered only once. After the
  13227. * first trigger it will get removed. This is like adding an `event listener`
  13228. * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
  13229. *
  13230. * @param {string|string[]} type
  13231. * An event name or an array of event names.
  13232. *
  13233. * @param {EventTarget~EventListener} fn
  13234. * The function to be called once for each event name.
  13235. */
  13236. EventTarget.prototype.one = function (type, fn) {
  13237. // Remove the addEventListener alialing Events.on
  13238. // so we don't get into an infinite type loop
  13239. var ael = this.addEventListener;
  13240. this.addEventListener = function () {};
  13241. Events.one(this, type, fn);
  13242. this.addEventListener = ael;
  13243. };
  13244. /**
  13245. * This function causes an event to happen. This will then cause any `event listeners`
  13246. * that are waiting for that event, to get called. If there are no `event listeners`
  13247. * for an event then nothing will happen.
  13248. *
  13249. * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
  13250. * Trigger will also call the `on` + `uppercaseEventName` function.
  13251. *
  13252. * Example:
  13253. * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
  13254. * `onClick` if it exists.
  13255. *
  13256. * @param {string|EventTarget~Event|Object} event
  13257. * The name of the event, an `Event`, or an object with a key of type set to
  13258. * an event name.
  13259. */
  13260. EventTarget.prototype.trigger = function (event) {
  13261. var type = event.type || event;
  13262. if (typeof event === 'string') {
  13263. event = { type: type };
  13264. }
  13265. event = Events.fixEvent(event);
  13266. if (this.allowedEvents_[type] && this['on' + type]) {
  13267. this['on' + type](event);
  13268. }
  13269. Events.trigger(this, event);
  13270. };
  13271. /**
  13272. * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
  13273. * the standard DOM API.
  13274. *
  13275. * @function
  13276. * @see {@link EventTarget#trigger}
  13277. */
  13278. EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
  13279. exports['default'] = EventTarget;
  13280. },{"./utils/events.js":124}],85:[function(require,module,exports){
  13281. 'use strict';
  13282. exports.__esModule = true;
  13283. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  13284. var _log = require('./utils/log');
  13285. var _log2 = _interopRequireDefault(_log);
  13286. var _obj = require('./utils/obj');
  13287. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13288. /**
  13289. * @file extend.js
  13290. * @module extend
  13291. */
  13292. /**
  13293. * A combination of node inherits and babel's inherits (after transpile).
  13294. * Both work the same but node adds `super_` to the subClass
  13295. * and Bable adds the superClass as __proto__. Both seem useful.
  13296. *
  13297. * @param {Object} subClass
  13298. * The class to inherit to
  13299. *
  13300. * @param {Object} superClass
  13301. * The class to inherit from
  13302. *
  13303. * @private
  13304. */
  13305. var _inherits = function _inherits(subClass, superClass) {
  13306. if (typeof superClass !== 'function' && superClass !== null) {
  13307. throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
  13308. }
  13309. subClass.prototype = Object.create(superClass && superClass.prototype, {
  13310. constructor: {
  13311. value: subClass,
  13312. enumerable: false,
  13313. writable: true,
  13314. configurable: true
  13315. }
  13316. });
  13317. if (superClass) {
  13318. // node
  13319. subClass.super_ = superClass;
  13320. }
  13321. };
  13322. /**
  13323. * Function for subclassing using the same inheritance that
  13324. * videojs uses internally
  13325. *
  13326. * @param {Object} superClass
  13327. * The class to inherit from
  13328. *
  13329. * @param {Object} [subClassMethods={}]
  13330. * The class to inherit to
  13331. *
  13332. * @return {Object}
  13333. * The new object with subClassMethods that inherited superClass.
  13334. */
  13335. var extendFn = function extendFn(superClass) {
  13336. var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13337. var subClass = function subClass() {
  13338. superClass.apply(this, arguments);
  13339. };
  13340. var methods = {};
  13341. if ((0, _obj.isObject)(subClassMethods)) {
  13342. if (typeof subClassMethods.init === 'function') {
  13343. _log2['default'].warn('Constructor logic via init() is deprecated; please use constructor() instead.');
  13344. subClassMethods.constructor = subClassMethods.init;
  13345. }
  13346. if (subClassMethods.constructor !== Object.prototype.constructor) {
  13347. subClass = subClassMethods.constructor;
  13348. }
  13349. methods = subClassMethods;
  13350. } else if (typeof subClassMethods === 'function') {
  13351. subClass = subClassMethods;
  13352. }
  13353. _inherits(subClass, superClass);
  13354. // Extend subObj's prototype with functions and other properties from props
  13355. for (var name in methods) {
  13356. if (methods.hasOwnProperty(name)) {
  13357. subClass.prototype[name] = methods[name];
  13358. }
  13359. }
  13360. return subClass;
  13361. };
  13362. exports['default'] = extendFn;
  13363. },{"./utils/log":128,"./utils/obj":130}],86:[function(require,module,exports){
  13364. 'use strict';
  13365. exports.__esModule = true;
  13366. var _document = require('global/document');
  13367. var _document2 = _interopRequireDefault(_document);
  13368. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13369. /**
  13370. * Store the browser-specific methods for the fullscreen API.
  13371. *
  13372. * @type {Object}
  13373. * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
  13374. * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
  13375. */
  13376. var FullscreenApi = {};
  13377. // browser API methods
  13378. /**
  13379. * @file fullscreen-api.js
  13380. * @module fullscreen-api
  13381. * @private
  13382. */
  13383. var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
  13384. // WebKit
  13385. ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
  13386. // Old WebKit (Safari 5.1)
  13387. ['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
  13388. // Mozilla
  13389. ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
  13390. // Microsoft
  13391. ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
  13392. var specApi = apiMap[0];
  13393. var browserApi = void 0;
  13394. // determine the supported set of functions
  13395. for (var i = 0; i < apiMap.length; i++) {
  13396. // check for exitFullscreen function
  13397. if (apiMap[i][1] in _document2['default']) {
  13398. browserApi = apiMap[i];
  13399. break;
  13400. }
  13401. }
  13402. // map the browser API names to the spec API names
  13403. if (browserApi) {
  13404. for (var _i = 0; _i < browserApi.length; _i++) {
  13405. FullscreenApi[specApi[_i]] = browserApi[_i];
  13406. }
  13407. }
  13408. exports['default'] = FullscreenApi;
  13409. },{"global/document":136}],87:[function(require,module,exports){
  13410. 'use strict';
  13411. exports.__esModule = true;
  13412. var _component = require('./component');
  13413. var _component2 = _interopRequireDefault(_component);
  13414. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13415. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  13416. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  13417. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  13418. * @file loading-spinner.js
  13419. */
  13420. /**
  13421. * A loading spinner for use during waiting/loading events.
  13422. *
  13423. * @extends Component
  13424. */
  13425. var LoadingSpinner = function (_Component) {
  13426. _inherits(LoadingSpinner, _Component);
  13427. function LoadingSpinner() {
  13428. _classCallCheck(this, LoadingSpinner);
  13429. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  13430. }
  13431. /**
  13432. * Create the `LoadingSpinner`s DOM element.
  13433. *
  13434. * @return {Element}
  13435. * The dom element that gets created.
  13436. */
  13437. LoadingSpinner.prototype.createEl = function createEl() {
  13438. return _Component.prototype.createEl.call(this, 'div', {
  13439. className: 'vjs-loading-spinner',
  13440. dir: 'ltr'
  13441. });
  13442. };
  13443. return LoadingSpinner;
  13444. }(_component2['default']);
  13445. _component2['default'].registerComponent('LoadingSpinner', LoadingSpinner);
  13446. exports['default'] = LoadingSpinner;
  13447. },{"./component":47}],88:[function(require,module,exports){
  13448. 'use strict';
  13449. exports.__esModule = true;
  13450. var _obj = require('./utils/obj');
  13451. /**
  13452. * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
  13453. *
  13454. * @param {number|string|Object|MediaError} value
  13455. * This can be of multiple types:
  13456. * - number: should be a standard error code
  13457. * - string: an error message (the code will be 0)
  13458. * - Object: arbitrary properties
  13459. * - `MediaError` (native): used to populate a video.js `MediaError` object
  13460. * - `MediaError` (video.js): will return itself if it's already a
  13461. * video.js `MediaError` object.
  13462. *
  13463. * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
  13464. * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
  13465. *
  13466. * @class MediaError
  13467. */
  13468. function MediaError(value) {
  13469. // Allow redundant calls to this constructor to avoid having `instanceof`
  13470. // checks peppered around the code.
  13471. if (value instanceof MediaError) {
  13472. return value;
  13473. }
  13474. if (typeof value === 'number') {
  13475. this.code = value;
  13476. } else if (typeof value === 'string') {
  13477. // default code is zero, so this is a custom error
  13478. this.message = value;
  13479. } else if ((0, _obj.isObject)(value)) {
  13480. // We assign the `code` property manually because native `MediaError` objects
  13481. // do not expose it as an own/enumerable property of the object.
  13482. if (typeof value.code === 'number') {
  13483. this.code = value.code;
  13484. }
  13485. (0, _obj.assign)(this, value);
  13486. }
  13487. if (!this.message) {
  13488. this.message = MediaError.defaultMessages[this.code] || '';
  13489. }
  13490. }
  13491. /**
  13492. * The error code that refers two one of the defined `MediaError` types
  13493. *
  13494. * @type {Number}
  13495. */
  13496. /**
  13497. * @file media-error.js
  13498. */
  13499. MediaError.prototype.code = 0;
  13500. /**
  13501. * An optional message that to show with the error. Message is not part of the HTML5
  13502. * video spec but allows for more informative custom errors.
  13503. *
  13504. * @type {String}
  13505. */
  13506. MediaError.prototype.message = '';
  13507. /**
  13508. * An optional status code that can be set by plugins to allow even more detail about
  13509. * the error. For example a plugin might provide a specific HTTP status code and an
  13510. * error message for that code. Then when the plugin gets that error this class will
  13511. * know how to display an error message for it. This allows a custom message to show
  13512. * up on the `Player` error overlay.
  13513. *
  13514. * @type {Array}
  13515. */
  13516. MediaError.prototype.status = null;
  13517. /**
  13518. * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
  13519. * specification listed under {@link MediaError} for more information.
  13520. *
  13521. * @enum {array}
  13522. * @readonly
  13523. * @property {string} 0 - MEDIA_ERR_CUSTOM
  13524. * @property {string} 1 - MEDIA_ERR_CUSTOM
  13525. * @property {string} 2 - MEDIA_ERR_ABORTED
  13526. * @property {string} 3 - MEDIA_ERR_NETWORK
  13527. * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
  13528. * @property {string} 5 - MEDIA_ERR_ENCRYPTED
  13529. */
  13530. MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
  13531. /**
  13532. * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
  13533. *
  13534. * @type {Array}
  13535. * @constant
  13536. */
  13537. MediaError.defaultMessages = {
  13538. 1: 'You aborted the media playback',
  13539. 2: 'A network error caused the media download to fail part-way.',
  13540. 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
  13541. 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
  13542. 5: 'The media is encrypted and we do not have the keys to decrypt it.'
  13543. };
  13544. // Add types as properties on MediaError
  13545. // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
  13546. for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
  13547. MediaError[MediaError.errorTypes[errNum]] = errNum;
  13548. // values should be accessible on both the class and instance
  13549. MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
  13550. }
  13551. // jsdocs for instance/static members added above
  13552. // instance methods use `#` and static methods use `.`
  13553. /**
  13554. * W3C error code for any custom error.
  13555. *
  13556. * @member MediaError#MEDIA_ERR_CUSTOM
  13557. * @constant {number}
  13558. * @default 0
  13559. */
  13560. /**
  13561. * W3C error code for any custom error.
  13562. *
  13563. * @member MediaError.MEDIA_ERR_CUSTOM
  13564. * @constant {number}
  13565. * @default 0
  13566. */
  13567. /**
  13568. * W3C error code for media error aborted.
  13569. *
  13570. * @member MediaError#MEDIA_ERR_ABORTED
  13571. * @constant {number}
  13572. * @default 1
  13573. */
  13574. /**
  13575. * W3C error code for media error aborted.
  13576. *
  13577. * @member MediaError.MEDIA_ERR_ABORTED
  13578. * @constant {number}
  13579. * @default 1
  13580. */
  13581. /**
  13582. * W3C error code for any network error.
  13583. *
  13584. * @member MediaError#MEDIA_ERR_NETWORK
  13585. * @constant {number}
  13586. * @default 2
  13587. */
  13588. /**
  13589. * W3C error code for any network error.
  13590. *
  13591. * @member MediaError.MEDIA_ERR_NETWORK
  13592. * @constant {number}
  13593. * @default 2
  13594. */
  13595. /**
  13596. * W3C error code for any decoding error.
  13597. *
  13598. * @member MediaError#MEDIA_ERR_DECODE
  13599. * @constant {number}
  13600. * @default 3
  13601. */
  13602. /**
  13603. * W3C error code for any decoding error.
  13604. *
  13605. * @member MediaError.MEDIA_ERR_DECODE
  13606. * @constant {number}
  13607. * @default 3
  13608. */
  13609. /**
  13610. * W3C error code for any time that a source is not supported.
  13611. *
  13612. * @member MediaError#MEDIA_ERR_SRC_NOT_SUPPORTED
  13613. * @constant {number}
  13614. * @default 4
  13615. */
  13616. /**
  13617. * W3C error code for any time that a source is not supported.
  13618. *
  13619. * @member MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
  13620. * @constant {number}
  13621. * @default 4
  13622. */
  13623. /**
  13624. * W3C error code for any time that a source is encrypted.
  13625. *
  13626. * @member MediaError#MEDIA_ERR_ENCRYPTED
  13627. * @constant {number}
  13628. * @default 5
  13629. */
  13630. /**
  13631. * W3C error code for any time that a source is encrypted.
  13632. *
  13633. * @member MediaError.MEDIA_ERR_ENCRYPTED
  13634. * @constant {number}
  13635. * @default 5
  13636. */
  13637. exports['default'] = MediaError;
  13638. },{"./utils/obj":130}],89:[function(require,module,exports){
  13639. 'use strict';
  13640. exports.__esModule = true;
  13641. var _clickableComponent = require('../clickable-component.js');
  13642. var _clickableComponent2 = _interopRequireDefault(_clickableComponent);
  13643. var _component = require('../component.js');
  13644. var _component2 = _interopRequireDefault(_component);
  13645. var _menu = require('./menu.js');
  13646. var _menu2 = _interopRequireDefault(_menu);
  13647. var _dom = require('../utils/dom.js');
  13648. var Dom = _interopRequireWildcard(_dom);
  13649. var _fn = require('../utils/fn.js');
  13650. var Fn = _interopRequireWildcard(_fn);
  13651. var _toTitleCase = require('../utils/to-title-case.js');
  13652. var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
  13653. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  13654. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13655. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  13656. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  13657. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  13658. * @file menu-button.js
  13659. */
  13660. /**
  13661. * A `MenuButton` class for any popup {@link Menu}.
  13662. *
  13663. * @extends ClickableComponent
  13664. */
  13665. var MenuButton = function (_ClickableComponent) {
  13666. _inherits(MenuButton, _ClickableComponent);
  13667. /**
  13668. * Creates an instance of this class.
  13669. *
  13670. * @param {Player} player
  13671. * The `Player` that this class should be attached to.
  13672. *
  13673. * @param {Object} [options={}]
  13674. * The key/value store of player options.
  13675. */
  13676. function MenuButton(player) {
  13677. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13678. _classCallCheck(this, MenuButton);
  13679. var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  13680. _this.update();
  13681. _this.enabled_ = true;
  13682. _this.el_.setAttribute('aria-haspopup', 'true');
  13683. _this.el_.setAttribute('role', 'menuitem');
  13684. _this.on('keydown', _this.handleSubmenuKeyPress);
  13685. return _this;
  13686. }
  13687. /**
  13688. * Update the menu based on the current state of its items.
  13689. */
  13690. MenuButton.prototype.update = function update() {
  13691. var menu = this.createMenu();
  13692. if (this.menu) {
  13693. this.removeChild(this.menu);
  13694. }
  13695. this.menu = menu;
  13696. this.addChild(menu);
  13697. /**
  13698. * Track the state of the menu button
  13699. *
  13700. * @type {Boolean}
  13701. * @private
  13702. */
  13703. this.buttonPressed_ = false;
  13704. this.el_.setAttribute('aria-expanded', 'false');
  13705. if (this.items && this.items.length <= this.hideThreshold_) {
  13706. this.hide();
  13707. } else {
  13708. this.show();
  13709. }
  13710. };
  13711. /**
  13712. * Create the menu and add all items to it.
  13713. *
  13714. * @return {Menu}
  13715. * The constructed menu
  13716. */
  13717. MenuButton.prototype.createMenu = function createMenu() {
  13718. var menu = new _menu2['default'](this.player_);
  13719. /**
  13720. * Hide the menu if the number of items is less than or equal to this threshold. This defaults
  13721. * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
  13722. * it here because every time we run `createMenu` we need to reset the value.
  13723. *
  13724. * @protected
  13725. * @type {Number}
  13726. */
  13727. this.hideThreshold_ = 0;
  13728. // Add a title list item to the top
  13729. if (this.options_.title) {
  13730. var title = Dom.createEl('li', {
  13731. className: 'vjs-menu-title',
  13732. innerHTML: (0, _toTitleCase2['default'])(this.options_.title),
  13733. tabIndex: -1
  13734. });
  13735. this.hideThreshold_ += 1;
  13736. menu.children_.unshift(title);
  13737. Dom.insertElFirst(title, menu.contentEl());
  13738. }
  13739. this.items = this.createItems();
  13740. if (this.items) {
  13741. // Add menu items to the menu
  13742. for (var i = 0; i < this.items.length; i++) {
  13743. menu.addItem(this.items[i]);
  13744. }
  13745. }
  13746. return menu;
  13747. };
  13748. /**
  13749. * Create the list of menu items. Specific to each subclass.
  13750. *
  13751. * @abstract
  13752. */
  13753. MenuButton.prototype.createItems = function createItems() {};
  13754. /**
  13755. * Create the `MenuButtons`s DOM element.
  13756. *
  13757. * @return {Element}
  13758. * The element that gets created.
  13759. */
  13760. MenuButton.prototype.createEl = function createEl() {
  13761. return _ClickableComponent.prototype.createEl.call(this, 'div', {
  13762. className: this.buildCSSClass()
  13763. });
  13764. };
  13765. /**
  13766. * Builds the default DOM `className`.
  13767. *
  13768. * @return {string}
  13769. * The DOM `className` for this object.
  13770. */
  13771. MenuButton.prototype.buildCSSClass = function buildCSSClass() {
  13772. var menuButtonClass = 'vjs-menu-button';
  13773. // If the inline option is passed, we want to use different styles altogether.
  13774. if (this.options_.inline === true) {
  13775. menuButtonClass += '-inline';
  13776. } else {
  13777. menuButtonClass += '-popup';
  13778. }
  13779. return 'vjs-menu-button ' + menuButtonClass + ' ' + _ClickableComponent.prototype.buildCSSClass.call(this);
  13780. };
  13781. /**
  13782. * Handle a click on a `MenuButton`.
  13783. * See {@link ClickableComponent#handleClick} for instances where this is called.
  13784. *
  13785. * @param {EventTarget~Event} event
  13786. * The `keydown`, `tap`, or `click` event that caused this function to be
  13787. * called.
  13788. *
  13789. * @listens tap
  13790. * @listens click
  13791. */
  13792. MenuButton.prototype.handleClick = function handleClick(event) {
  13793. // When you click the button it adds focus, which will show the menu.
  13794. // So we'll remove focus when the mouse leaves the button. Focus is needed
  13795. // for tab navigation.
  13796. this.one(this.menu.contentEl(), 'mouseleave', Fn.bind(this, function (e) {
  13797. this.unpressButton();
  13798. this.el_.blur();
  13799. }));
  13800. if (this.buttonPressed_) {
  13801. this.unpressButton();
  13802. } else {
  13803. this.pressButton();
  13804. }
  13805. };
  13806. /**
  13807. * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
  13808. * {@link ClickableComponent#handleKeyPress} for instances where this is called.
  13809. *
  13810. * @param {EventTarget~Event} event
  13811. * The `keydown` event that caused this function to be called.
  13812. *
  13813. * @listens keydown
  13814. */
  13815. MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
  13816. // Escape (27) key or Tab (9) key unpress the 'button'
  13817. if (event.which === 27 || event.which === 9) {
  13818. if (this.buttonPressed_) {
  13819. this.unpressButton();
  13820. }
  13821. // Don't preventDefault for Tab key - we still want to lose focus
  13822. if (event.which !== 9) {
  13823. event.preventDefault();
  13824. }
  13825. // Up (38) key or Down (40) key press the 'button'
  13826. } else if (event.which === 38 || event.which === 40) {
  13827. if (!this.buttonPressed_) {
  13828. this.pressButton();
  13829. event.preventDefault();
  13830. }
  13831. } else {
  13832. _ClickableComponent.prototype.handleKeyPress.call(this, event);
  13833. }
  13834. };
  13835. /**
  13836. * Handle a `keydown` event on a sub-menu. The listener for this is added in
  13837. * the constructor.
  13838. *
  13839. * @param {EventTarget~Event} event
  13840. * Key press event
  13841. *
  13842. * @listens keydown
  13843. */
  13844. MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
  13845. // Escape (27) key or Tab (9) key unpress the 'button'
  13846. if (event.which === 27 || event.which === 9) {
  13847. if (this.buttonPressed_) {
  13848. this.unpressButton();
  13849. }
  13850. // Don't preventDefault for Tab key - we still want to lose focus
  13851. if (event.which !== 9) {
  13852. event.preventDefault();
  13853. }
  13854. }
  13855. };
  13856. /**
  13857. * Put the current `MenuButton` into a pressed state.
  13858. */
  13859. MenuButton.prototype.pressButton = function pressButton() {
  13860. if (this.enabled_) {
  13861. this.buttonPressed_ = true;
  13862. this.menu.lockShowing();
  13863. this.el_.setAttribute('aria-expanded', 'true');
  13864. // set the focus into the submenu
  13865. this.menu.focus();
  13866. }
  13867. };
  13868. /**
  13869. * Take the current `MenuButton` out of a pressed state.
  13870. */
  13871. MenuButton.prototype.unpressButton = function unpressButton() {
  13872. if (this.enabled_) {
  13873. this.buttonPressed_ = false;
  13874. this.menu.unlockShowing();
  13875. this.el_.setAttribute('aria-expanded', 'false');
  13876. // Set focus back to this menu button
  13877. this.el_.focus();
  13878. }
  13879. };
  13880. /**
  13881. * Disable the `MenuButton`. Don't allow it to be clicked.
  13882. *
  13883. * @return {MenuButton}
  13884. * Returns itself; method can be chained.
  13885. */
  13886. MenuButton.prototype.disable = function disable() {
  13887. // Unpress, but don't force focus on this button
  13888. this.buttonPressed_ = false;
  13889. this.menu.unlockShowing();
  13890. this.el_.setAttribute('aria-expanded', 'false');
  13891. this.enabled_ = false;
  13892. return _ClickableComponent.prototype.disable.call(this);
  13893. };
  13894. /**
  13895. * Enable the `MenuButton`. Allow it to be clicked.
  13896. *
  13897. * @return {MenuButton}
  13898. * Returns itself; method can be chained.
  13899. */
  13900. MenuButton.prototype.enable = function enable() {
  13901. this.enabled_ = true;
  13902. return _ClickableComponent.prototype.enable.call(this);
  13903. };
  13904. return MenuButton;
  13905. }(_clickableComponent2['default']);
  13906. _component2['default'].registerComponent('MenuButton', MenuButton);
  13907. exports['default'] = MenuButton;
  13908. },{"../clickable-component.js":45,"../component.js":47,"../utils/dom.js":123,"../utils/fn.js":125,"../utils/to-title-case.js":133,"./menu.js":91}],90:[function(require,module,exports){
  13909. 'use strict';
  13910. exports.__esModule = true;
  13911. var _clickableComponent = require('../clickable-component.js');
  13912. var _clickableComponent2 = _interopRequireDefault(_clickableComponent);
  13913. var _component = require('../component.js');
  13914. var _component2 = _interopRequireDefault(_component);
  13915. var _obj = require('../utils/obj');
  13916. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  13917. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  13918. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  13919. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  13920. * @file menu-item.js
  13921. */
  13922. /**
  13923. * The component for a menu item. `<li>`
  13924. *
  13925. * @extends ClickableComponent
  13926. */
  13927. var MenuItem = function (_ClickableComponent) {
  13928. _inherits(MenuItem, _ClickableComponent);
  13929. /**
  13930. * Creates an instance of the this class.
  13931. *
  13932. * @param {Player} player
  13933. * The `Player` that this class should be attached to.
  13934. *
  13935. * @param {Object} [options={}]
  13936. * The key/value store of player options.
  13937. *
  13938. */
  13939. function MenuItem(player, options) {
  13940. _classCallCheck(this, MenuItem);
  13941. var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  13942. _this.selectable = options.selectable;
  13943. _this.selected(options.selected);
  13944. if (_this.selectable) {
  13945. // TODO: May need to be either menuitemcheckbox or menuitemradio,
  13946. // and may need logical grouping of menu items.
  13947. _this.el_.setAttribute('role', 'menuitemcheckbox');
  13948. } else {
  13949. _this.el_.setAttribute('role', 'menuitem');
  13950. }
  13951. return _this;
  13952. }
  13953. /**
  13954. * Create the `MenuItem's DOM element
  13955. *
  13956. * @param {string} [type=li]
  13957. * Element's node type, not actually used, always set to `li`.
  13958. *
  13959. * @param {Object} [props={}]
  13960. * An object of properties that should be set on the element
  13961. *
  13962. * @param {Object} [attrs={}]
  13963. * An object of attributes that should be set on the element
  13964. *
  13965. * @return {Element}
  13966. * The element that gets created.
  13967. */
  13968. MenuItem.prototype.createEl = function createEl(type, props, attrs) {
  13969. // The control is textual, not just an icon
  13970. this.nonIconControl = true;
  13971. return _ClickableComponent.prototype.createEl.call(this, 'li', (0, _obj.assign)({
  13972. className: 'vjs-menu-item',
  13973. innerHTML: this.localize(this.options_.label),
  13974. tabIndex: -1
  13975. }, props), attrs);
  13976. };
  13977. /**
  13978. * Any click on a `MenuItem` puts int into the selected state.
  13979. * See {@link ClickableComponent#handleClick} for instances where this is called.
  13980. *
  13981. * @param {EventTarget~Event} event
  13982. * The `keydown`, `tap`, or `click` event that caused this function to be
  13983. * called.
  13984. *
  13985. * @listens tap
  13986. * @listens click
  13987. */
  13988. MenuItem.prototype.handleClick = function handleClick(event) {
  13989. this.selected(true);
  13990. };
  13991. /**
  13992. * Set the state for this menu item as selected or not.
  13993. *
  13994. * @param {boolean} selected
  13995. * if the menu item is selected or not
  13996. */
  13997. MenuItem.prototype.selected = function selected(_selected) {
  13998. if (this.selectable) {
  13999. if (_selected) {
  14000. this.addClass('vjs-selected');
  14001. this.el_.setAttribute('aria-checked', 'true');
  14002. // aria-checked isn't fully supported by browsers/screen readers,
  14003. // so indicate selected state to screen reader in the control text.
  14004. this.controlText(', selected');
  14005. } else {
  14006. this.removeClass('vjs-selected');
  14007. this.el_.setAttribute('aria-checked', 'false');
  14008. // Indicate un-selected state to screen reader
  14009. // Note that a space clears out the selected state text
  14010. this.controlText(' ');
  14011. }
  14012. }
  14013. };
  14014. return MenuItem;
  14015. }(_clickableComponent2['default']);
  14016. _component2['default'].registerComponent('MenuItem', MenuItem);
  14017. exports['default'] = MenuItem;
  14018. },{"../clickable-component.js":45,"../component.js":47,"../utils/obj":130}],91:[function(require,module,exports){
  14019. 'use strict';
  14020. exports.__esModule = true;
  14021. var _component = require('../component.js');
  14022. var _component2 = _interopRequireDefault(_component);
  14023. var _dom = require('../utils/dom.js');
  14024. var Dom = _interopRequireWildcard(_dom);
  14025. var _fn = require('../utils/fn.js');
  14026. var Fn = _interopRequireWildcard(_fn);
  14027. var _events = require('../utils/events.js');
  14028. var Events = _interopRequireWildcard(_events);
  14029. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  14030. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  14031. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  14032. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  14033. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  14034. * @file menu.js
  14035. */
  14036. /**
  14037. * The Menu component is used to build popup menus, including subtitle and
  14038. * captions selection menus.
  14039. *
  14040. * @extends Component
  14041. */
  14042. var Menu = function (_Component) {
  14043. _inherits(Menu, _Component);
  14044. /**
  14045. * Create an instance of this class.
  14046. *
  14047. * @param {Player} player
  14048. * the player that this component should attach to
  14049. *
  14050. * @param {Object} [options]
  14051. * Object of option names and values
  14052. *
  14053. */
  14054. function Menu(player, options) {
  14055. _classCallCheck(this, Menu);
  14056. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  14057. _this.focusedChild_ = -1;
  14058. _this.on('keydown', _this.handleKeyPress);
  14059. return _this;
  14060. }
  14061. /**
  14062. * Add a {@link MenuItem} to the menu.
  14063. *
  14064. * @param {Object|string} component
  14065. * The name or instance of the `MenuItem` to add.
  14066. *
  14067. */
  14068. Menu.prototype.addItem = function addItem(component) {
  14069. this.addChild(component);
  14070. component.on('click', Fn.bind(this, function (event) {
  14071. this.unlockShowing();
  14072. // TODO: Need to set keyboard focus back to the menuButton
  14073. }));
  14074. };
  14075. /**
  14076. * Create the `Menu`s DOM element.
  14077. *
  14078. * @return {Element}
  14079. * the element that was created
  14080. */
  14081. Menu.prototype.createEl = function createEl() {
  14082. var contentElType = this.options_.contentElType || 'ul';
  14083. this.contentEl_ = Dom.createEl(contentElType, {
  14084. className: 'vjs-menu-content'
  14085. });
  14086. this.contentEl_.setAttribute('role', 'menu');
  14087. var el = _Component.prototype.createEl.call(this, 'div', {
  14088. append: this.contentEl_,
  14089. className: 'vjs-menu'
  14090. });
  14091. el.setAttribute('role', 'presentation');
  14092. el.appendChild(this.contentEl_);
  14093. // Prevent clicks from bubbling up. Needed for Menu Buttons,
  14094. // where a click on the parent is significant
  14095. Events.on(el, 'click', function (event) {
  14096. event.preventDefault();
  14097. event.stopImmediatePropagation();
  14098. });
  14099. return el;
  14100. };
  14101. /**
  14102. * Handle a `keydown` event on this menu. This listener is added in the constructor.
  14103. *
  14104. * @param {EventTarget~Event} event
  14105. * A `keydown` event that happened on the menu.
  14106. *
  14107. * @listens keydown
  14108. */
  14109. Menu.prototype.handleKeyPress = function handleKeyPress(event) {
  14110. // Left and Down Arrows
  14111. if (event.which === 37 || event.which === 40) {
  14112. event.preventDefault();
  14113. this.stepForward();
  14114. // Up and Right Arrows
  14115. } else if (event.which === 38 || event.which === 39) {
  14116. event.preventDefault();
  14117. this.stepBack();
  14118. }
  14119. };
  14120. /**
  14121. * Move to next (lower) menu item for keyboard users.
  14122. */
  14123. Menu.prototype.stepForward = function stepForward() {
  14124. var stepChild = 0;
  14125. if (this.focusedChild_ !== undefined) {
  14126. stepChild = this.focusedChild_ + 1;
  14127. }
  14128. this.focus(stepChild);
  14129. };
  14130. /**
  14131. * Move to previous (higher) menu item for keyboard users.
  14132. */
  14133. Menu.prototype.stepBack = function stepBack() {
  14134. var stepChild = 0;
  14135. if (this.focusedChild_ !== undefined) {
  14136. stepChild = this.focusedChild_ - 1;
  14137. }
  14138. this.focus(stepChild);
  14139. };
  14140. /**
  14141. * Set focus on a {@link MenuItem} in the `Menu`.
  14142. *
  14143. * @param {Object|string} [item=0]
  14144. * Index of child item set focus on.
  14145. */
  14146. Menu.prototype.focus = function focus() {
  14147. var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  14148. var children = this.children().slice();
  14149. var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
  14150. if (haveTitle) {
  14151. children.shift();
  14152. }
  14153. if (children.length > 0) {
  14154. if (item < 0) {
  14155. item = 0;
  14156. } else if (item >= children.length) {
  14157. item = children.length - 1;
  14158. }
  14159. this.focusedChild_ = item;
  14160. children[item].el_.focus();
  14161. }
  14162. };
  14163. return Menu;
  14164. }(_component2['default']);
  14165. _component2['default'].registerComponent('Menu', Menu);
  14166. exports['default'] = Menu;
  14167. },{"../component.js":47,"../utils/dom.js":123,"../utils/events.js":124,"../utils/fn.js":125}],92:[function(require,module,exports){
  14168. 'use strict';
  14169. exports.__esModule = true;
  14170. var _dom = require('./utils/dom');
  14171. var Dom = _interopRequireWildcard(_dom);
  14172. var _fn = require('./utils/fn');
  14173. var Fn = _interopRequireWildcard(_fn);
  14174. var _component = require('./component');
  14175. var _component2 = _interopRequireDefault(_component);
  14176. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  14177. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  14178. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  14179. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  14180. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  14181. * @file modal-dialog.js
  14182. */
  14183. var MODAL_CLASS_NAME = 'vjs-modal-dialog';
  14184. var ESC = 27;
  14185. /**
  14186. * The `ModalDialog` displays over the video and its controls, which blocks
  14187. * interaction with the player until it is closed.
  14188. *
  14189. * Modal dialogs include a "Close" button and will close when that button
  14190. * is activated - or when ESC is pressed anywhere.
  14191. *
  14192. * @extends Component
  14193. */
  14194. var ModalDialog = function (_Component) {
  14195. _inherits(ModalDialog, _Component);
  14196. /**
  14197. * Create an instance of this class.
  14198. *
  14199. * @param {Player} player
  14200. * The `Player` that this class should be attached to.
  14201. *
  14202. * @param {Object} [options]
  14203. * The key/value store of player options.
  14204. *
  14205. * @param {Mixed} [options.content=undefined]
  14206. * Provide customized content for this modal.
  14207. *
  14208. * @param {string} [options.description]
  14209. * A text description for the modal, primarily for accessibility.
  14210. *
  14211. * @param {boolean} [options.fillAlways=false]
  14212. * Normally, modals are automatically filled only the first time
  14213. * they open. This tells the modal to refresh its content
  14214. * every time it opens.
  14215. *
  14216. * @param {string} [options.label]
  14217. * A text label for the modal, primarily for accessibility.
  14218. *
  14219. * @param {boolean} [options.temporary=true]
  14220. * If `true`, the modal can only be opened once; it will be
  14221. * disposed as soon as it's closed.
  14222. *
  14223. * @param {boolean} [options.uncloseable=false]
  14224. * If `true`, the user will not be able to close the modal
  14225. * through the UI in the normal ways. Programmatic closing is
  14226. * still possible.
  14227. */
  14228. function ModalDialog(player, options) {
  14229. _classCallCheck(this, ModalDialog);
  14230. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  14231. _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
  14232. _this.closeable(!_this.options_.uncloseable);
  14233. _this.content(_this.options_.content);
  14234. // Make sure the contentEl is defined AFTER any children are initialized
  14235. // because we only want the contents of the modal in the contentEl
  14236. // (not the UI elements like the close button).
  14237. _this.contentEl_ = Dom.createEl('div', {
  14238. className: MODAL_CLASS_NAME + '-content'
  14239. }, {
  14240. role: 'document'
  14241. });
  14242. _this.descEl_ = Dom.createEl('p', {
  14243. className: MODAL_CLASS_NAME + '-description vjs-offscreen',
  14244. id: _this.el().getAttribute('aria-describedby')
  14245. });
  14246. Dom.textContent(_this.descEl_, _this.description());
  14247. _this.el_.appendChild(_this.descEl_);
  14248. _this.el_.appendChild(_this.contentEl_);
  14249. return _this;
  14250. }
  14251. /**
  14252. * Create the `ModalDialog`'s DOM element
  14253. *
  14254. * @return {Element}
  14255. * The DOM element that gets created.
  14256. */
  14257. ModalDialog.prototype.createEl = function createEl() {
  14258. return _Component.prototype.createEl.call(this, 'div', {
  14259. className: this.buildCSSClass(),
  14260. tabIndex: -1
  14261. }, {
  14262. 'aria-describedby': this.id() + '_description',
  14263. 'aria-hidden': 'true',
  14264. 'aria-label': this.label(),
  14265. 'role': 'dialog'
  14266. });
  14267. };
  14268. /**
  14269. * Builds the default DOM `className`.
  14270. *
  14271. * @return {string}
  14272. * The DOM `className` for this object.
  14273. */
  14274. ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
  14275. return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
  14276. };
  14277. /**
  14278. * Handles `keydown` events on the document, looking for ESC, which closes
  14279. * the modal.
  14280. *
  14281. * @param {EventTarget~Event} e
  14282. * The keypress that triggered this event.
  14283. *
  14284. * @listens keydown
  14285. */
  14286. ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
  14287. if (e.which === ESC && this.closeable()) {
  14288. this.close();
  14289. }
  14290. };
  14291. /**
  14292. * Returns the label string for this modal. Primarily used for accessibility.
  14293. *
  14294. * @return {string}
  14295. * the localized or raw label of this modal.
  14296. */
  14297. ModalDialog.prototype.label = function label() {
  14298. return this.options_.label || this.localize('Modal Window');
  14299. };
  14300. /**
  14301. * Returns the description string for this modal. Primarily used for
  14302. * accessibility.
  14303. *
  14304. * @return {string}
  14305. * The localized or raw description of this modal.
  14306. */
  14307. ModalDialog.prototype.description = function description() {
  14308. var desc = this.options_.description || this.localize('This is a modal window.');
  14309. // Append a universal closeability message if the modal is closeable.
  14310. if (this.closeable()) {
  14311. desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
  14312. }
  14313. return desc;
  14314. };
  14315. /**
  14316. * Opens the modal.
  14317. *
  14318. * @fires ModalDialog#beforemodalopen
  14319. * @fires ModalDialog#modalopen
  14320. *
  14321. * @return {ModalDialog}
  14322. * Returns itself; method can be chained.
  14323. */
  14324. ModalDialog.prototype.open = function open() {
  14325. if (!this.opened_) {
  14326. var player = this.player();
  14327. /**
  14328. * Fired just before a `ModalDialog` is opened.
  14329. *
  14330. * @event ModalDialog#beforemodalopen
  14331. * @type {EventTarget~Event}
  14332. */
  14333. this.trigger('beforemodalopen');
  14334. this.opened_ = true;
  14335. // Fill content if the modal has never opened before and
  14336. // never been filled.
  14337. if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
  14338. this.fill();
  14339. }
  14340. // If the player was playing, pause it and take note of its previously
  14341. // playing state.
  14342. this.wasPlaying_ = !player.paused();
  14343. if (this.options_.pauseOnOpen && this.wasPlaying_) {
  14344. player.pause();
  14345. }
  14346. if (this.closeable()) {
  14347. this.on(this.el_.ownerDocument, 'keydown', Fn.bind(this, this.handleKeyPress));
  14348. }
  14349. player.controls(false);
  14350. this.show();
  14351. this.el().setAttribute('aria-hidden', 'false');
  14352. /**
  14353. * Fired just after a `ModalDialog` is opened.
  14354. *
  14355. * @event ModalDialog#modalopen
  14356. * @type {EventTarget~Event}
  14357. */
  14358. this.trigger('modalopen');
  14359. this.hasBeenOpened_ = true;
  14360. }
  14361. return this;
  14362. };
  14363. /**
  14364. * If the `ModalDialog` is currently open or closed.
  14365. *
  14366. * @param {boolean} [value]
  14367. * If given, it will open (`true`) or close (`false`) the modal.
  14368. *
  14369. * @return {boolean}
  14370. * the current open state of the modaldialog
  14371. */
  14372. ModalDialog.prototype.opened = function opened(value) {
  14373. if (typeof value === 'boolean') {
  14374. this[value ? 'open' : 'close']();
  14375. }
  14376. return this.opened_;
  14377. };
  14378. /**
  14379. * Closes the modal, does nothing if the `ModalDialog` is
  14380. * not open.
  14381. *
  14382. * @fires ModalDialog#beforemodalclose
  14383. * @fires ModalDialog#modalclose
  14384. *
  14385. * @return {ModalDialog}
  14386. * Returns itself; method can be chained.
  14387. */
  14388. ModalDialog.prototype.close = function close() {
  14389. if (this.opened_) {
  14390. var player = this.player();
  14391. /**
  14392. * Fired just before a `ModalDialog` is closed.
  14393. *
  14394. * @event ModalDialog#beforemodalclose
  14395. * @type {EventTarget~Event}
  14396. */
  14397. this.trigger('beforemodalclose');
  14398. this.opened_ = false;
  14399. if (this.wasPlaying_ && this.options_.pauseOnOpen) {
  14400. player.play();
  14401. }
  14402. if (this.closeable()) {
  14403. this.off(this.el_.ownerDocument, 'keydown', Fn.bind(this, this.handleKeyPress));
  14404. }
  14405. player.controls(true);
  14406. this.hide();
  14407. this.el().setAttribute('aria-hidden', 'true');
  14408. /**
  14409. * Fired just after a `ModalDialog` is closed.
  14410. *
  14411. * @event ModalDialog#modalclose
  14412. * @type {EventTarget~Event}
  14413. */
  14414. this.trigger('modalclose');
  14415. if (this.options_.temporary) {
  14416. this.dispose();
  14417. }
  14418. }
  14419. return this;
  14420. };
  14421. /**
  14422. * Check to see if the `ModalDialog` is closeable via the UI.
  14423. *
  14424. * @param {boolean} [value]
  14425. * If given as a boolean, it will set the `closeable` option.
  14426. *
  14427. * @return {boolean}
  14428. * Returns the final value of the closable option.
  14429. */
  14430. ModalDialog.prototype.closeable = function closeable(value) {
  14431. if (typeof value === 'boolean') {
  14432. var closeable = this.closeable_ = !!value;
  14433. var close = this.getChild('closeButton');
  14434. // If this is being made closeable and has no close button, add one.
  14435. if (closeable && !close) {
  14436. // The close button should be a child of the modal - not its
  14437. // content element, so temporarily change the content element.
  14438. var temp = this.contentEl_;
  14439. this.contentEl_ = this.el_;
  14440. close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' });
  14441. this.contentEl_ = temp;
  14442. this.on(close, 'close', this.close);
  14443. }
  14444. // If this is being made uncloseable and has a close button, remove it.
  14445. if (!closeable && close) {
  14446. this.off(close, 'close', this.close);
  14447. this.removeChild(close);
  14448. close.dispose();
  14449. }
  14450. }
  14451. return this.closeable_;
  14452. };
  14453. /**
  14454. * Fill the modal's content element with the modal's "content" option.
  14455. * The content element will be emptied before this change takes place.
  14456. *
  14457. * @return {ModalDialog}
  14458. * Returns itself; method can be chained.
  14459. */
  14460. ModalDialog.prototype.fill = function fill() {
  14461. return this.fillWith(this.content());
  14462. };
  14463. /**
  14464. * Fill the modal's content element with arbitrary content.
  14465. * The content element will be emptied before this change takes place.
  14466. *
  14467. * @fires ModalDialog#beforemodalfill
  14468. * @fires ModalDialog#modalfill
  14469. *
  14470. * @param {Mixed} [content]
  14471. * The same rules apply to this as apply to the `content` option.
  14472. *
  14473. * @return {ModalDialog}
  14474. * Returns itself; method can be chained.
  14475. */
  14476. ModalDialog.prototype.fillWith = function fillWith(content) {
  14477. var contentEl = this.contentEl();
  14478. var parentEl = contentEl.parentNode;
  14479. var nextSiblingEl = contentEl.nextSibling;
  14480. /**
  14481. * Fired just before a `ModalDialog` is filled with content.
  14482. *
  14483. * @event ModalDialog#beforemodalfill
  14484. * @type {EventTarget~Event}
  14485. */
  14486. this.trigger('beforemodalfill');
  14487. this.hasBeenFilled_ = true;
  14488. // Detach the content element from the DOM before performing
  14489. // manipulation to avoid modifying the live DOM multiple times.
  14490. parentEl.removeChild(contentEl);
  14491. this.empty();
  14492. Dom.insertContent(contentEl, content);
  14493. /**
  14494. * Fired just after a `ModalDialog` is filled with content.
  14495. *
  14496. * @event ModalDialog#modalfill
  14497. * @type {EventTarget~Event}
  14498. */
  14499. this.trigger('modalfill');
  14500. // Re-inject the re-filled content element.
  14501. if (nextSiblingEl) {
  14502. parentEl.insertBefore(contentEl, nextSiblingEl);
  14503. } else {
  14504. parentEl.appendChild(contentEl);
  14505. }
  14506. return this;
  14507. };
  14508. /**
  14509. * Empties the content element. This happens anytime the modal is filled.
  14510. *
  14511. * @fires ModalDialog#beforemodalempty
  14512. * @fires ModalDialog#modalempty
  14513. *
  14514. * @return {ModalDialog}
  14515. * Returns itself; method can be chained.
  14516. */
  14517. ModalDialog.prototype.empty = function empty() {
  14518. /**
  14519. * Fired just before a `ModalDialog` is emptied.
  14520. *
  14521. * @event ModalDialog#beforemodalempty
  14522. * @type {EventTarget~Event}
  14523. */
  14524. this.trigger('beforemodalempty');
  14525. Dom.emptyEl(this.contentEl());
  14526. /**
  14527. * Fired just after a `ModalDialog` is emptied.
  14528. *
  14529. * @event ModalDialog#modalempty
  14530. * @type {EventTarget~Event}
  14531. */
  14532. this.trigger('modalempty');
  14533. return this;
  14534. };
  14535. /**
  14536. * Gets or sets the modal content, which gets normalized before being
  14537. * rendered into the DOM.
  14538. *
  14539. * This does not update the DOM or fill the modal, but it is called during
  14540. * that process.
  14541. *
  14542. * @param {Mixed} [value]
  14543. * If defined, sets the internal content value to be used on the
  14544. * next call(s) to `fill`. This value is normalized before being
  14545. * inserted. To "clear" the internal content value, pass `null`.
  14546. *
  14547. * @return {Mixed}
  14548. * The current content of the modal dialog
  14549. */
  14550. ModalDialog.prototype.content = function content(value) {
  14551. if (typeof value !== 'undefined') {
  14552. this.content_ = value;
  14553. }
  14554. return this.content_;
  14555. };
  14556. return ModalDialog;
  14557. }(_component2['default']);
  14558. /**
  14559. * Default options for `ModalDialog` default options.
  14560. *
  14561. * @type {Object}
  14562. * @private
  14563. */
  14564. ModalDialog.prototype.options_ = {
  14565. pauseOnOpen: true,
  14566. temporary: true
  14567. };
  14568. _component2['default'].registerComponent('ModalDialog', ModalDialog);
  14569. exports['default'] = ModalDialog;
  14570. },{"./component":47,"./utils/dom":123,"./utils/fn":125}],93:[function(require,module,exports){
  14571. 'use strict';
  14572. exports.__esModule = true;
  14573. var _component = require('./component.js');
  14574. var _component2 = _interopRequireDefault(_component);
  14575. var _document = require('global/document');
  14576. var _document2 = _interopRequireDefault(_document);
  14577. var _window = require('global/window');
  14578. var _window2 = _interopRequireDefault(_window);
  14579. var _events = require('./utils/events.js');
  14580. var Events = _interopRequireWildcard(_events);
  14581. var _dom = require('./utils/dom.js');
  14582. var Dom = _interopRequireWildcard(_dom);
  14583. var _fn = require('./utils/fn.js');
  14584. var Fn = _interopRequireWildcard(_fn);
  14585. var _guid = require('./utils/guid.js');
  14586. var Guid = _interopRequireWildcard(_guid);
  14587. var _browser = require('./utils/browser.js');
  14588. var browser = _interopRequireWildcard(_browser);
  14589. var _log = require('./utils/log.js');
  14590. var _log2 = _interopRequireDefault(_log);
  14591. var _toTitleCase = require('./utils/to-title-case.js');
  14592. var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
  14593. var _timeRanges = require('./utils/time-ranges.js');
  14594. var _buffer = require('./utils/buffer.js');
  14595. var _stylesheet = require('./utils/stylesheet.js');
  14596. var stylesheet = _interopRequireWildcard(_stylesheet);
  14597. var _fullscreenApi = require('./fullscreen-api.js');
  14598. var _fullscreenApi2 = _interopRequireDefault(_fullscreenApi);
  14599. var _mediaError = require('./media-error.js');
  14600. var _mediaError2 = _interopRequireDefault(_mediaError);
  14601. var _tuple = require('safe-json-parse/tuple');
  14602. var _tuple2 = _interopRequireDefault(_tuple);
  14603. var _obj = require('./utils/obj');
  14604. var _mergeOptions = require('./utils/merge-options.js');
  14605. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  14606. var _textTrackListConverter = require('./tracks/text-track-list-converter.js');
  14607. var _textTrackListConverter2 = _interopRequireDefault(_textTrackListConverter);
  14608. var _modalDialog = require('./modal-dialog');
  14609. var _modalDialog2 = _interopRequireDefault(_modalDialog);
  14610. var _tech = require('./tech/tech.js');
  14611. var _tech2 = _interopRequireDefault(_tech);
  14612. var _audioTrackList = require('./tracks/audio-track-list.js');
  14613. var _audioTrackList2 = _interopRequireDefault(_audioTrackList);
  14614. var _videoTrackList = require('./tracks/video-track-list.js');
  14615. var _videoTrackList2 = _interopRequireDefault(_videoTrackList);
  14616. require('./tech/loader.js');
  14617. require('./tech/flash.js');
  14618. require('./poster-image.js');
  14619. require('./tracks/text-track-display.js');
  14620. require('./loading-spinner.js');
  14621. require('./big-play-button.js');
  14622. require('./close-button.js');
  14623. require('./control-bar/control-bar.js');
  14624. require('./error-display.js');
  14625. require('./tracks/text-track-settings.js');
  14626. require('./tech/html5.js');
  14627. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  14628. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  14629. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  14630. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  14631. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  14632. * @file player.js
  14633. */
  14634. // Subclasses Component
  14635. // The following imports are used only to ensure that the corresponding modules
  14636. // are always included in the video.js package. Importing the modules will
  14637. // execute them and they will register themselves with video.js.
  14638. // Import Html5 tech, at least for disposing the original video tag.
  14639. // The following tech events are simply re-triggered
  14640. // on the player when they happen
  14641. var TECH_EVENTS_RETRIGGER = [
  14642. /**
  14643. * Fired while the user agent is downloading media data.
  14644. *
  14645. * @event Player#progress
  14646. * @type {EventTarget~Event}
  14647. */
  14648. /**
  14649. * Retrigger the `progress` event that was triggered by the {@link Tech}.
  14650. *
  14651. * @private
  14652. * @method Player#handleTechProgress_
  14653. * @fires Player#progress
  14654. * @listens Tech#progress
  14655. */
  14656. 'progress',
  14657. /**
  14658. * Fires when the loading of an audio/video is aborted.
  14659. *
  14660. * @event Player#abort
  14661. * @type {EventTarget~Event}
  14662. */
  14663. /**
  14664. * Retrigger the `abort` event that was triggered by the {@link Tech}.
  14665. *
  14666. * @private
  14667. * @method Player#handleTechAbort_
  14668. * @fires Player#abort
  14669. * @listens Tech#abort
  14670. */
  14671. 'abort',
  14672. /**
  14673. * Fires when the browser is intentionally not getting media data.
  14674. *
  14675. * @event Player#suspend
  14676. * @type {EventTarget~Event}
  14677. */
  14678. /**
  14679. * Retrigger the `suspend` event that was triggered by the {@link Tech}.
  14680. *
  14681. * @private
  14682. * @method Player#handleTechSuspend_
  14683. * @fires Player#suspend
  14684. * @listens Tech#suspend
  14685. */
  14686. 'suspend',
  14687. /**
  14688. * Fires when the current playlist is empty.
  14689. *
  14690. * @event Player#emptied
  14691. * @type {EventTarget~Event}
  14692. */
  14693. /**
  14694. * Retrigger the `emptied` event that was triggered by the {@link Tech}.
  14695. *
  14696. * @private
  14697. * @method Player#handleTechEmptied_
  14698. * @fires Player#emptied
  14699. * @listens Tech#emptied
  14700. */
  14701. 'emptied',
  14702. /**
  14703. * Fires when the browser is trying to get media data, but data is not available.
  14704. *
  14705. * @event Player#stalled
  14706. * @type {EventTarget~Event}
  14707. */
  14708. /**
  14709. * Retrigger the `stalled` event that was triggered by the {@link Tech}.
  14710. *
  14711. * @private
  14712. * @method Player#handleTechStalled_
  14713. * @fires Player#stalled
  14714. * @listens Tech#stalled
  14715. */
  14716. 'stalled',
  14717. /**
  14718. * Fires when the browser has loaded meta data for the audio/video.
  14719. *
  14720. * @event Player#loadedmetadata
  14721. * @type {EventTarget~Event}
  14722. */
  14723. /**
  14724. * Retrigger the `stalled` event that was triggered by the {@link Tech}.
  14725. *
  14726. * @private
  14727. * @method Player#handleTechLoadedmetadata_
  14728. * @fires Player#loadedmetadata
  14729. * @listens Tech#loadedmetadata
  14730. */
  14731. 'loadedmetadata',
  14732. /**
  14733. * Fires when the browser has loaded the current frame of the audio/video.
  14734. *
  14735. * @event player#loadeddata
  14736. * @type {event}
  14737. */
  14738. /**
  14739. * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
  14740. *
  14741. * @private
  14742. * @method Player#handleTechLoaddeddata_
  14743. * @fires Player#loadeddata
  14744. * @listens Tech#loadeddata
  14745. */
  14746. 'loadeddata',
  14747. /**
  14748. * Fires when the current playback position has changed.
  14749. *
  14750. * @event player#timeupdate
  14751. * @type {event}
  14752. */
  14753. /**
  14754. * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
  14755. *
  14756. * @private
  14757. * @method Player#handleTechTimeUpdate_
  14758. * @fires Player#timeupdate
  14759. * @listens Tech#timeupdate
  14760. */
  14761. 'timeupdate',
  14762. /**
  14763. * Fires when the playing speed of the audio/video is changed
  14764. *
  14765. * @event player#ratechange
  14766. * @type {event}
  14767. */
  14768. /**
  14769. * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
  14770. *
  14771. * @private
  14772. * @method Player#handleTechRatechange_
  14773. * @fires Player#ratechange
  14774. * @listens Tech#ratechange
  14775. */
  14776. 'ratechange',
  14777. /**
  14778. * Fires when the volume has been changed
  14779. *
  14780. * @event player#volumechange
  14781. * @type {event}
  14782. */
  14783. /**
  14784. * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
  14785. *
  14786. * @private
  14787. * @method Player#handleTechVolumechange_
  14788. * @fires Player#volumechange
  14789. * @listens Tech#volumechange
  14790. */
  14791. 'volumechange',
  14792. /**
  14793. * Fires when the text track has been changed
  14794. *
  14795. * @event player#texttrackchange
  14796. * @type {event}
  14797. */
  14798. /**
  14799. * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
  14800. *
  14801. * @private
  14802. * @method Player#handleTechTexttrackchange_
  14803. * @fires Player#texttrackchange
  14804. * @listens Tech#texttrackchange
  14805. */
  14806. 'texttrackchange'];
  14807. /**
  14808. * An instance of the `Player` class is created when any of the Video.js setup methods
  14809. * are used to initialize a video.
  14810. *
  14811. * After an instance has been created it can be accessed globally in two ways:
  14812. * 1. By calling `videojs('example_video_1');`
  14813. * 2. By using it directly via `videojs.players.example_video_1;`
  14814. *
  14815. * @extends Component
  14816. */
  14817. var Player = function (_Component) {
  14818. _inherits(Player, _Component);
  14819. /**
  14820. * Create an instance of this class.
  14821. *
  14822. * @param {Element} tag
  14823. * The original video DOM element used for configuring options.
  14824. *
  14825. * @param {Object} [options]
  14826. * Object of option names and values.
  14827. *
  14828. * @param {Component~ReadyCallback} [ready]
  14829. * Ready callback function.
  14830. */
  14831. function Player(tag, options, ready) {
  14832. _classCallCheck(this, Player);
  14833. // Make sure tag ID exists
  14834. tag.id = tag.id || 'vjs_video_' + Guid.newGUID();
  14835. // Set Options
  14836. // The options argument overrides options set in the video tag
  14837. // which overrides globally set options.
  14838. // This latter part coincides with the load order
  14839. // (tag must exist before Player)
  14840. options = (0, _obj.assign)(Player.getTagSettings(tag), options);
  14841. // Delay the initialization of children because we need to set up
  14842. // player properties first, and can't use `this` before `super()`
  14843. options.initChildren = false;
  14844. // Same with creating the element
  14845. options.createEl = false;
  14846. // we don't want the player to report touch activity on itself
  14847. // see enableTouchActivity in Component
  14848. options.reportTouchActivity = false;
  14849. // If language is not set, get the closest lang attribute
  14850. if (!options.language) {
  14851. if (typeof tag.closest === 'function') {
  14852. var closest = tag.closest('[lang]');
  14853. if (closest) {
  14854. options.language = closest.getAttribute('lang');
  14855. }
  14856. } else {
  14857. var element = tag;
  14858. while (element && element.nodeType === 1) {
  14859. if (Dom.getElAttributes(element).hasOwnProperty('lang')) {
  14860. options.language = element.getAttribute('lang');
  14861. break;
  14862. }
  14863. element = element.parentNode;
  14864. }
  14865. }
  14866. }
  14867. // Run base component initializing with new options
  14868. // if the global option object was accidentally blown away by
  14869. // someone, bail early with an informative error
  14870. var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready));
  14871. if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
  14872. throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
  14873. }
  14874. // Store the original tag used to set options
  14875. _this.tag = tag;
  14876. // Store the tag attributes used to restore html5 element
  14877. _this.tagAttributes = tag && Dom.getElAttributes(tag);
  14878. // Update current language
  14879. _this.language(_this.options_.language);
  14880. // Update Supported Languages
  14881. if (options.languages) {
  14882. // Normalise player option languages to lowercase
  14883. var languagesToLower = {};
  14884. Object.getOwnPropertyNames(options.languages).forEach(function (name) {
  14885. languagesToLower[name.toLowerCase()] = options.languages[name];
  14886. });
  14887. _this.languages_ = languagesToLower;
  14888. } else {
  14889. _this.languages_ = Player.prototype.options_.languages;
  14890. }
  14891. // Cache for video property values.
  14892. _this.cache_ = {};
  14893. // Set poster
  14894. _this.poster_ = options.poster || '';
  14895. // Set controls
  14896. _this.controls_ = !!options.controls;
  14897. // Original tag settings stored in options
  14898. // now remove immediately so native controls don't flash.
  14899. // May be turned back on by HTML5 tech if nativeControlsForTouch is true
  14900. tag.controls = false;
  14901. /*
  14902. * Store the internal state of scrubbing
  14903. *
  14904. * @private
  14905. * @return {Boolean} True if the user is scrubbing
  14906. */
  14907. _this.scrubbing_ = false;
  14908. _this.el_ = _this.createEl();
  14909. // We also want to pass the original player options to each component and plugin
  14910. // as well so they don't need to reach back into the player for options later.
  14911. // We also need to do another copy of this.options_ so we don't end up with
  14912. // an infinite loop.
  14913. var playerOptionsCopy = (0, _mergeOptions2['default'])(_this.options_);
  14914. // Load plugins
  14915. if (options.plugins) {
  14916. var plugins = options.plugins;
  14917. Object.getOwnPropertyNames(plugins).forEach(function (name) {
  14918. if (typeof this[name] === 'function') {
  14919. this[name](plugins[name]);
  14920. } else {
  14921. _log2['default'].error('Unable to find plugin:', name);
  14922. }
  14923. }, _this);
  14924. }
  14925. _this.options_.playerOptions = playerOptionsCopy;
  14926. _this.initChildren();
  14927. // Set isAudio based on whether or not an audio tag was used
  14928. _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
  14929. // Update controls className. Can't do this when the controls are initially
  14930. // set because the element doesn't exist yet.
  14931. if (_this.controls()) {
  14932. _this.addClass('vjs-controls-enabled');
  14933. } else {
  14934. _this.addClass('vjs-controls-disabled');
  14935. }
  14936. // Set ARIA label and region role depending on player type
  14937. _this.el_.setAttribute('role', 'region');
  14938. if (_this.isAudio()) {
  14939. _this.el_.setAttribute('aria-label', 'audio player');
  14940. } else {
  14941. _this.el_.setAttribute('aria-label', 'video player');
  14942. }
  14943. if (_this.isAudio()) {
  14944. _this.addClass('vjs-audio');
  14945. }
  14946. if (_this.flexNotSupported_()) {
  14947. _this.addClass('vjs-no-flex');
  14948. }
  14949. // TODO: Make this smarter. Toggle user state between touching/mousing
  14950. // using events, since devices can have both touch and mouse events.
  14951. // if (browser.TOUCH_ENABLED) {
  14952. // this.addClass('vjs-touch-enabled');
  14953. // }
  14954. // iOS Safari has broken hover handling
  14955. if (!browser.IS_IOS) {
  14956. _this.addClass('vjs-workinghover');
  14957. }
  14958. // Make player easily findable by ID
  14959. Player.players[_this.id_] = _this;
  14960. // Add a major version class to aid css in plugins
  14961. var majorVersion = '5.20.3'.split('.')[0];
  14962. _this.addClass('vjs-v' + majorVersion);
  14963. // When the player is first initialized, trigger activity so components
  14964. // like the control bar show themselves if needed
  14965. _this.userActive(true);
  14966. _this.reportUserActivity();
  14967. _this.listenForUserActivity_();
  14968. _this.on('fullscreenchange', _this.handleFullscreenChange_);
  14969. _this.on('stageclick', _this.handleStageClick_);
  14970. return _this;
  14971. }
  14972. /**
  14973. * Destroys the video player and does any necessary cleanup.
  14974. *
  14975. * This is especially helpful if you are dynamically adding and removing videos
  14976. * to/from the DOM.
  14977. *
  14978. * @fires Player#dispose
  14979. */
  14980. Player.prototype.dispose = function dispose() {
  14981. /**
  14982. * Called when the player is being disposed of.
  14983. *
  14984. * @event Player#dispose
  14985. * @type {EventTarget~Event}
  14986. */
  14987. this.trigger('dispose');
  14988. // prevent dispose from being called twice
  14989. this.off('dispose');
  14990. if (this.styleEl_ && this.styleEl_.parentNode) {
  14991. this.styleEl_.parentNode.removeChild(this.styleEl_);
  14992. }
  14993. // Kill reference to this player
  14994. Player.players[this.id_] = null;
  14995. if (this.tag && this.tag.player) {
  14996. this.tag.player = null;
  14997. }
  14998. if (this.el_ && this.el_.player) {
  14999. this.el_.player = null;
  15000. }
  15001. if (this.tech_) {
  15002. this.tech_.dispose();
  15003. }
  15004. _Component.prototype.dispose.call(this);
  15005. };
  15006. /**
  15007. * Create the `Player`'s DOM element.
  15008. *
  15009. * @return {Element}
  15010. * The DOM element that gets created.
  15011. */
  15012. Player.prototype.createEl = function createEl() {
  15013. var tag = this.tag;
  15014. var el = void 0;
  15015. var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
  15016. if (playerElIngest) {
  15017. el = this.el_ = tag.parentNode;
  15018. } else {
  15019. el = this.el_ = _Component.prototype.createEl.call(this, 'div');
  15020. }
  15021. // set tabindex to -1 so we could focus on the player element
  15022. tag.setAttribute('tabindex', '-1');
  15023. // Remove width/height attrs from tag so CSS can make it 100% width/height
  15024. tag.removeAttribute('width');
  15025. tag.removeAttribute('height');
  15026. // Copy over all the attributes from the tag, including ID and class
  15027. // ID will now reference player box, not the video tag
  15028. var attrs = Dom.getElAttributes(tag);
  15029. Object.getOwnPropertyNames(attrs).forEach(function (attr) {
  15030. // workaround so we don't totally break IE7
  15031. // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
  15032. if (attr === 'class') {
  15033. el.className += ' ' + attrs[attr];
  15034. } else {
  15035. el.setAttribute(attr, attrs[attr]);
  15036. }
  15037. });
  15038. // Update tag id/class for use as HTML5 playback tech
  15039. // Might think we should do this after embedding in container so .vjs-tech class
  15040. // doesn't flash 100% width/height, but class only applies with .video-js parent
  15041. tag.playerId = tag.id;
  15042. tag.id += '_html5_api';
  15043. tag.className = 'vjs-tech';
  15044. // Make player findable on elements
  15045. tag.player = el.player = this;
  15046. // Default state of video is paused
  15047. this.addClass('vjs-paused');
  15048. // Add a style element in the player that we'll use to set the width/height
  15049. // of the player in a way that's still overrideable by CSS, just like the
  15050. // video element
  15051. if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) {
  15052. this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
  15053. var defaultsStyleEl = Dom.$('.vjs-styles-defaults');
  15054. var head = Dom.$('head');
  15055. head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
  15056. }
  15057. // Pass in the width/height/aspectRatio options which will update the style el
  15058. this.width(this.options_.width);
  15059. this.height(this.options_.height);
  15060. this.fluid(this.options_.fluid);
  15061. this.aspectRatio(this.options_.aspectRatio);
  15062. // Hide any links within the video/audio tag, because IE doesn't hide them completely.
  15063. var links = tag.getElementsByTagName('a');
  15064. for (var i = 0; i < links.length; i++) {
  15065. var linkEl = links.item(i);
  15066. Dom.addElClass(linkEl, 'vjs-hidden');
  15067. linkEl.setAttribute('hidden', 'hidden');
  15068. }
  15069. // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
  15070. // keep track of the original for later so we can know if the source originally failed
  15071. tag.initNetworkState_ = tag.networkState;
  15072. // Wrap video tag in div (el/box) container
  15073. if (tag.parentNode && !playerElIngest) {
  15074. tag.parentNode.insertBefore(el, tag);
  15075. }
  15076. // insert the tag as the first child of the player element
  15077. // then manually add it to the children array so that this.addChild
  15078. // will work properly for other components
  15079. //
  15080. // Breaks iPhone, fixed in HTML5 setup.
  15081. Dom.insertElFirst(tag, el);
  15082. this.children_.unshift(tag);
  15083. this.el_ = el;
  15084. return el;
  15085. };
  15086. /**
  15087. * A getter/setter for the `Player`'s width.
  15088. *
  15089. * @param {number} [value]
  15090. * The value to set the `Player's width to.
  15091. *
  15092. * @return {number}
  15093. * The current width of the `Player`.
  15094. */
  15095. Player.prototype.width = function width(value) {
  15096. return this.dimension('width', value);
  15097. };
  15098. /**
  15099. * A getter/setter for the `Player`'s height.
  15100. *
  15101. * @param {number} [value]
  15102. * The value to set the `Player's heigth to.
  15103. *
  15104. * @return {number}
  15105. * The current heigth of the `Player`.
  15106. */
  15107. Player.prototype.height = function height(value) {
  15108. return this.dimension('height', value);
  15109. };
  15110. /**
  15111. * A getter/setter for the `Player`'s width & height.
  15112. *
  15113. * @param {string} dimension
  15114. * This string can be:
  15115. * - 'width'
  15116. * - 'height'
  15117. *
  15118. * @param {number} [value]
  15119. * Value for dimension specified in the first argument.
  15120. *
  15121. * @return {Player|number}
  15122. * - Returns itself when setting; method can be chained.
  15123. * - The dimension arguments value when getting (width/height).
  15124. */
  15125. Player.prototype.dimension = function dimension(_dimension, value) {
  15126. var privDimension = _dimension + '_';
  15127. if (value === undefined) {
  15128. return this[privDimension] || 0;
  15129. }
  15130. if (value === '') {
  15131. // If an empty string is given, reset the dimension to be automatic
  15132. this[privDimension] = undefined;
  15133. } else {
  15134. var parsedVal = parseFloat(value);
  15135. if (isNaN(parsedVal)) {
  15136. _log2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension);
  15137. return this;
  15138. }
  15139. this[privDimension] = parsedVal;
  15140. }
  15141. this.updateStyleEl_();
  15142. return this;
  15143. };
  15144. /**
  15145. * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
  15146. *
  15147. * @param {boolean} [bool]
  15148. * - A value of true adds the class.
  15149. * - A value of false removes the class.
  15150. * - No value will toggle the fluid class.
  15151. *
  15152. * @return {boolean|undefined}
  15153. * - The value of fluid when getting.
  15154. * - `undefined` when setting.
  15155. */
  15156. Player.prototype.fluid = function fluid(bool) {
  15157. if (bool === undefined) {
  15158. return !!this.fluid_;
  15159. }
  15160. this.fluid_ = !!bool;
  15161. if (bool) {
  15162. this.addClass('vjs-fluid');
  15163. } else {
  15164. this.removeClass('vjs-fluid');
  15165. }
  15166. this.updateStyleEl_();
  15167. };
  15168. /**
  15169. * Get/Set the aspect ratio
  15170. *
  15171. * @param {string} [ratio]
  15172. * Aspect ratio for player
  15173. *
  15174. * @return {string|undefined}
  15175. * returns the current aspect ratio when getting
  15176. */
  15177. /**
  15178. * A getter/setter for the `Player`'s aspect ratio.
  15179. *
  15180. * @param {string} [ratio]
  15181. * The value to set the `Player's aspect ratio to.
  15182. *
  15183. * @return {string|undefined}
  15184. * - The current aspect ratio of the `Player` when getting.
  15185. * - undefined when setting
  15186. */
  15187. Player.prototype.aspectRatio = function aspectRatio(ratio) {
  15188. if (ratio === undefined) {
  15189. return this.aspectRatio_;
  15190. }
  15191. // Check for width:height format
  15192. if (!/^\d+\:\d+$/.test(ratio)) {
  15193. throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
  15194. }
  15195. this.aspectRatio_ = ratio;
  15196. // We're assuming if you set an aspect ratio you want fluid mode,
  15197. // because in fixed mode you could calculate width and height yourself.
  15198. this.fluid(true);
  15199. this.updateStyleEl_();
  15200. };
  15201. /**
  15202. * Update styles of the `Player` element (height, width and aspect ratio).
  15203. *
  15204. * @private
  15205. * @listens Tech#loadedmetadata
  15206. */
  15207. Player.prototype.updateStyleEl_ = function updateStyleEl_() {
  15208. if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE === true) {
  15209. var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
  15210. var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
  15211. var techEl = this.tech_ && this.tech_.el();
  15212. if (techEl) {
  15213. if (_width >= 0) {
  15214. techEl.width = _width;
  15215. }
  15216. if (_height >= 0) {
  15217. techEl.height = _height;
  15218. }
  15219. }
  15220. return;
  15221. }
  15222. var width = void 0;
  15223. var height = void 0;
  15224. var aspectRatio = void 0;
  15225. var idClass = void 0;
  15226. // The aspect ratio is either used directly or to calculate width and height.
  15227. if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
  15228. // Use any aspectRatio that's been specifically set
  15229. aspectRatio = this.aspectRatio_;
  15230. } else if (this.videoWidth() > 0) {
  15231. // Otherwise try to get the aspect ratio from the video metadata
  15232. aspectRatio = this.videoWidth() + ':' + this.videoHeight();
  15233. } else {
  15234. // Or use a default. The video element's is 2:1, but 16:9 is more common.
  15235. aspectRatio = '16:9';
  15236. }
  15237. // Get the ratio as a decimal we can use to calculate dimensions
  15238. var ratioParts = aspectRatio.split(':');
  15239. var ratioMultiplier = ratioParts[1] / ratioParts[0];
  15240. if (this.width_ !== undefined) {
  15241. // Use any width that's been specifically set
  15242. width = this.width_;
  15243. } else if (this.height_ !== undefined) {
  15244. // Or calulate the width from the aspect ratio if a height has been set
  15245. width = this.height_ / ratioMultiplier;
  15246. } else {
  15247. // Or use the video's metadata, or use the video el's default of 300
  15248. width = this.videoWidth() || 300;
  15249. }
  15250. if (this.height_ !== undefined) {
  15251. // Use any height that's been specifically set
  15252. height = this.height_;
  15253. } else {
  15254. // Otherwise calculate the height from the ratio and the width
  15255. height = width * ratioMultiplier;
  15256. }
  15257. // Ensure the CSS class is valid by starting with an alpha character
  15258. if (/^[^a-zA-Z]/.test(this.id())) {
  15259. idClass = 'dimensions-' + this.id();
  15260. } else {
  15261. idClass = this.id() + '-dimensions';
  15262. }
  15263. // Ensure the right class is still on the player for the style element
  15264. this.addClass(idClass);
  15265. stylesheet.setTextContent(this.styleEl_, '\n .' + idClass + ' {\n width: ' + width + 'px;\n height: ' + height + 'px;\n }\n\n .' + idClass + '.vjs-fluid {\n padding-top: ' + ratioMultiplier * 100 + '%;\n }\n ');
  15266. };
  15267. /**
  15268. * Load/Create an instance of playback {@link Tech} including element
  15269. * and API methods. Then append the `Tech` element in `Player` as a child.
  15270. *
  15271. * @param {string} techName
  15272. * name of the playback technology
  15273. *
  15274. * @param {string} source
  15275. * video source
  15276. *
  15277. * @private
  15278. */
  15279. Player.prototype.loadTech_ = function loadTech_(techName, source) {
  15280. var _this2 = this;
  15281. // Pause and remove current playback technology
  15282. if (this.tech_) {
  15283. this.unloadTech_();
  15284. }
  15285. // get rid of the HTML5 video tag as soon as we are using another tech
  15286. if (techName !== 'Html5' && this.tag) {
  15287. _tech2['default'].getTech('Html5').disposeMediaElement(this.tag);
  15288. this.tag.player = null;
  15289. this.tag = null;
  15290. }
  15291. this.techName_ = techName;
  15292. // Turn off API access because we're loading a new tech that might load asynchronously
  15293. this.isReady_ = false;
  15294. // Grab tech-specific options from player options and add source and parent element to use.
  15295. var techOptions = (0, _obj.assign)({
  15296. source: source,
  15297. 'nativeControlsForTouch': this.options_.nativeControlsForTouch,
  15298. 'playerId': this.id(),
  15299. 'techId': this.id() + '_' + techName + '_api',
  15300. 'videoTracks': this.videoTracks_,
  15301. 'textTracks': this.textTracks_,
  15302. 'audioTracks': this.audioTracks_,
  15303. 'autoplay': this.options_.autoplay,
  15304. 'playsinline': this.options_.playsinline,
  15305. 'preload': this.options_.preload,
  15306. 'loop': this.options_.loop,
  15307. 'muted': this.options_.muted,
  15308. 'poster': this.poster(),
  15309. 'language': this.language(),
  15310. 'playerElIngest': this.playerElIngest_ || false,
  15311. 'vtt.js': this.options_['vtt.js']
  15312. }, this.options_[techName.toLowerCase()]);
  15313. if (this.tag) {
  15314. techOptions.tag = this.tag;
  15315. }
  15316. if (source) {
  15317. this.currentType_ = source.type;
  15318. if (source.src === this.cache_.src && this.cache_.currentTime > 0) {
  15319. techOptions.startTime = this.cache_.currentTime;
  15320. }
  15321. this.cache_.sources = null;
  15322. this.cache_.source = source;
  15323. this.cache_.src = source.src;
  15324. }
  15325. // Initialize tech instance
  15326. var TechComponent = _tech2['default'].getTech(techName);
  15327. // Support old behavior of techs being registered as components.
  15328. // Remove once that deprecated behavior is removed.
  15329. if (!TechComponent) {
  15330. TechComponent = _component2['default'].getComponent(techName);
  15331. }
  15332. this.tech_ = new TechComponent(techOptions);
  15333. // player.triggerReady is always async, so don't need this to be async
  15334. this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);
  15335. _textTrackListConverter2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
  15336. // Listen to all HTML5-defined events and trigger them on the player
  15337. TECH_EVENTS_RETRIGGER.forEach(function (event) {
  15338. _this2.on(_this2.tech_, event, _this2['handleTech' + (0, _toTitleCase2['default'])(event) + '_']);
  15339. });
  15340. this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
  15341. this.on(this.tech_, 'waiting', this.handleTechWaiting_);
  15342. this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
  15343. this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
  15344. this.on(this.tech_, 'playing', this.handleTechPlaying_);
  15345. this.on(this.tech_, 'ended', this.handleTechEnded_);
  15346. this.on(this.tech_, 'seeking', this.handleTechSeeking_);
  15347. this.on(this.tech_, 'seeked', this.handleTechSeeked_);
  15348. this.on(this.tech_, 'play', this.handleTechPlay_);
  15349. this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
  15350. this.on(this.tech_, 'pause', this.handleTechPause_);
  15351. this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
  15352. this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
  15353. this.on(this.tech_, 'error', this.handleTechError_);
  15354. this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
  15355. this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
  15356. this.on(this.tech_, 'textdata', this.handleTechTextData_);
  15357. this.usingNativeControls(this.techGet_('controls'));
  15358. if (this.controls() && !this.usingNativeControls()) {
  15359. this.addTechControlsListeners_();
  15360. }
  15361. // Add the tech element in the DOM if it was not already there
  15362. // Make sure to not insert the original video element if using Html5
  15363. if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) {
  15364. Dom.insertElFirst(this.tech_.el(), this.el());
  15365. }
  15366. // Get rid of the original video tag reference after the first tech is loaded
  15367. if (this.tag) {
  15368. this.tag.player = null;
  15369. this.tag = null;
  15370. }
  15371. };
  15372. /**
  15373. * Unload and dispose of the current playback {@link Tech}.
  15374. *
  15375. * @private
  15376. */
  15377. Player.prototype.unloadTech_ = function unloadTech_() {
  15378. // Save the current text tracks so that we can reuse the same text tracks with the next tech
  15379. this.videoTracks_ = this.videoTracks();
  15380. this.textTracks_ = this.textTracks();
  15381. this.audioTracks_ = this.audioTracks();
  15382. this.textTracksJson_ = _textTrackListConverter2['default'].textTracksToJson(this.tech_);
  15383. this.isReady_ = false;
  15384. this.tech_.dispose();
  15385. this.tech_ = false;
  15386. };
  15387. /**
  15388. * Return a reference to the current {@link Tech}, but only if given an object with the
  15389. * `IWillNotUseThisInPlugins` property having a true value. This is try and prevent misuse
  15390. * of techs by plugins.
  15391. *
  15392. * @param {Object} safety
  15393. * An object that must contain `{IWillNotUseThisInPlugins: true}`
  15394. *
  15395. * @param {boolean} safety.IWillNotUseThisInPlugins
  15396. * Must be set to true or else this function will throw an error.
  15397. *
  15398. * @return {Tech}
  15399. * The Tech
  15400. */
  15401. Player.prototype.tech = function tech(safety) {
  15402. if (safety && safety.IWillNotUseThisInPlugins) {
  15403. return this.tech_;
  15404. }
  15405. var errorText = '\n Please make sure that you are not using this inside of a plugin.\n To disable this alert and error, please pass in an object with\n `IWillNotUseThisInPlugins` to the `tech` method. See\n https://github.com/videojs/video.js/issues/2617 for more info.\n ';
  15406. _window2['default'].alert(errorText);
  15407. throw new Error(errorText);
  15408. };
  15409. /**
  15410. * Set up click and touch listeners for the playback element
  15411. *
  15412. * - On desktops: a click on the video itself will toggle playback
  15413. * - On mobile devices: a click on the video toggles controls
  15414. * which is done by toggling the user state between active and
  15415. * inactive
  15416. * - A tap can signal that a user has become active or has become inactive
  15417. * e.g. a quick tap on an iPhone movie should reveal the controls. Another
  15418. * quick tap should hide them again (signaling the user is in an inactive
  15419. * viewing state)
  15420. * - In addition to this, we still want the user to be considered inactive after
  15421. * a few seconds of inactivity.
  15422. *
  15423. * > Note: the only part of iOS interaction we can't mimic with this setup
  15424. * is a touch and hold on the video element counting as activity in order to
  15425. * keep the controls showing, but that shouldn't be an issue. A touch and hold
  15426. * on any controls will still keep the user active
  15427. *
  15428. * @private
  15429. */
  15430. Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
  15431. // Make sure to remove all the previous listeners in case we are called multiple times.
  15432. this.removeTechControlsListeners_();
  15433. // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
  15434. // trigger mousedown/up.
  15435. // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
  15436. // Any touch events are set to block the mousedown event from happening
  15437. this.on(this.tech_, 'mousedown', this.handleTechClick_);
  15438. // If the controls were hidden we don't want that to change without a tap event
  15439. // so we'll check if the controls were already showing before reporting user
  15440. // activity
  15441. this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
  15442. this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
  15443. this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
  15444. // The tap listener needs to come after the touchend listener because the tap
  15445. // listener cancels out any reportedUserActivity when setting userActive(false)
  15446. this.on(this.tech_, 'tap', this.handleTechTap_);
  15447. };
  15448. /**
  15449. * Remove the listeners used for click and tap controls. This is needed for
  15450. * toggling to controls disabled, where a tap/touch should do nothing.
  15451. *
  15452. * @private
  15453. */
  15454. Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
  15455. // We don't want to just use `this.off()` because there might be other needed
  15456. // listeners added by techs that extend this.
  15457. this.off(this.tech_, 'tap', this.handleTechTap_);
  15458. this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
  15459. this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
  15460. this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
  15461. this.off(this.tech_, 'mousedown', this.handleTechClick_);
  15462. };
  15463. /**
  15464. * Player waits for the tech to be ready
  15465. *
  15466. * @private
  15467. */
  15468. Player.prototype.handleTechReady_ = function handleTechReady_() {
  15469. this.triggerReady();
  15470. // Keep the same volume as before
  15471. if (this.cache_.volume) {
  15472. this.techCall_('setVolume', this.cache_.volume);
  15473. }
  15474. // Look if the tech found a higher resolution poster while loading
  15475. this.handleTechPosterChange_();
  15476. // Update the duration if available
  15477. this.handleTechDurationChange_();
  15478. // Chrome and Safari both have issues with autoplay.
  15479. // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
  15480. // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
  15481. // This fixes both issues. Need to wait for API, so it updates displays correctly
  15482. if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
  15483. try {
  15484. // Chrome Fix. Fixed in Chrome v16.
  15485. delete this.tag.poster;
  15486. } catch (e) {
  15487. (0, _log2['default'])('deleting tag.poster throws in some browsers', e);
  15488. }
  15489. this.play();
  15490. }
  15491. };
  15492. /**
  15493. * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
  15494. * function will also trigger {@link Player#firstplay} if it is the first loadstart
  15495. * for a video.
  15496. *
  15497. * @fires Player#loadstart
  15498. * @fires Player#firstplay
  15499. * @listens Tech#loadstart
  15500. * @private
  15501. */
  15502. Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
  15503. // TODO: Update to use `emptied` event instead. See #1277.
  15504. this.removeClass('vjs-ended');
  15505. this.removeClass('vjs-seeking');
  15506. // reset the error state
  15507. this.error(null);
  15508. // If it's already playing we want to trigger a firstplay event now.
  15509. // The firstplay event relies on both the play and loadstart events
  15510. // which can happen in any order for a new source
  15511. if (!this.paused()) {
  15512. /**
  15513. * Fired when the user agent begins looking for media data
  15514. *
  15515. * @event Player#loadstart
  15516. * @type {EventTarget~Event}
  15517. */
  15518. this.trigger('loadstart');
  15519. this.trigger('firstplay');
  15520. } else {
  15521. // reset the hasStarted state
  15522. this.hasStarted(false);
  15523. this.trigger('loadstart');
  15524. }
  15525. };
  15526. /**
  15527. * Add/remove the vjs-has-started class
  15528. *
  15529. * @fires Player#firstplay
  15530. *
  15531. * @param {boolean} hasStarted
  15532. * - true: adds the class
  15533. * - false: remove the class
  15534. *
  15535. * @return {boolean}
  15536. * the boolean value of hasStarted
  15537. */
  15538. Player.prototype.hasStarted = function hasStarted(_hasStarted) {
  15539. if (_hasStarted !== undefined) {
  15540. // only update if this is a new value
  15541. if (this.hasStarted_ !== _hasStarted) {
  15542. this.hasStarted_ = _hasStarted;
  15543. if (_hasStarted) {
  15544. this.addClass('vjs-has-started');
  15545. // trigger the firstplay event if this newly has played
  15546. this.trigger('firstplay');
  15547. } else {
  15548. this.removeClass('vjs-has-started');
  15549. }
  15550. }
  15551. return this;
  15552. }
  15553. return !!this.hasStarted_;
  15554. };
  15555. /**
  15556. * Fired whenever the media begins or resumes playback
  15557. *
  15558. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
  15559. * @fires Player#play
  15560. * @listens Tech#play
  15561. * @private
  15562. */
  15563. Player.prototype.handleTechPlay_ = function handleTechPlay_() {
  15564. this.removeClass('vjs-ended');
  15565. this.removeClass('vjs-paused');
  15566. this.addClass('vjs-playing');
  15567. // hide the poster when the user hits play
  15568. this.hasStarted(true);
  15569. /**
  15570. * Triggered whenever an {@link Tech#play} event happens. Indicates that
  15571. * playback has started or resumed.
  15572. *
  15573. * @event Player#play
  15574. * @type {EventTarget~Event}
  15575. */
  15576. this.trigger('play');
  15577. };
  15578. /**
  15579. * Retrigger the `waiting` event that was triggered by the {@link Tech}.
  15580. *
  15581. * @fires Player#waiting
  15582. * @listens Tech#waiting
  15583. * @private
  15584. */
  15585. Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
  15586. var _this3 = this;
  15587. this.addClass('vjs-waiting');
  15588. /**
  15589. * A readyState change on the DOM element has caused playback to stop.
  15590. *
  15591. * @event Player#waiting
  15592. * @type {EventTarget~Event}
  15593. */
  15594. this.trigger('waiting');
  15595. this.one('timeupdate', function () {
  15596. return _this3.removeClass('vjs-waiting');
  15597. });
  15598. };
  15599. /**
  15600. * Retrigger the `canplay` event that was triggered by the {@link Tech}.
  15601. * > Note: This is not consistent between browsers. See #1351
  15602. *
  15603. * @fires Player#canplay
  15604. * @listens Tech#canplay
  15605. * @private
  15606. */
  15607. Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
  15608. this.removeClass('vjs-waiting');
  15609. /**
  15610. * The media has a readyState of HAVE_FUTURE_DATA or greater.
  15611. *
  15612. * @event Player#canplay
  15613. * @type {EventTarget~Event}
  15614. */
  15615. this.trigger('canplay');
  15616. };
  15617. /**
  15618. * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
  15619. *
  15620. * @fires Player#canplaythrough
  15621. * @listens Tech#canplaythrough
  15622. * @private
  15623. */
  15624. Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
  15625. this.removeClass('vjs-waiting');
  15626. /**
  15627. * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
  15628. * entire media file can be played without buffering.
  15629. *
  15630. * @event Player#canplaythrough
  15631. * @type {EventTarget~Event}
  15632. */
  15633. this.trigger('canplaythrough');
  15634. };
  15635. /**
  15636. * Retrigger the `playing` event that was triggered by the {@link Tech}.
  15637. *
  15638. * @fires Player#playing
  15639. * @listens Tech#playing
  15640. * @private
  15641. */
  15642. Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
  15643. this.removeClass('vjs-waiting');
  15644. /**
  15645. * The media is no longer blocked from playback, and has started playing.
  15646. *
  15647. * @event Player#playing
  15648. * @type {EventTarget~Event}
  15649. */
  15650. this.trigger('playing');
  15651. };
  15652. /**
  15653. * Retrigger the `seeking` event that was triggered by the {@link Tech}.
  15654. *
  15655. * @fires Player#seeking
  15656. * @listens Tech#seeking
  15657. * @private
  15658. */
  15659. Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
  15660. this.addClass('vjs-seeking');
  15661. /**
  15662. * Fired whenever the player is jumping to a new time
  15663. *
  15664. * @event Player#seeking
  15665. * @type {EventTarget~Event}
  15666. */
  15667. this.trigger('seeking');
  15668. };
  15669. /**
  15670. * Retrigger the `seeked` event that was triggered by the {@link Tech}.
  15671. *
  15672. * @fires Player#seeked
  15673. * @listens Tech#seeked
  15674. * @private
  15675. */
  15676. Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
  15677. this.removeClass('vjs-seeking');
  15678. /**
  15679. * Fired when the player has finished jumping to a new time
  15680. *
  15681. * @event Player#seeked
  15682. * @type {EventTarget~Event}
  15683. */
  15684. this.trigger('seeked');
  15685. };
  15686. /**
  15687. * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
  15688. *
  15689. * @fires Player#firstplay
  15690. * @listens Tech#firstplay
  15691. * @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated
  15692. * @private
  15693. */
  15694. Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
  15695. // If the first starttime attribute is specified
  15696. // then we will start at the given offset in seconds
  15697. if (this.options_.starttime) {
  15698. _log2['default'].warn('Passing the `starttime` option to the player will be deprecated in 6.0');
  15699. this.currentTime(this.options_.starttime);
  15700. }
  15701. this.addClass('vjs-has-started');
  15702. /**
  15703. * Fired the first time a video is played. Not part of the HLS spec, and this is
  15704. * probably not the best implementation yet, so use sparingly. If you don't have a
  15705. * reason to prevent playback, use `myPlayer.one('play');` instead.
  15706. *
  15707. * @event Player#firstplay
  15708. * @type {EventTarget~Event}
  15709. */
  15710. this.trigger('firstplay');
  15711. };
  15712. /**
  15713. * Retrigger the `pause` event that was triggered by the {@link Tech}.
  15714. *
  15715. * @fires Player#pause
  15716. * @listens Tech#pause
  15717. * @private
  15718. */
  15719. Player.prototype.handleTechPause_ = function handleTechPause_() {
  15720. this.removeClass('vjs-playing');
  15721. this.addClass('vjs-paused');
  15722. /**
  15723. * Fired whenever the media has been paused
  15724. *
  15725. * @event Player#pause
  15726. * @type {EventTarget~Event}
  15727. */
  15728. this.trigger('pause');
  15729. };
  15730. /**
  15731. * Retrigger the `ended` event that was triggered by the {@link Tech}.
  15732. *
  15733. * @fires Player#ended
  15734. * @listens Tech#ended
  15735. * @private
  15736. */
  15737. Player.prototype.handleTechEnded_ = function handleTechEnded_() {
  15738. this.addClass('vjs-ended');
  15739. if (this.options_.loop) {
  15740. this.currentTime(0);
  15741. this.play();
  15742. } else if (!this.paused()) {
  15743. this.pause();
  15744. }
  15745. /**
  15746. * Fired when the end of the media resource is reached (currentTime == duration)
  15747. *
  15748. * @event Player#ended
  15749. * @type {EventTarget~Event}
  15750. */
  15751. this.trigger('ended');
  15752. };
  15753. /**
  15754. * Fired when the duration of the media resource is first known or changed
  15755. *
  15756. * @listens Tech#durationchange
  15757. * @private
  15758. */
  15759. Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
  15760. this.duration(this.techGet_('duration'));
  15761. };
  15762. /**
  15763. * Handle a click on the media element to play/pause
  15764. *
  15765. * @param {EventTarget~Event} event
  15766. * the event that caused this function to trigger
  15767. *
  15768. * @listens Tech#mousedown
  15769. * @private
  15770. */
  15771. Player.prototype.handleTechClick_ = function handleTechClick_(event) {
  15772. // We're using mousedown to detect clicks thanks to Flash, but mousedown
  15773. // will also be triggered with right-clicks, so we need to prevent that
  15774. if (event.button !== 0) {
  15775. return;
  15776. }
  15777. // When controls are disabled a click should not toggle playback because
  15778. // the click is considered a control
  15779. if (this.controls()) {
  15780. if (this.paused()) {
  15781. this.play();
  15782. } else {
  15783. this.pause();
  15784. }
  15785. }
  15786. };
  15787. /**
  15788. * Handle a tap on the media element. It will toggle the user
  15789. * activity state, which hides and shows the controls.
  15790. *
  15791. * @listens Tech#tap
  15792. * @private
  15793. */
  15794. Player.prototype.handleTechTap_ = function handleTechTap_() {
  15795. this.userActive(!this.userActive());
  15796. };
  15797. /**
  15798. * Handle touch to start
  15799. *
  15800. * @listens Tech#touchstart
  15801. * @private
  15802. */
  15803. Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
  15804. this.userWasActive = this.userActive();
  15805. };
  15806. /**
  15807. * Handle touch to move
  15808. *
  15809. * @listens Tech#touchmove
  15810. * @private
  15811. */
  15812. Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
  15813. if (this.userWasActive) {
  15814. this.reportUserActivity();
  15815. }
  15816. };
  15817. /**
  15818. * Handle touch to end
  15819. *
  15820. * @param {EventTarget~Event} event
  15821. * the touchend event that triggered
  15822. * this function
  15823. *
  15824. * @listens Tech#touchend
  15825. * @private
  15826. */
  15827. Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
  15828. // Stop the mouse events from also happening
  15829. event.preventDefault();
  15830. };
  15831. /**
  15832. * Fired when the player switches in or out of fullscreen mode
  15833. *
  15834. * @private
  15835. * @listens Player#fullscreenchange
  15836. */
  15837. Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
  15838. if (this.isFullscreen()) {
  15839. this.addClass('vjs-fullscreen');
  15840. } else {
  15841. this.removeClass('vjs-fullscreen');
  15842. }
  15843. };
  15844. /**
  15845. * native click events on the SWF aren't triggered on IE11, Win8.1RT
  15846. * use stageclick events triggered from inside the SWF instead
  15847. *
  15848. * @private
  15849. * @listens stageclick
  15850. */
  15851. Player.prototype.handleStageClick_ = function handleStageClick_() {
  15852. this.reportUserActivity();
  15853. };
  15854. /**
  15855. * Handle Tech Fullscreen Change
  15856. *
  15857. * @param {EventTarget~Event} event
  15858. * the fullscreenchange event that triggered this function
  15859. *
  15860. * @param {Object} data
  15861. * the data that was sent with the event
  15862. *
  15863. * @private
  15864. * @listens Tech#fullscreenchange
  15865. * @fires Player#fullscreenchange
  15866. */
  15867. Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
  15868. if (data) {
  15869. this.isFullscreen(data.isFullscreen);
  15870. }
  15871. /**
  15872. * Fired when going in and out of fullscreen.
  15873. *
  15874. * @event Player#fullscreenchange
  15875. * @type {EventTarget~Event}
  15876. */
  15877. this.trigger('fullscreenchange');
  15878. };
  15879. /**
  15880. * Fires when an error occurred during the loading of an audio/video.
  15881. *
  15882. * @private
  15883. * @listens Tech#error
  15884. */
  15885. Player.prototype.handleTechError_ = function handleTechError_() {
  15886. var error = this.tech_.error();
  15887. this.error(error);
  15888. };
  15889. /**
  15890. * Retrigger the `textdata` event that was triggered by the {@link Tech}.
  15891. *
  15892. * @fires Player#textdata
  15893. * @listens Tech#textdata
  15894. * @private
  15895. */
  15896. Player.prototype.handleTechTextData_ = function handleTechTextData_() {
  15897. var data = null;
  15898. if (arguments.length > 1) {
  15899. data = arguments[1];
  15900. }
  15901. /**
  15902. * Fires when we get a textdata event from tech
  15903. *
  15904. * @event Player#textdata
  15905. * @type {EventTarget~Event}
  15906. */
  15907. this.trigger('textdata', data);
  15908. };
  15909. /**
  15910. * Get object for cached values.
  15911. *
  15912. * @return {Object}
  15913. * get the current object cache
  15914. */
  15915. Player.prototype.getCache = function getCache() {
  15916. return this.cache_;
  15917. };
  15918. /**
  15919. * Pass values to the playback tech
  15920. *
  15921. * @param {string} [method]
  15922. * the method to call
  15923. *
  15924. * @param {Object} arg
  15925. * the argument to pass
  15926. *
  15927. * @private
  15928. */
  15929. Player.prototype.techCall_ = function techCall_(method, arg) {
  15930. // If it's not ready yet, call method when it is
  15931. if (this.tech_ && !this.tech_.isReady_) {
  15932. this.tech_.ready(function () {
  15933. this[method](arg);
  15934. }, true);
  15935. // Otherwise call method now
  15936. } else {
  15937. try {
  15938. if (this.tech_) {
  15939. this.tech_[method](arg);
  15940. }
  15941. } catch (e) {
  15942. (0, _log2['default'])(e);
  15943. throw e;
  15944. }
  15945. }
  15946. };
  15947. /**
  15948. * Get calls can't wait for the tech, and sometimes don't need to.
  15949. *
  15950. * @param {string} method
  15951. * Tech method
  15952. *
  15953. * @return {Function|undefined}
  15954. * the method or undefined
  15955. *
  15956. * @private
  15957. */
  15958. Player.prototype.techGet_ = function techGet_(method) {
  15959. if (this.tech_ && this.tech_.isReady_) {
  15960. // Flash likes to die and reload when you hide or reposition it.
  15961. // In these cases the object methods go away and we get errors.
  15962. // When that happens we'll catch the errors and inform tech that it's not ready any more.
  15963. try {
  15964. return this.tech_[method]();
  15965. } catch (e) {
  15966. // When building additional tech libs, an expected method may not be defined yet
  15967. if (this.tech_[method] === undefined) {
  15968. (0, _log2['default'])('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
  15969. // When a method isn't available on the object it throws a TypeError
  15970. } else if (e.name === 'TypeError') {
  15971. (0, _log2['default'])('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
  15972. this.tech_.isReady_ = false;
  15973. } else {
  15974. (0, _log2['default'])(e);
  15975. }
  15976. throw e;
  15977. }
  15978. }
  15979. return;
  15980. };
  15981. /**
  15982. * start media playback
  15983. *
  15984. * @return {Player}
  15985. * A reference to the player object this function was called on
  15986. */
  15987. Player.prototype.play = function play() {
  15988. // Only calls the tech's play if we already have a src loaded
  15989. if (this.src() || this.currentSrc()) {
  15990. this.techCall_('play');
  15991. } else {
  15992. this.tech_.one('loadstart', function () {
  15993. this.play();
  15994. });
  15995. }
  15996. return this;
  15997. };
  15998. /**
  15999. * Pause the video playback
  16000. *
  16001. * @return {Player}
  16002. * A reference to the player object this function was called on
  16003. */
  16004. Player.prototype.pause = function pause() {
  16005. this.techCall_('pause');
  16006. return this;
  16007. };
  16008. /**
  16009. * Check if the player is paused or has yet to play
  16010. *
  16011. * @return {boolean}
  16012. * - false: if the media is currently playing
  16013. * - true: if media is not currently playing
  16014. */
  16015. Player.prototype.paused = function paused() {
  16016. // The initial state of paused should be true (in Safari it's actually false)
  16017. return this.techGet_('paused') === false ? false : true;
  16018. };
  16019. /**
  16020. * Returns whether or not the user is "scrubbing". Scrubbing is
  16021. * when the user has clicked the progress bar handle and is
  16022. * dragging it along the progress bar.
  16023. *
  16024. * @param {boolean} [isScrubbing]
  16025. * wether the user is or is not scrubbing
  16026. *
  16027. * @return {boolean|Player}
  16028. * A instance of the player that called this function when setting,
  16029. * and the value of scrubbing when getting
  16030. */
  16031. Player.prototype.scrubbing = function scrubbing(isScrubbing) {
  16032. if (isScrubbing !== undefined) {
  16033. this.scrubbing_ = !!isScrubbing;
  16034. if (isScrubbing) {
  16035. this.addClass('vjs-scrubbing');
  16036. } else {
  16037. this.removeClass('vjs-scrubbing');
  16038. }
  16039. return this;
  16040. }
  16041. return this.scrubbing_;
  16042. };
  16043. /**
  16044. * Get or set the current time (in seconds)
  16045. *
  16046. * @param {number|string} [seconds]
  16047. * The time to seek to in seconds
  16048. *
  16049. * @return {Player|number}
  16050. * - the current time in seconds when getting
  16051. * - a reference to the current player object when setting
  16052. */
  16053. Player.prototype.currentTime = function currentTime(seconds) {
  16054. if (seconds !== undefined) {
  16055. this.techCall_('setCurrentTime', seconds);
  16056. return this;
  16057. }
  16058. // cache last currentTime and return. default to 0 seconds
  16059. //
  16060. // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
  16061. // currentTime when scrubbing, but may not provide much performance benefit afterall.
  16062. // Should be tested. Also something has to read the actual current time or the cache will
  16063. // never get updated.
  16064. this.cache_.currentTime = this.techGet_('currentTime') || 0;
  16065. return this.cache_.currentTime;
  16066. };
  16067. /**
  16068. * Normally gets the length in time of the video in seconds;
  16069. * in all but the rarest use cases an argument will NOT be passed to the method
  16070. *
  16071. * > **NOTE**: The video must have started loading before the duration can be
  16072. * known, and in the case of Flash, may not be known until the video starts
  16073. * playing.
  16074. *
  16075. * @fires Player#durationchange
  16076. *
  16077. * @param {number} [seconds]
  16078. * The duration of the video to set in seconds
  16079. *
  16080. * @return {number|Player}
  16081. * - The duration of the video in seconds when getting
  16082. * - A reference to the player that called this function
  16083. * when setting
  16084. */
  16085. Player.prototype.duration = function duration(seconds) {
  16086. if (seconds === undefined) {
  16087. // return NaN if the duration is not known
  16088. return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
  16089. }
  16090. seconds = parseFloat(seconds);
  16091. // Standardize on Inifity for signaling video is live
  16092. if (seconds < 0) {
  16093. seconds = Infinity;
  16094. }
  16095. if (seconds !== this.cache_.duration) {
  16096. // Cache the last set value for optimized scrubbing (esp. Flash)
  16097. this.cache_.duration = seconds;
  16098. if (seconds === Infinity) {
  16099. this.addClass('vjs-live');
  16100. } else {
  16101. this.removeClass('vjs-live');
  16102. }
  16103. /**
  16104. * @event Player#durationchange
  16105. * @type {EventTarget~Event}
  16106. */
  16107. this.trigger('durationchange');
  16108. }
  16109. return this;
  16110. };
  16111. /**
  16112. * Calculates how much time is left in the video. Not part
  16113. * of the native video API.
  16114. *
  16115. * @return {number}
  16116. * The time remaining in seconds
  16117. */
  16118. Player.prototype.remainingTime = function remainingTime() {
  16119. return this.duration() - this.currentTime();
  16120. };
  16121. //
  16122. // Kind of like an array of portions of the video that have been downloaded.
  16123. /**
  16124. * Get a TimeRange object with an array of the times of the video
  16125. * that have been downloaded. If you just want the percent of the
  16126. * video that's been downloaded, use bufferedPercent.
  16127. *
  16128. * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
  16129. *
  16130. * @return {TimeRange}
  16131. * A mock TimeRange object (following HTML spec)
  16132. */
  16133. Player.prototype.buffered = function buffered() {
  16134. var buffered = this.techGet_('buffered');
  16135. if (!buffered || !buffered.length) {
  16136. buffered = (0, _timeRanges.createTimeRange)(0, 0);
  16137. }
  16138. return buffered;
  16139. };
  16140. /**
  16141. * Get the percent (as a decimal) of the video that's been downloaded.
  16142. * This method is not a part of the native HTML video API.
  16143. *
  16144. * @return {number}
  16145. * A decimal between 0 and 1 representing the percent
  16146. * that is bufferred 0 being 0% and 1 being 100%
  16147. */
  16148. Player.prototype.bufferedPercent = function bufferedPercent() {
  16149. return (0, _buffer.bufferedPercent)(this.buffered(), this.duration());
  16150. };
  16151. /**
  16152. * Get the ending time of the last buffered time range
  16153. * This is used in the progress bar to encapsulate all time ranges.
  16154. *
  16155. * @return {number}
  16156. * The end of the last buffered time range
  16157. */
  16158. Player.prototype.bufferedEnd = function bufferedEnd() {
  16159. var buffered = this.buffered();
  16160. var duration = this.duration();
  16161. var end = buffered.end(buffered.length - 1);
  16162. if (end > duration) {
  16163. end = duration;
  16164. }
  16165. return end;
  16166. };
  16167. /**
  16168. * Get or set the current volume of the media
  16169. *
  16170. * @param {number} [percentAsDecimal]
  16171. * The new volume as a decimal percent:
  16172. * - 0 is muted/0%/off
  16173. * - 1.0 is 100%/full
  16174. * - 0.5 is half volume or 50%
  16175. *
  16176. * @return {Player|number}
  16177. * a reference to the calling player when setting and the
  16178. * current volume as a percent when getting
  16179. */
  16180. Player.prototype.volume = function volume(percentAsDecimal) {
  16181. var vol = void 0;
  16182. if (percentAsDecimal !== undefined) {
  16183. // Force value to between 0 and 1
  16184. vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
  16185. this.cache_.volume = vol;
  16186. this.techCall_('setVolume', vol);
  16187. return this;
  16188. }
  16189. // Default to 1 when returning current volume.
  16190. vol = parseFloat(this.techGet_('volume'));
  16191. return isNaN(vol) ? 1 : vol;
  16192. };
  16193. /**
  16194. * Get the current muted state, or turn mute on or off
  16195. *
  16196. * @param {boolean} [muted]
  16197. * - true to mute
  16198. * - false to unmute
  16199. *
  16200. * @return {boolean|Player}
  16201. * - true if mute is on and getting
  16202. * - false if mute is off and getting
  16203. * - A reference to the current player when setting
  16204. */
  16205. Player.prototype.muted = function muted(_muted) {
  16206. if (_muted !== undefined) {
  16207. this.techCall_('setMuted', _muted);
  16208. return this;
  16209. }
  16210. return this.techGet_('muted') || false;
  16211. };
  16212. /**
  16213. * Check if current tech can support native fullscreen
  16214. * (e.g. with built in controls like iOS, so not our flash swf)
  16215. *
  16216. * @return {boolean}
  16217. * if native fullscreen is supported
  16218. */
  16219. Player.prototype.supportsFullScreen = function supportsFullScreen() {
  16220. return this.techGet_('supportsFullScreen') || false;
  16221. };
  16222. /**
  16223. * Check if the player is in fullscreen mode or tell the player that it
  16224. * is or is not in fullscreen mode.
  16225. *
  16226. * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
  16227. * property and instead document.fullscreenElement is used. But isFullscreen is
  16228. * still a valuable property for internal player workings.
  16229. *
  16230. * @param {boolean} [isFS]
  16231. * Set the players current fullscreen state
  16232. *
  16233. * @return {boolean|Player}
  16234. * - true if fullscreen is on and getting
  16235. * - false if fullscreen is off and getting
  16236. * - A reference to the current player when setting
  16237. */
  16238. Player.prototype.isFullscreen = function isFullscreen(isFS) {
  16239. if (isFS !== undefined) {
  16240. this.isFullscreen_ = !!isFS;
  16241. return this;
  16242. }
  16243. return !!this.isFullscreen_;
  16244. };
  16245. /**
  16246. * Increase the size of the video to full screen
  16247. * In some browsers, full screen is not supported natively, so it enters
  16248. * "full window mode", where the video fills the browser window.
  16249. * In browsers and devices that support native full screen, sometimes the
  16250. * browser's default controls will be shown, and not the Video.js custom skin.
  16251. * This includes most mobile devices (iOS, Android) and older versions of
  16252. * Safari.
  16253. *
  16254. * @fires Player#fullscreenchange
  16255. * @return {Player}
  16256. * A reference to the current player
  16257. */
  16258. Player.prototype.requestFullscreen = function requestFullscreen() {
  16259. var fsApi = _fullscreenApi2['default'];
  16260. this.isFullscreen(true);
  16261. if (fsApi.requestFullscreen) {
  16262. // the browser supports going fullscreen at the element level so we can
  16263. // take the controls fullscreen as well as the video
  16264. // Trigger fullscreenchange event after change
  16265. // We have to specifically add this each time, and remove
  16266. // when canceling fullscreen. Otherwise if there's multiple
  16267. // players on a page, they would all be reacting to the same fullscreen
  16268. // events
  16269. Events.on(_document2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) {
  16270. this.isFullscreen(_document2['default'][fsApi.fullscreenElement]);
  16271. // If cancelling fullscreen, remove event listener.
  16272. if (this.isFullscreen() === false) {
  16273. Events.off(_document2['default'], fsApi.fullscreenchange, documentFullscreenChange);
  16274. }
  16275. /**
  16276. * @event Player#fullscreenchange
  16277. * @type {EventTarget~Event}
  16278. */
  16279. this.trigger('fullscreenchange');
  16280. }));
  16281. this.el_[fsApi.requestFullscreen]();
  16282. } else if (this.tech_.supportsFullScreen()) {
  16283. // we can't take the video.js controls fullscreen but we can go fullscreen
  16284. // with native controls
  16285. this.techCall_('enterFullScreen');
  16286. } else {
  16287. // fullscreen isn't supported so we'll just stretch the video element to
  16288. // fill the viewport
  16289. this.enterFullWindow();
  16290. /**
  16291. * @event Player#fullscreenchange
  16292. * @type {EventTarget~Event}
  16293. */
  16294. this.trigger('fullscreenchange');
  16295. }
  16296. return this;
  16297. };
  16298. /**
  16299. * Return the video to its normal size after having been in full screen mode
  16300. *
  16301. * @fires Player#fullscreenchange
  16302. *
  16303. * @return {Player}
  16304. * A reference to the current player
  16305. */
  16306. Player.prototype.exitFullscreen = function exitFullscreen() {
  16307. var fsApi = _fullscreenApi2['default'];
  16308. this.isFullscreen(false);
  16309. // Check for browser element fullscreen support
  16310. if (fsApi.requestFullscreen) {
  16311. _document2['default'][fsApi.exitFullscreen]();
  16312. } else if (this.tech_.supportsFullScreen()) {
  16313. this.techCall_('exitFullScreen');
  16314. } else {
  16315. this.exitFullWindow();
  16316. /**
  16317. * @event Player#fullscreenchange
  16318. * @type {EventTarget~Event}
  16319. */
  16320. this.trigger('fullscreenchange');
  16321. }
  16322. return this;
  16323. };
  16324. /**
  16325. * When fullscreen isn't supported we can stretch the
  16326. * video container to as wide as the browser will let us.
  16327. *
  16328. * @fires Player#enterFullWindow
  16329. */
  16330. Player.prototype.enterFullWindow = function enterFullWindow() {
  16331. this.isFullWindow = true;
  16332. // Storing original doc overflow value to return to when fullscreen is off
  16333. this.docOrigOverflow = _document2['default'].documentElement.style.overflow;
  16334. // Add listener for esc key to exit fullscreen
  16335. Events.on(_document2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey));
  16336. // Hide any scroll bars
  16337. _document2['default'].documentElement.style.overflow = 'hidden';
  16338. // Apply fullscreen styles
  16339. Dom.addElClass(_document2['default'].body, 'vjs-full-window');
  16340. /**
  16341. * @event Player#enterFullWindow
  16342. * @type {EventTarget~Event}
  16343. */
  16344. this.trigger('enterFullWindow');
  16345. };
  16346. /**
  16347. * Check for call to either exit full window or
  16348. * full screen on ESC key
  16349. *
  16350. * @param {string} event
  16351. * Event to check for key press
  16352. */
  16353. Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
  16354. if (event.keyCode === 27) {
  16355. if (this.isFullscreen() === true) {
  16356. this.exitFullscreen();
  16357. } else {
  16358. this.exitFullWindow();
  16359. }
  16360. }
  16361. };
  16362. /**
  16363. * Exit full window
  16364. *
  16365. * @fires Player#exitFullWindow
  16366. */
  16367. Player.prototype.exitFullWindow = function exitFullWindow() {
  16368. this.isFullWindow = false;
  16369. Events.off(_document2['default'], 'keydown', this.fullWindowOnEscKey);
  16370. // Unhide scroll bars.
  16371. _document2['default'].documentElement.style.overflow = this.docOrigOverflow;
  16372. // Remove fullscreen styles
  16373. Dom.removeElClass(_document2['default'].body, 'vjs-full-window');
  16374. // Resize the box, controller, and poster to original sizes
  16375. // this.positionAll();
  16376. /**
  16377. * @event Player#exitFullWindow
  16378. * @type {EventTarget~Event}
  16379. */
  16380. this.trigger('exitFullWindow');
  16381. };
  16382. /**
  16383. * Check whether the player can play a given mimetype
  16384. *
  16385. * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
  16386. *
  16387. * @param {string} type
  16388. * The mimetype to check
  16389. *
  16390. * @return {string}
  16391. * 'probably', 'maybe', or '' (empty string)
  16392. */
  16393. Player.prototype.canPlayType = function canPlayType(type) {
  16394. var can = void 0;
  16395. // Loop through each playback technology in the options order
  16396. for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
  16397. var techName = (0, _toTitleCase2['default'])(j[i]);
  16398. var tech = _tech2['default'].getTech(techName);
  16399. // Support old behavior of techs being registered as components.
  16400. // Remove once that deprecated behavior is removed.
  16401. if (!tech) {
  16402. tech = _component2['default'].getComponent(techName);
  16403. }
  16404. // Check if the current tech is defined before continuing
  16405. if (!tech) {
  16406. _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
  16407. continue;
  16408. }
  16409. // Check if the browser supports this technology
  16410. if (tech.isSupported()) {
  16411. can = tech.canPlayType(type);
  16412. if (can) {
  16413. return can;
  16414. }
  16415. }
  16416. }
  16417. return '';
  16418. };
  16419. /**
  16420. * Select source based on tech-order or source-order
  16421. * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
  16422. * defaults to tech-order selection
  16423. *
  16424. * @param {Array} sources
  16425. * The sources for a media asset
  16426. *
  16427. * @return {Object|boolean}
  16428. * Object of source and tech order or false
  16429. */
  16430. Player.prototype.selectSource = function selectSource(sources) {
  16431. var _this4 = this;
  16432. // Get only the techs specified in `techOrder` that exist and are supported by the
  16433. // current platform
  16434. var techs = this.options_.techOrder.map(_toTitleCase2['default']).map(function (techName) {
  16435. // `Component.getComponent(...)` is for support of old behavior of techs
  16436. // being registered as components.
  16437. // Remove once that deprecated behavior is removed.
  16438. return [techName, _tech2['default'].getTech(techName) || _component2['default'].getComponent(techName)];
  16439. }).filter(function (_ref) {
  16440. var techName = _ref[0],
  16441. tech = _ref[1];
  16442. // Check if the current tech is defined before continuing
  16443. if (tech) {
  16444. // Check if the browser supports this technology
  16445. return tech.isSupported();
  16446. }
  16447. _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
  16448. return false;
  16449. });
  16450. // Iterate over each `innerArray` element once per `outerArray` element and execute
  16451. // `tester` with both. If `tester` returns a non-falsy value, exit early and return
  16452. // that value.
  16453. var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
  16454. var found = void 0;
  16455. outerArray.some(function (outerChoice) {
  16456. return innerArray.some(function (innerChoice) {
  16457. found = tester(outerChoice, innerChoice);
  16458. if (found) {
  16459. return true;
  16460. }
  16461. });
  16462. });
  16463. return found;
  16464. };
  16465. var foundSourceAndTech = void 0;
  16466. var flip = function flip(fn) {
  16467. return function (a, b) {
  16468. return fn(b, a);
  16469. };
  16470. };
  16471. var finder = function finder(_ref2, source) {
  16472. var techName = _ref2[0],
  16473. tech = _ref2[1];
  16474. if (tech.canPlaySource(source, _this4.options_[techName.toLowerCase()])) {
  16475. return { source: source, tech: techName };
  16476. }
  16477. };
  16478. // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
  16479. // to select from them based on their priority.
  16480. if (this.options_.sourceOrder) {
  16481. // Source-first ordering
  16482. foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
  16483. } else {
  16484. // Tech-first ordering
  16485. foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
  16486. }
  16487. return foundSourceAndTech || false;
  16488. };
  16489. /**
  16490. * The source function updates the video source
  16491. * There are three types of variables you can pass as the argument.
  16492. * **URL string**: A URL to the the video file. Use this method if you are sure
  16493. * the current playback technology (HTML5/Flash) can support the source you
  16494. * provide. Currently only MP4 files can be used in both HTML5 and Flash.
  16495. *
  16496. * @param {Tech~SourceObject|Tech~SourceObject[]} [source]
  16497. * One SourceObject or an array of SourceObjects
  16498. *
  16499. * @return {string|Player}
  16500. * - The current video source when getting
  16501. * - The player when setting
  16502. */
  16503. Player.prototype.src = function src(source) {
  16504. if (source === undefined) {
  16505. return this.techGet_('src');
  16506. }
  16507. var currentTech = _tech2['default'].getTech(this.techName_);
  16508. // Support old behavior of techs being registered as components.
  16509. // Remove once that deprecated behavior is removed.
  16510. if (!currentTech) {
  16511. currentTech = _component2['default'].getComponent(this.techName_);
  16512. }
  16513. // case: Array of source objects to choose from and pick the best to play
  16514. if (Array.isArray(source)) {
  16515. this.sourceList_(source);
  16516. // case: URL String (http://myvideo...)
  16517. } else if (typeof source === 'string') {
  16518. // create a source object from the string
  16519. this.src({ src: source });
  16520. // case: Source object { src: '', type: '' ... }
  16521. } else if (source instanceof Object) {
  16522. // check if the source has a type and the loaded tech cannot play the source
  16523. // if there's no type we'll just try the current tech
  16524. if (source.type && !currentTech.canPlaySource(source, this.options_[this.techName_.toLowerCase()])) {
  16525. // create a source list with the current source and send through
  16526. // the tech loop to check for a compatible technology
  16527. this.sourceList_([source]);
  16528. } else {
  16529. this.cache_.sources = null;
  16530. this.cache_.source = source;
  16531. this.cache_.src = source.src;
  16532. this.currentType_ = source.type || '';
  16533. // wait until the tech is ready to set the source
  16534. this.ready(function () {
  16535. // The setSource tech method was added with source handlers
  16536. // so older techs won't support it
  16537. // We need to check the direct prototype for the case where subclasses
  16538. // of the tech do not support source handlers
  16539. if (currentTech.prototype.hasOwnProperty('setSource')) {
  16540. this.techCall_('setSource', source);
  16541. } else {
  16542. this.techCall_('src', source.src);
  16543. }
  16544. if (this.options_.preload === 'auto') {
  16545. this.load();
  16546. }
  16547. if (this.options_.autoplay) {
  16548. this.play();
  16549. }
  16550. // Set the source synchronously if possible (#2326)
  16551. }, true);
  16552. }
  16553. }
  16554. return this;
  16555. };
  16556. /**
  16557. * Handle an array of source objects
  16558. *
  16559. * @param {Tech~SourceObject[]} sources
  16560. * Array of source objects
  16561. *
  16562. * @private
  16563. */
  16564. Player.prototype.sourceList_ = function sourceList_(sources) {
  16565. var sourceTech = this.selectSource(sources);
  16566. if (sourceTech) {
  16567. if (sourceTech.tech === this.techName_) {
  16568. // if this technology is already loaded, set the source
  16569. this.src(sourceTech.source);
  16570. } else {
  16571. // load this technology with the chosen source
  16572. this.loadTech_(sourceTech.tech, sourceTech.source);
  16573. }
  16574. this.cache_.sources = sources;
  16575. } else {
  16576. // We need to wrap this in a timeout to give folks a chance to add error event handlers
  16577. this.setTimeout(function () {
  16578. this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
  16579. }, 0);
  16580. // we could not find an appropriate tech, but let's still notify the delegate that this is it
  16581. // this needs a better comment about why this is needed
  16582. this.triggerReady();
  16583. }
  16584. };
  16585. /**
  16586. * Begin loading the src data.
  16587. *
  16588. * @return {Player}
  16589. * A reference to the player
  16590. */
  16591. Player.prototype.load = function load() {
  16592. this.techCall_('load');
  16593. return this;
  16594. };
  16595. /**
  16596. * Reset the player. Loads the first tech in the techOrder,
  16597. * and calls `reset` on the tech`.
  16598. *
  16599. * @return {Player}
  16600. * A reference to the player
  16601. */
  16602. Player.prototype.reset = function reset() {
  16603. this.loadTech_((0, _toTitleCase2['default'])(this.options_.techOrder[0]), null);
  16604. this.techCall_('reset');
  16605. return this;
  16606. };
  16607. /**
  16608. * Returns all of the current source objects.
  16609. *
  16610. * @return {Tech~SourceObject[]}
  16611. * The current source objects
  16612. */
  16613. Player.prototype.currentSources = function currentSources() {
  16614. var source = this.currentSource();
  16615. var sources = [];
  16616. // assume `{}` or `{ src }`
  16617. if (Object.keys(source).length !== 0) {
  16618. sources.push(source);
  16619. }
  16620. return this.cache_.sources || sources;
  16621. };
  16622. /**
  16623. * Returns the current source object.
  16624. *
  16625. * @return {Tech~SourceObject}
  16626. * The current source object
  16627. */
  16628. Player.prototype.currentSource = function currentSource() {
  16629. var source = {};
  16630. var src = this.currentSrc();
  16631. if (src) {
  16632. source.src = src;
  16633. }
  16634. return this.cache_.source || source;
  16635. };
  16636. /**
  16637. * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
  16638. * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
  16639. *
  16640. * @return {string}
  16641. * The current source
  16642. */
  16643. Player.prototype.currentSrc = function currentSrc() {
  16644. return this.techGet_('currentSrc') || this.cache_.src || '';
  16645. };
  16646. /**
  16647. * Get the current source type e.g. video/mp4
  16648. * This can allow you rebuild the current source object so that you could load the same
  16649. * source and tech later
  16650. *
  16651. * @return {string}
  16652. * The source MIME type
  16653. */
  16654. Player.prototype.currentType = function currentType() {
  16655. return this.currentType_ || '';
  16656. };
  16657. /**
  16658. * Get or set the preload attribute
  16659. *
  16660. * @param {boolean} [value]
  16661. * - true means that we should preload
  16662. * - false maens that we should not preload
  16663. *
  16664. * @return {string|Player}
  16665. * - the preload attribute value when getting
  16666. * - the player when setting
  16667. */
  16668. Player.prototype.preload = function preload(value) {
  16669. if (value !== undefined) {
  16670. this.techCall_('setPreload', value);
  16671. this.options_.preload = value;
  16672. return this;
  16673. }
  16674. return this.techGet_('preload');
  16675. };
  16676. /**
  16677. * Get or set the autoplay attribute.
  16678. *
  16679. * @param {boolean} [value]
  16680. * - true means that we should autoplay
  16681. * - false maens that we should not autoplay
  16682. *
  16683. * @return {string|Player}
  16684. * - the current value of autoplay
  16685. * - the player when setting
  16686. */
  16687. Player.prototype.autoplay = function autoplay(value) {
  16688. if (value !== undefined) {
  16689. this.techCall_('setAutoplay', value);
  16690. this.options_.autoplay = value;
  16691. return this;
  16692. }
  16693. return this.techGet_('autoplay', value);
  16694. };
  16695. /**
  16696. * Set or unset the playsinline attribute.
  16697. * Playsinline tells the browser that non-fullscreen playback is preferred.
  16698. *
  16699. * @param {boolean} [value]
  16700. * - true means that we should try to play inline by default
  16701. * - false means that we should use the browser's default playback mode,
  16702. * which in most cases is inline. iOS Safari is a notable exception
  16703. * and plays fullscreen by default.
  16704. *
  16705. * @return {string|Player}
  16706. * - the current value of playsinline
  16707. * - the player when setting
  16708. *
  16709. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  16710. */
  16711. Player.prototype.playsinline = function playsinline(value) {
  16712. if (value !== undefined) {
  16713. this.techCall_('setPlaysinline', value);
  16714. this.options_.playsinline = value;
  16715. return this;
  16716. }
  16717. return this.techGet_('playsinline');
  16718. };
  16719. /**
  16720. * Get or set the loop attribute on the video element.
  16721. *
  16722. * @param {boolean} [value]
  16723. * - true means that we should loop the video
  16724. * - false means that we should not loop the video
  16725. *
  16726. * @return {string|Player}
  16727. * - the current value of loop when getting
  16728. * - the player when setting
  16729. */
  16730. Player.prototype.loop = function loop(value) {
  16731. if (value !== undefined) {
  16732. this.techCall_('setLoop', value);
  16733. this.options_.loop = value;
  16734. return this;
  16735. }
  16736. return this.techGet_('loop');
  16737. };
  16738. /**
  16739. * Get or set the poster image source url
  16740. *
  16741. * @fires Player#posterchange
  16742. *
  16743. * @param {string} [src]
  16744. * Poster image source URL
  16745. *
  16746. * @return {string|Player}
  16747. * - the current value of poster when getting
  16748. * - the player when setting
  16749. */
  16750. Player.prototype.poster = function poster(src) {
  16751. if (src === undefined) {
  16752. return this.poster_;
  16753. }
  16754. // The correct way to remove a poster is to set as an empty string
  16755. // other falsey values will throw errors
  16756. if (!src) {
  16757. src = '';
  16758. }
  16759. // update the internal poster variable
  16760. this.poster_ = src;
  16761. // update the tech's poster
  16762. this.techCall_('setPoster', src);
  16763. // alert components that the poster has been set
  16764. /**
  16765. * This event fires when the poster image is changed on the player.
  16766. *
  16767. * @event Player#posterchange
  16768. * @type {EventTarget~Event}
  16769. */
  16770. this.trigger('posterchange');
  16771. return this;
  16772. };
  16773. /**
  16774. * Some techs (e.g. YouTube) can provide a poster source in an
  16775. * asynchronous way. We want the poster component to use this
  16776. * poster source so that it covers up the tech's controls.
  16777. * (YouTube's play button). However we only want to use this
  16778. * soruce if the player user hasn't set a poster through
  16779. * the normal APIs.
  16780. *
  16781. * @fires Player#posterchange
  16782. * @listens Tech#posterchange
  16783. * @private
  16784. */
  16785. Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
  16786. if (!this.poster_ && this.tech_ && this.tech_.poster) {
  16787. this.poster_ = this.tech_.poster() || '';
  16788. // Let components know the poster has changed
  16789. this.trigger('posterchange');
  16790. }
  16791. };
  16792. /**
  16793. * Get or set whether or not the controls are showing.
  16794. *
  16795. * @fires Player#controlsenabled
  16796. *
  16797. * @param {boolean} [bool]
  16798. * - true to turn controls on
  16799. * - false to turn controls off
  16800. *
  16801. * @return {boolean|Player}
  16802. * - the current value of controls when getting
  16803. * - the player when setting
  16804. */
  16805. Player.prototype.controls = function controls(bool) {
  16806. if (bool !== undefined) {
  16807. bool = !!bool;
  16808. // Don't trigger a change event unless it actually changed
  16809. if (this.controls_ !== bool) {
  16810. this.controls_ = bool;
  16811. if (this.usingNativeControls()) {
  16812. this.techCall_('setControls', bool);
  16813. }
  16814. if (bool) {
  16815. this.removeClass('vjs-controls-disabled');
  16816. this.addClass('vjs-controls-enabled');
  16817. /**
  16818. * @event Player#controlsenabled
  16819. * @type {EventTarget~Event}
  16820. */
  16821. this.trigger('controlsenabled');
  16822. if (!this.usingNativeControls()) {
  16823. this.addTechControlsListeners_();
  16824. }
  16825. } else {
  16826. this.removeClass('vjs-controls-enabled');
  16827. this.addClass('vjs-controls-disabled');
  16828. /**
  16829. * @event Player#controlsdisabled
  16830. * @type {EventTarget~Event}
  16831. */
  16832. this.trigger('controlsdisabled');
  16833. if (!this.usingNativeControls()) {
  16834. this.removeTechControlsListeners_();
  16835. }
  16836. }
  16837. }
  16838. return this;
  16839. }
  16840. return !!this.controls_;
  16841. };
  16842. /**
  16843. * Toggle native controls on/off. Native controls are the controls built into
  16844. * devices (e.g. default iPhone controls), Flash, or other techs
  16845. * (e.g. Vimeo Controls)
  16846. * **This should only be set by the current tech, because only the tech knows
  16847. * if it can support native controls**
  16848. *
  16849. * @fires Player#usingnativecontrols
  16850. * @fires Player#usingcustomcontrols
  16851. *
  16852. * @param {boolean} [bool]
  16853. * - true to turn native controls on
  16854. * - false to turn native controls off
  16855. *
  16856. * @return {boolean|Player}
  16857. * - the current value of native controls when getting
  16858. * - the player when setting
  16859. */
  16860. Player.prototype.usingNativeControls = function usingNativeControls(bool) {
  16861. if (bool !== undefined) {
  16862. bool = !!bool;
  16863. // Don't trigger a change event unless it actually changed
  16864. if (this.usingNativeControls_ !== bool) {
  16865. this.usingNativeControls_ = bool;
  16866. if (bool) {
  16867. this.addClass('vjs-using-native-controls');
  16868. /**
  16869. * player is using the native device controls
  16870. *
  16871. * @event Player#usingnativecontrols
  16872. * @type {EventTarget~Event}
  16873. */
  16874. this.trigger('usingnativecontrols');
  16875. } else {
  16876. this.removeClass('vjs-using-native-controls');
  16877. /**
  16878. * player is using the custom HTML controls
  16879. *
  16880. * @event Player#usingcustomcontrols
  16881. * @type {EventTarget~Event}
  16882. */
  16883. this.trigger('usingcustomcontrols');
  16884. }
  16885. }
  16886. return this;
  16887. }
  16888. return !!this.usingNativeControls_;
  16889. };
  16890. /**
  16891. * Set or get the current MediaError
  16892. *
  16893. * @fires Player#error
  16894. *
  16895. * @param {MediaError|string|number} [err]
  16896. * A MediaError or a string/number to be turned
  16897. * into a MediaError
  16898. *
  16899. * @return {MediaError|null|Player}
  16900. * - The current MediaError when getting (or null)
  16901. * - The player when setting
  16902. */
  16903. Player.prototype.error = function error(err) {
  16904. if (err === undefined) {
  16905. return this.error_ || null;
  16906. }
  16907. // restoring to default
  16908. if (err === null) {
  16909. this.error_ = err;
  16910. this.removeClass('vjs-error');
  16911. if (this.errorDisplay) {
  16912. this.errorDisplay.close();
  16913. }
  16914. return this;
  16915. }
  16916. this.error_ = new _mediaError2['default'](err);
  16917. // add the vjs-error classname to the player
  16918. this.addClass('vjs-error');
  16919. // log the name of the error type and any message
  16920. // ie8 just logs "[object object]" if you just log the error object
  16921. _log2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaError2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
  16922. /**
  16923. * @event Player#error
  16924. * @type {EventTarget~Event}
  16925. */
  16926. this.trigger('error');
  16927. return this;
  16928. };
  16929. /**
  16930. * Report user activity
  16931. *
  16932. * @param {Object} event
  16933. * Event object
  16934. */
  16935. Player.prototype.reportUserActivity = function reportUserActivity(event) {
  16936. this.userActivity_ = true;
  16937. };
  16938. /**
  16939. * Get/set if user is active
  16940. *
  16941. * @fires Player#useractive
  16942. * @fires Player#userinactive
  16943. *
  16944. * @param {boolean} [bool]
  16945. * - true if the user is active
  16946. * - false if the user is inactive
  16947. * @return {boolean|Player}
  16948. * - the current value of userActive when getting
  16949. * - the player when setting
  16950. */
  16951. Player.prototype.userActive = function userActive(bool) {
  16952. if (bool !== undefined) {
  16953. bool = !!bool;
  16954. if (bool !== this.userActive_) {
  16955. this.userActive_ = bool;
  16956. if (bool) {
  16957. // If the user was inactive and is now active we want to reset the
  16958. // inactivity timer
  16959. this.userActivity_ = true;
  16960. this.removeClass('vjs-user-inactive');
  16961. this.addClass('vjs-user-active');
  16962. /**
  16963. * @event Player#useractive
  16964. * @type {EventTarget~Event}
  16965. */
  16966. this.trigger('useractive');
  16967. } else {
  16968. // We're switching the state to inactive manually, so erase any other
  16969. // activity
  16970. this.userActivity_ = false;
  16971. // Chrome/Safari/IE have bugs where when you change the cursor it can
  16972. // trigger a mousemove event. This causes an issue when you're hiding
  16973. // the cursor when the user is inactive, and a mousemove signals user
  16974. // activity. Making it impossible to go into inactive mode. Specifically
  16975. // this happens in fullscreen when we really need to hide the cursor.
  16976. //
  16977. // When this gets resolved in ALL browsers it can be removed
  16978. // https://code.google.com/p/chromium/issues/detail?id=103041
  16979. if (this.tech_) {
  16980. this.tech_.one('mousemove', function (e) {
  16981. e.stopPropagation();
  16982. e.preventDefault();
  16983. });
  16984. }
  16985. this.removeClass('vjs-user-active');
  16986. this.addClass('vjs-user-inactive');
  16987. /**
  16988. * @event Player#userinactive
  16989. * @type {EventTarget~Event}
  16990. */
  16991. this.trigger('userinactive');
  16992. }
  16993. }
  16994. return this;
  16995. }
  16996. return this.userActive_;
  16997. };
  16998. /**
  16999. * Listen for user activity based on timeout value
  17000. *
  17001. * @private
  17002. */
  17003. Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
  17004. var mouseInProgress = void 0;
  17005. var lastMoveX = void 0;
  17006. var lastMoveY = void 0;
  17007. var handleActivity = Fn.bind(this, this.reportUserActivity);
  17008. var handleMouseMove = function handleMouseMove(e) {
  17009. // #1068 - Prevent mousemove spamming
  17010. // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
  17011. if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
  17012. lastMoveX = e.screenX;
  17013. lastMoveY = e.screenY;
  17014. handleActivity();
  17015. }
  17016. };
  17017. var handleMouseDown = function handleMouseDown() {
  17018. handleActivity();
  17019. // For as long as the they are touching the device or have their mouse down,
  17020. // we consider them active even if they're not moving their finger or mouse.
  17021. // So we want to continue to update that they are active
  17022. this.clearInterval(mouseInProgress);
  17023. // Setting userActivity=true now and setting the interval to the same time
  17024. // as the activityCheck interval (250) should ensure we never miss the
  17025. // next activityCheck
  17026. mouseInProgress = this.setInterval(handleActivity, 250);
  17027. };
  17028. var handleMouseUp = function handleMouseUp(event) {
  17029. handleActivity();
  17030. // Stop the interval that maintains activity if the mouse/touch is down
  17031. this.clearInterval(mouseInProgress);
  17032. };
  17033. // Any mouse movement will be considered user activity
  17034. this.on('mousedown', handleMouseDown);
  17035. this.on('mousemove', handleMouseMove);
  17036. this.on('mouseup', handleMouseUp);
  17037. // Listen for keyboard navigation
  17038. // Shouldn't need to use inProgress interval because of key repeat
  17039. this.on('keydown', handleActivity);
  17040. this.on('keyup', handleActivity);
  17041. // Run an interval every 250 milliseconds instead of stuffing everything into
  17042. // the mousemove/touchmove function itself, to prevent performance degradation.
  17043. // `this.reportUserActivity` simply sets this.userActivity_ to true, which
  17044. // then gets picked up by this loop
  17045. // http://ejohn.org/blog/learning-from-twitter/
  17046. var inactivityTimeout = void 0;
  17047. this.setInterval(function () {
  17048. // Check to see if mouse/touch activity has happened
  17049. if (this.userActivity_) {
  17050. // Reset the activity tracker
  17051. this.userActivity_ = false;
  17052. // If the user state was inactive, set the state to active
  17053. this.userActive(true);
  17054. // Clear any existing inactivity timeout to start the timer over
  17055. this.clearTimeout(inactivityTimeout);
  17056. var timeout = this.options_.inactivityTimeout;
  17057. if (timeout > 0) {
  17058. // In <timeout> milliseconds, if no more activity has occurred the
  17059. // user will be considered inactive
  17060. inactivityTimeout = this.setTimeout(function () {
  17061. // Protect against the case where the inactivityTimeout can trigger just
  17062. // before the next user activity is picked up by the activity check loop
  17063. // causing a flicker
  17064. if (!this.userActivity_) {
  17065. this.userActive(false);
  17066. }
  17067. }, timeout);
  17068. }
  17069. }
  17070. }, 250);
  17071. };
  17072. /**
  17073. * Gets or sets the current playback rate. A playback rate of
  17074. * 1.0 represents normal speed and 0.5 would indicate half-speed
  17075. * playback, for instance.
  17076. *
  17077. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
  17078. *
  17079. * @param {number} [rate]
  17080. * New playback rate to set.
  17081. *
  17082. * @return {number|Player}
  17083. * - The current playback rate when getting or 1.0
  17084. * - the player when setting
  17085. */
  17086. Player.prototype.playbackRate = function playbackRate(rate) {
  17087. if (rate !== undefined) {
  17088. this.techCall_('setPlaybackRate', rate);
  17089. return this;
  17090. }
  17091. if (this.tech_ && this.tech_.featuresPlaybackRate) {
  17092. return this.techGet_('playbackRate');
  17093. }
  17094. return 1.0;
  17095. };
  17096. /**
  17097. * Gets or sets the audio flag
  17098. *
  17099. * @param {boolean} bool
  17100. * - true signals that this is an audio player
  17101. * - false signals that this is not an audio player
  17102. *
  17103. * @return {Player|boolean}
  17104. * - the current value of isAudio when getting
  17105. * - the player if setting
  17106. */
  17107. Player.prototype.isAudio = function isAudio(bool) {
  17108. if (bool !== undefined) {
  17109. this.isAudio_ = !!bool;
  17110. return this;
  17111. }
  17112. return !!this.isAudio_;
  17113. };
  17114. /**
  17115. * Get the {@link VideoTrackList}
  17116. *
  17117. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
  17118. *
  17119. * @return {VideoTrackList}
  17120. * the current video track list
  17121. */
  17122. Player.prototype.videoTracks = function videoTracks() {
  17123. // if we have not yet loadTech_, we create videoTracks_
  17124. // these will be passed to the tech during loading
  17125. if (!this.tech_) {
  17126. this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default']();
  17127. return this.videoTracks_;
  17128. }
  17129. return this.tech_.videoTracks();
  17130. };
  17131. /**
  17132. * Get the {@link AudioTrackList}
  17133. *
  17134. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
  17135. *
  17136. * @return {AudioTrackList}
  17137. * the current audio track list
  17138. */
  17139. Player.prototype.audioTracks = function audioTracks() {
  17140. // if we have not yet loadTech_, we create videoTracks_
  17141. // these will be passed to the tech during loading
  17142. if (!this.tech_) {
  17143. this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default']();
  17144. return this.audioTracks_;
  17145. }
  17146. return this.tech_.audioTracks();
  17147. };
  17148. /**
  17149. * Get the {@link TextTrackList}
  17150. *
  17151. * Text tracks are tracks of timed text events.
  17152. * - Captions: text displayed over the video
  17153. * for the hearing impaired
  17154. * - Subtitles: text displayed over the video for
  17155. * those who don't understand language in the video
  17156. * - Chapters: text displayed in a menu allowing the user to jump
  17157. * to particular points (chapters) in the video
  17158. * - Descriptions: (not yet implemented) audio descriptions that are read back to
  17159. * the user by a screen reading device
  17160. *
  17161. * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
  17162. *
  17163. * @return {TextTrackList|undefined}
  17164. * The current TextTrackList or undefined if
  17165. * or undefined if we don't have a tech
  17166. */
  17167. Player.prototype.textTracks = function textTracks() {
  17168. // cannot use techGet_ directly because it checks to see whether the tech is ready.
  17169. // Flash is unlikely to be ready in time but textTracks should still work.
  17170. if (this.tech_) {
  17171. return this.tech_.textTracks();
  17172. }
  17173. };
  17174. /**
  17175. * Get the "remote" {@link TextTrackList}. Remote Text Tracks
  17176. * are tracks that were added to the HTML video element and can
  17177. * be removed, whereas normal texttracks cannot be removed.
  17178. *
  17179. *
  17180. * @return {TextTrackList|undefined}
  17181. * The current remote text track list or undefined
  17182. * if we don't have a tech
  17183. */
  17184. Player.prototype.remoteTextTracks = function remoteTextTracks() {
  17185. if (this.tech_) {
  17186. return this.tech_.remoteTextTracks();
  17187. }
  17188. };
  17189. /**
  17190. * Get the "remote" {@link HTMLTrackElementList}.
  17191. * This gives the user all of the DOM elements that match up
  17192. * with the remote {@link TextTrackList}.
  17193. *
  17194. * @return {HTMLTrackElementList}
  17195. * The current remote text track list elements
  17196. * or undefined if we don't have a tech
  17197. */
  17198. Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
  17199. if (this.tech_) {
  17200. return this.tech_.remoteTextTrackEls();
  17201. }
  17202. };
  17203. /**
  17204. * A helper method for adding a {@link TextTrack} to our
  17205. * {@link TextTrackList}.
  17206. *
  17207. * In addition to the W3C settings we allow adding additional info through options.
  17208. *
  17209. * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
  17210. *
  17211. * @param {string} [kind]
  17212. * the kind of TextTrack you are adding
  17213. *
  17214. * @param {string} [label]
  17215. * the label to give the TextTrack label
  17216. *
  17217. * @param {string} [language]
  17218. * the language to set on the TextTrack
  17219. *
  17220. * @return {TextTrack|undefined}
  17221. * the TextTrack that was added or undefined
  17222. * if there is no tech
  17223. */
  17224. Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  17225. if (this.tech_) {
  17226. return this.tech_.addTextTrack(kind, label, language);
  17227. }
  17228. };
  17229. /**
  17230. * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
  17231. * automatically removed from the video element whenever the source changes, unless
  17232. * manualCleanup is set to false.
  17233. *
  17234. * @param {Object} options
  17235. * Options to pass to {@link HTMLTrackElement} during creation. See
  17236. * {@link HTMLTrackElement} for object properties that you should use.
  17237. *
  17238. * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
  17239. *
  17240. * @return {HTMLTrackElement}
  17241. * the HTMLTrackElement that was created and added
  17242. * to the HTMLTrackElementList and the remote
  17243. * TextTrackList
  17244. *
  17245. * @deprecated The default value of the "manualCleanup" parameter will default
  17246. * to "false" in upcoming versions of Video.js
  17247. */
  17248. Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
  17249. if (this.tech_) {
  17250. return this.tech_.addRemoteTextTrack(options, manualCleanup);
  17251. }
  17252. };
  17253. /**
  17254. * Remove a remote {@link TextTrack} from the respective
  17255. * {@link TextTrackList} and {@link HTMLTrackElementList}.
  17256. *
  17257. * @param {Object} track
  17258. * Remote {@link TextTrack} to remove
  17259. *
  17260. * @return {undefined}
  17261. * does not return anything
  17262. */
  17263. Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
  17264. var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
  17265. _ref3$track = _ref3.track,
  17266. track = _ref3$track === undefined ? arguments[0] : _ref3$track;
  17267. // destructure the input into an object with a track argument, defaulting to arguments[0]
  17268. // default the whole argument to an empty object if nothing was passed in
  17269. if (this.tech_) {
  17270. return this.tech_.removeRemoteTextTrack(track);
  17271. }
  17272. };
  17273. /**
  17274. * Gets available media playback quality metrics as specified by the W3C's Media
  17275. * Playback Quality API.
  17276. *
  17277. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  17278. *
  17279. * @return {Object|undefined}
  17280. * An object with supported media playback quality metrics or undefined if there
  17281. * is no tech or the tech does not support it.
  17282. */
  17283. Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  17284. return this.techGet_('getVideoPlaybackQuality');
  17285. };
  17286. /**
  17287. * Get video width
  17288. *
  17289. * @return {number}
  17290. * current video width
  17291. */
  17292. Player.prototype.videoWidth = function videoWidth() {
  17293. return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
  17294. };
  17295. /**
  17296. * Get video height
  17297. *
  17298. * @return {number}
  17299. * current video height
  17300. */
  17301. Player.prototype.videoHeight = function videoHeight() {
  17302. return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
  17303. };
  17304. // Methods to add support for
  17305. // initialTime: function() { return this.techCall_('initialTime'); },
  17306. // startOffsetTime: function() { return this.techCall_('startOffsetTime'); },
  17307. // played: function() { return this.techCall_('played'); },
  17308. // defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); },
  17309. // defaultMuted: function() { return this.techCall_('defaultMuted'); }
  17310. /**
  17311. * The player's language code
  17312. * NOTE: The language should be set in the player options if you want the
  17313. * the controls to be built with a specific language. Changing the lanugage
  17314. * later will not update controls text.
  17315. *
  17316. * @param {string} [code]
  17317. * the language code to set the player to
  17318. *
  17319. * @return {string|Player}
  17320. * - The current language code when getting
  17321. * - A reference to the player when setting
  17322. */
  17323. Player.prototype.language = function language(code) {
  17324. if (code === undefined) {
  17325. return this.language_;
  17326. }
  17327. this.language_ = String(code).toLowerCase();
  17328. return this;
  17329. };
  17330. /**
  17331. * Get the player's language dictionary
  17332. * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
  17333. * Languages specified directly in the player options have precedence
  17334. *
  17335. * @return {Array}
  17336. * An array of of supported languages
  17337. */
  17338. Player.prototype.languages = function languages() {
  17339. return (0, _mergeOptions2['default'])(Player.prototype.options_.languages, this.languages_);
  17340. };
  17341. /**
  17342. * returns a JavaScript object reperesenting the current track
  17343. * information. **DOES not return it as JSON**
  17344. *
  17345. * @return {Object}
  17346. * Object representing the current of track info
  17347. */
  17348. Player.prototype.toJSON = function toJSON() {
  17349. var options = (0, _mergeOptions2['default'])(this.options_);
  17350. var tracks = options.tracks;
  17351. options.tracks = [];
  17352. for (var i = 0; i < tracks.length; i++) {
  17353. var track = tracks[i];
  17354. // deep merge tracks and null out player so no circular references
  17355. track = (0, _mergeOptions2['default'])(track);
  17356. track.player = undefined;
  17357. options.tracks[i] = track;
  17358. }
  17359. return options;
  17360. };
  17361. /**
  17362. * Creates a simple modal dialog (an instance of the {@link ModalDialog}
  17363. * component) that immediately overlays the player with arbitrary
  17364. * content and removes itself when closed.
  17365. *
  17366. * @param {string|Function|Element|Array|null} content
  17367. * Same as {@link ModalDialog#content}'s param of the same name.
  17368. * The most straight-forward usage is to provide a string or DOM
  17369. * element.
  17370. *
  17371. * @param {Object} [options]
  17372. * Extra options which will be passed on to the {@link ModalDialog}.
  17373. *
  17374. * @return {ModalDialog}
  17375. * the {@link ModalDialog} that was created
  17376. */
  17377. Player.prototype.createModal = function createModal(content, options) {
  17378. var _this5 = this;
  17379. options = options || {};
  17380. options.content = content || '';
  17381. var modal = new _modalDialog2['default'](this, options);
  17382. this.addChild(modal);
  17383. modal.on('dispose', function () {
  17384. _this5.removeChild(modal);
  17385. });
  17386. return modal.open();
  17387. };
  17388. /**
  17389. * Gets tag settings
  17390. *
  17391. * @param {Element} tag
  17392. * The player tag
  17393. *
  17394. * @return {Object}
  17395. * An object containing all of the settings
  17396. * for a player tag
  17397. */
  17398. Player.getTagSettings = function getTagSettings(tag) {
  17399. var baseOptions = {
  17400. sources: [],
  17401. tracks: []
  17402. };
  17403. var tagOptions = Dom.getElAttributes(tag);
  17404. var dataSetup = tagOptions['data-setup'];
  17405. if (Dom.hasElClass(tag, 'vjs-fluid')) {
  17406. tagOptions.fluid = true;
  17407. }
  17408. // Check if data-setup attr exists.
  17409. if (dataSetup !== null) {
  17410. // Parse options JSON
  17411. // If empty string, make it a parsable json object.
  17412. var _safeParseTuple = (0, _tuple2['default'])(dataSetup || '{}'),
  17413. err = _safeParseTuple[0],
  17414. data = _safeParseTuple[1];
  17415. if (err) {
  17416. _log2['default'].error(err);
  17417. }
  17418. (0, _obj.assign)(tagOptions, data);
  17419. }
  17420. (0, _obj.assign)(baseOptions, tagOptions);
  17421. // Get tag children settings
  17422. if (tag.hasChildNodes()) {
  17423. var children = tag.childNodes;
  17424. for (var i = 0, j = children.length; i < j; i++) {
  17425. var child = children[i];
  17426. // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
  17427. var childName = child.nodeName.toLowerCase();
  17428. if (childName === 'source') {
  17429. baseOptions.sources.push(Dom.getElAttributes(child));
  17430. } else if (childName === 'track') {
  17431. baseOptions.tracks.push(Dom.getElAttributes(child));
  17432. }
  17433. }
  17434. }
  17435. return baseOptions;
  17436. };
  17437. /**
  17438. * Determine wether or not flexbox is supported
  17439. *
  17440. * @return {boolean}
  17441. * - true if flexbox is supported
  17442. * - false if flexbox is not supported
  17443. */
  17444. Player.prototype.flexNotSupported_ = function flexNotSupported_() {
  17445. var elem = _document2['default'].createElement('i');
  17446. // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
  17447. // common flex features that we can rely on when checking for flex support.
  17448. return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
  17449. // IE10-specific (2012 flex spec)
  17450. 'msFlexOrder' in elem.style);
  17451. };
  17452. return Player;
  17453. }(_component2['default']);
  17454. /**
  17455. * Global player list
  17456. *
  17457. * @type {Object}
  17458. */
  17459. Player.players = {};
  17460. var navigator = _window2['default'].navigator;
  17461. /*
  17462. * Player instance options, surfaced using options
  17463. * options = Player.prototype.options_
  17464. * Make changes in options, not here.
  17465. *
  17466. * @type {Object}
  17467. * @private
  17468. */
  17469. Player.prototype.options_ = {
  17470. // Default order of fallback technology
  17471. techOrder: ['html5', 'flash'],
  17472. // techOrder: ['flash','html5'],
  17473. html5: {},
  17474. flash: {},
  17475. // defaultVolume: 0.85,
  17476. defaultVolume: 0.00,
  17477. // default inactivity timeout
  17478. inactivityTimeout: 2000,
  17479. // default playback rates
  17480. playbackRates: [],
  17481. // Add playback rate selection by adding rates
  17482. // 'playbackRates': [0.5, 1, 1.5, 2],
  17483. // Included control sets
  17484. children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
  17485. language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
  17486. // locales and their language translations
  17487. languages: {},
  17488. // Default message to show when a video cannot be played.
  17489. notSupportedMessage: 'No compatible source was found for this media.'
  17490. };
  17491. [
  17492. /**
  17493. * Returns whether or not the player is in the "ended" state.
  17494. *
  17495. * @return {Boolean} True if the player is in the ended state, false if not.
  17496. * @method Player#ended
  17497. */
  17498. 'ended',
  17499. /**
  17500. * Returns whether or not the player is in the "seeking" state.
  17501. *
  17502. * @return {Boolean} True if the player is in the seeking state, false if not.
  17503. * @method Player#seeking
  17504. */
  17505. 'seeking',
  17506. /**
  17507. * Returns the TimeRanges of the media that are currently available
  17508. * for seeking to.
  17509. *
  17510. * @return {TimeRanges} the seekable intervals of the media timeline
  17511. * @method Player#seekable
  17512. */
  17513. 'seekable',
  17514. /**
  17515. * Returns the current state of network activity for the element, from
  17516. * the codes in the list below.
  17517. * - NETWORK_EMPTY (numeric value 0)
  17518. * The element has not yet been initialised. All attributes are in
  17519. * their initial states.
  17520. * - NETWORK_IDLE (numeric value 1)
  17521. * The element's resource selection algorithm is active and has
  17522. * selected a resource, but it is not actually using the network at
  17523. * this time.
  17524. * - NETWORK_LOADING (numeric value 2)
  17525. * The user agent is actively trying to download data.
  17526. * - NETWORK_NO_SOURCE (numeric value 3)
  17527. * The element's resource selection algorithm is active, but it has
  17528. * not yet found a resource to use.
  17529. *
  17530. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
  17531. * @return {number} the current network activity state
  17532. * @method Player#networkState
  17533. */
  17534. 'networkState',
  17535. /**
  17536. * Returns a value that expresses the current state of the element
  17537. * with respect to rendering the current playback position, from the
  17538. * codes in the list below.
  17539. * - HAVE_NOTHING (numeric value 0)
  17540. * No information regarding the media resource is available.
  17541. * - HAVE_METADATA (numeric value 1)
  17542. * Enough of the resource has been obtained that the duration of the
  17543. * resource is available.
  17544. * - HAVE_CURRENT_DATA (numeric value 2)
  17545. * Data for the immediate current playback position is available.
  17546. * - HAVE_FUTURE_DATA (numeric value 3)
  17547. * Data for the immediate current playback position is available, as
  17548. * well as enough data for the user agent to advance the current
  17549. * playback position in the direction of playback.
  17550. * - HAVE_ENOUGH_DATA (numeric value 4)
  17551. * The user agent estimates that enough data is available for
  17552. * playback to proceed uninterrupted.
  17553. *
  17554. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
  17555. * @return {number} the current playback rendering state
  17556. * @method Player#readyState
  17557. */
  17558. 'readyState'].forEach(function (fn) {
  17559. Player.prototype[fn] = function () {
  17560. return this.techGet_(fn);
  17561. };
  17562. });
  17563. TECH_EVENTS_RETRIGGER.forEach(function (event) {
  17564. Player.prototype['handleTech' + (0, _toTitleCase2['default'])(event) + '_'] = function () {
  17565. return this.trigger(event);
  17566. };
  17567. });
  17568. /**
  17569. * Fired when the player has initial duration and dimension information
  17570. *
  17571. * @event Player#loadedmetadata
  17572. * @type {EventTarget~Event}
  17573. */
  17574. /**
  17575. * Fired when the player has downloaded data at the current playback position
  17576. *
  17577. * @event Player#loadeddata
  17578. * @type {EventTarget~Event}
  17579. */
  17580. /**
  17581. * Fired when the current playback position has changed *
  17582. * During playback this is fired every 15-250 milliseconds, depending on the
  17583. * playback technology in use.
  17584. *
  17585. * @event Player#timeupdate
  17586. * @type {EventTarget~Event}
  17587. */
  17588. /**
  17589. * Fired when the volume changes
  17590. *
  17591. * @event Player#volumechange
  17592. * @type {EventTarget~Event}
  17593. */
  17594. _component2['default'].registerComponent('Player', Player);
  17595. exports['default'] = Player;
  17596. },{"./big-play-button.js":43,"./close-button.js":46,"./component.js":47,"./control-bar/control-bar.js":50,"./error-display.js":83,"./fullscreen-api.js":86,"./loading-spinner.js":87,"./media-error.js":88,"./modal-dialog":92,"./poster-image.js":97,"./tech/flash.js":101,"./tech/html5.js":102,"./tech/loader.js":103,"./tech/tech.js":104,"./tracks/audio-track-list.js":105,"./tracks/text-track-display.js":110,"./tracks/text-track-list-converter.js":111,"./tracks/text-track-settings.js":113,"./tracks/video-track-list.js":118,"./utils/browser.js":120,"./utils/buffer.js":121,"./utils/dom.js":123,"./utils/events.js":124,"./utils/fn.js":125,"./utils/guid.js":127,"./utils/log.js":128,"./utils/merge-options.js":129,"./utils/obj":130,"./utils/stylesheet.js":131,"./utils/time-ranges.js":132,"./utils/to-title-case.js":133,"global/document":136,"global/window":137,"safe-json-parse/tuple":40}],94:[function(require,module,exports){
  17597. 'use strict';
  17598. exports.__esModule = true;
  17599. var _player = require('./player.js');
  17600. var _player2 = _interopRequireDefault(_player);
  17601. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17602. /**
  17603. * The method for registering a video.js plugin. {@link videojs:videojs.registerPlugin].
  17604. *
  17605. * @param {string} name
  17606. * The name of the plugin that is being registered
  17607. *
  17608. * @param {plugins:PluginFn} init
  17609. * The function that gets run when a `Player` initializes.
  17610. */
  17611. var plugin = function plugin(name, init) {
  17612. _player2['default'].prototype[name] = init;
  17613. }; /**
  17614. * @file plugins.js
  17615. * @module plugins
  17616. */
  17617. exports['default'] = plugin;
  17618. },{"./player.js":93}],95:[function(require,module,exports){
  17619. 'use strict';
  17620. exports.__esModule = true;
  17621. var _clickableComponent = require('../clickable-component.js');
  17622. var _clickableComponent2 = _interopRequireDefault(_clickableComponent);
  17623. var _component = require('../component.js');
  17624. var _component2 = _interopRequireDefault(_component);
  17625. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17626. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  17627. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  17628. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  17629. * @file popup-button.js
  17630. */
  17631. /**
  17632. * A button class for use with {@link Popup} controls
  17633. *
  17634. * @extends ClickableComponent
  17635. */
  17636. var PopupButton = function (_ClickableComponent) {
  17637. _inherits(PopupButton, _ClickableComponent);
  17638. /**
  17639. * Create an instance of this class.
  17640. *
  17641. * @param {Player} player
  17642. * The `Player` that this class should be attached to.
  17643. *
  17644. * @param {Object} [options]
  17645. * The key/value store of player options.
  17646. */
  17647. function PopupButton(player) {
  17648. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  17649. _classCallCheck(this, PopupButton);
  17650. var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  17651. _this.update();
  17652. return _this;
  17653. }
  17654. /**
  17655. * Update the `Popup` that this button is attached to.
  17656. */
  17657. PopupButton.prototype.update = function update() {
  17658. var popup = this.createPopup();
  17659. if (this.popup) {
  17660. this.removeChild(this.popup);
  17661. }
  17662. this.popup = popup;
  17663. this.addChild(popup);
  17664. if (this.items && this.items.length === 0) {
  17665. this.hide();
  17666. } else if (this.items && this.items.length > 1) {
  17667. this.show();
  17668. }
  17669. };
  17670. /**
  17671. * Create a `Popup`. - Override with specific functionality for component
  17672. *
  17673. * @abstract
  17674. */
  17675. PopupButton.prototype.createPopup = function createPopup() {};
  17676. /**
  17677. * Create the `PopupButton`s DOM element.
  17678. *
  17679. * @return {Element}
  17680. * The element that gets created.
  17681. */
  17682. PopupButton.prototype.createEl = function createEl() {
  17683. return _ClickableComponent.prototype.createEl.call(this, 'div', {
  17684. className: this.buildCSSClass()
  17685. });
  17686. };
  17687. /**
  17688. * Builds the default DOM `className`.
  17689. *
  17690. * @return {string}
  17691. * The DOM `className` for this object.
  17692. */
  17693. PopupButton.prototype.buildCSSClass = function buildCSSClass() {
  17694. var menuButtonClass = 'vjs-menu-button';
  17695. // If the inline option is passed, we want to use different styles altogether.
  17696. if (this.options_.inline === true) {
  17697. menuButtonClass += '-inline';
  17698. } else {
  17699. menuButtonClass += '-popup';
  17700. }
  17701. return 'vjs-menu-button ' + menuButtonClass + ' ' + _ClickableComponent.prototype.buildCSSClass.call(this);
  17702. };
  17703. return PopupButton;
  17704. }(_clickableComponent2['default']);
  17705. _component2['default'].registerComponent('PopupButton', PopupButton);
  17706. exports['default'] = PopupButton;
  17707. },{"../clickable-component.js":45,"../component.js":47}],96:[function(require,module,exports){
  17708. 'use strict';
  17709. exports.__esModule = true;
  17710. var _component = require('../component.js');
  17711. var _component2 = _interopRequireDefault(_component);
  17712. var _dom = require('../utils/dom.js');
  17713. var Dom = _interopRequireWildcard(_dom);
  17714. var _fn = require('../utils/fn.js');
  17715. var Fn = _interopRequireWildcard(_fn);
  17716. var _events = require('../utils/events.js');
  17717. var Events = _interopRequireWildcard(_events);
  17718. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  17719. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17720. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  17721. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  17722. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  17723. * @file popup.js
  17724. */
  17725. /**
  17726. * The Popup component is used to build pop up controls.
  17727. *
  17728. * @extends Component
  17729. */
  17730. var Popup = function (_Component) {
  17731. _inherits(Popup, _Component);
  17732. function Popup() {
  17733. _classCallCheck(this, Popup);
  17734. return _possibleConstructorReturn(this, _Component.apply(this, arguments));
  17735. }
  17736. /**
  17737. * Add a popup item to the popup
  17738. *
  17739. * @param {Object|string} component
  17740. * Component or component type to add
  17741. *
  17742. */
  17743. Popup.prototype.addItem = function addItem(component) {
  17744. this.addChild(component);
  17745. component.on('click', Fn.bind(this, function () {
  17746. this.unlockShowing();
  17747. }));
  17748. };
  17749. /**
  17750. * Create the `PopupButton`s DOM element.
  17751. *
  17752. * @return {Element}
  17753. * The element that gets created.
  17754. */
  17755. Popup.prototype.createEl = function createEl() {
  17756. var contentElType = this.options_.contentElType || 'ul';
  17757. this.contentEl_ = Dom.createEl(contentElType, {
  17758. className: 'vjs-menu-content'
  17759. });
  17760. var el = _Component.prototype.createEl.call(this, 'div', {
  17761. append: this.contentEl_,
  17762. className: 'vjs-menu'
  17763. });
  17764. el.appendChild(this.contentEl_);
  17765. // Prevent clicks from bubbling up. Needed for Popup Buttons,
  17766. // where a click on the parent is significant
  17767. Events.on(el, 'click', function (event) {
  17768. event.preventDefault();
  17769. event.stopImmediatePropagation();
  17770. });
  17771. return el;
  17772. };
  17773. return Popup;
  17774. }(_component2['default']);
  17775. _component2['default'].registerComponent('Popup', Popup);
  17776. exports['default'] = Popup;
  17777. },{"../component.js":47,"../utils/dom.js":123,"../utils/events.js":124,"../utils/fn.js":125}],97:[function(require,module,exports){
  17778. 'use strict';
  17779. exports.__esModule = true;
  17780. var _clickableComponent = require('./clickable-component.js');
  17781. var _clickableComponent2 = _interopRequireDefault(_clickableComponent);
  17782. var _component = require('./component.js');
  17783. var _component2 = _interopRequireDefault(_component);
  17784. var _fn = require('./utils/fn.js');
  17785. var Fn = _interopRequireWildcard(_fn);
  17786. var _dom = require('./utils/dom.js');
  17787. var Dom = _interopRequireWildcard(_dom);
  17788. var _browser = require('./utils/browser.js');
  17789. var browser = _interopRequireWildcard(_browser);
  17790. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  17791. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17792. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  17793. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  17794. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  17795. * @file poster-image.js
  17796. */
  17797. /**
  17798. * A `ClickableComponent` that handles showing the poster image for the player.
  17799. *
  17800. * @extends ClickableComponent
  17801. */
  17802. var PosterImage = function (_ClickableComponent) {
  17803. _inherits(PosterImage, _ClickableComponent);
  17804. /**
  17805. * Create an instance of this class.
  17806. *
  17807. * @param {Player} player
  17808. * The `Player` that this class should attach to.
  17809. *
  17810. * @param {Object} [options]
  17811. * The key/value store of player options.
  17812. */
  17813. function PosterImage(player, options) {
  17814. _classCallCheck(this, PosterImage);
  17815. var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  17816. _this.update();
  17817. player.on('posterchange', Fn.bind(_this, _this.update));
  17818. return _this;
  17819. }
  17820. /**
  17821. * Clean up and dispose of the `PosterImage`.
  17822. */
  17823. PosterImage.prototype.dispose = function dispose() {
  17824. this.player().off('posterchange', this.update);
  17825. _ClickableComponent.prototype.dispose.call(this);
  17826. };
  17827. /**
  17828. * Create the `PosterImage`s DOM element.
  17829. *
  17830. * @return {Element}
  17831. * The element that gets created.
  17832. */
  17833. PosterImage.prototype.createEl = function createEl() {
  17834. var el = Dom.createEl('div', {
  17835. className: 'vjs-poster',
  17836. // Don't want poster to be tabbable.
  17837. tabIndex: -1
  17838. });
  17839. // To ensure the poster image resizes while maintaining its original aspect
  17840. // ratio, use a div with `background-size` when available. For browsers that
  17841. // do not support `background-size` (e.g. IE8), fall back on using a regular
  17842. // img element.
  17843. if (!browser.BACKGROUND_SIZE_SUPPORTED) {
  17844. this.fallbackImg_ = Dom.createEl('img');
  17845. el.appendChild(this.fallbackImg_);
  17846. }
  17847. return el;
  17848. };
  17849. /**
  17850. * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
  17851. *
  17852. * @listens Player#posterchange
  17853. *
  17854. * @param {EventTarget~Event} [event]
  17855. * The `Player#posterchange` event that triggered this function.
  17856. */
  17857. PosterImage.prototype.update = function update(event) {
  17858. var url = this.player().poster();
  17859. this.setSrc(url);
  17860. // If there's no poster source we should display:none on this component
  17861. // so it's not still clickable or right-clickable
  17862. if (url) {
  17863. this.show();
  17864. } else {
  17865. this.hide();
  17866. }
  17867. };
  17868. /**
  17869. * Set the source of the `PosterImage` depending on the display method.
  17870. *
  17871. * @param {string} url
  17872. * The URL to the source for the `PosterImage`.
  17873. */
  17874. PosterImage.prototype.setSrc = function setSrc(url) {
  17875. if (this.fallbackImg_) {
  17876. this.fallbackImg_.src = url;
  17877. } else {
  17878. var backgroundImage = '';
  17879. // Any falsey values should stay as an empty string, otherwise
  17880. // this will throw an extra error
  17881. if (url) {
  17882. backgroundImage = 'url("' + url + '")';
  17883. }
  17884. this.el_.style.backgroundImage = backgroundImage;
  17885. }
  17886. };
  17887. /**
  17888. * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
  17889. * {@link ClickableComponent#handleClick} for instances where this will be triggered.
  17890. *
  17891. * @listens tap
  17892. * @listens click
  17893. * @listens keydown
  17894. *
  17895. * @param {EventTarget~Event} event
  17896. + The `click`, `tap` or `keydown` event that caused this function to be called.
  17897. */
  17898. PosterImage.prototype.handleClick = function handleClick(event) {
  17899. // We don't want a click to trigger playback when controls are disabled
  17900. if (!this.player_.controls()) {
  17901. return;
  17902. }
  17903. if (this.player_.paused()) {
  17904. this.player_.play();
  17905. } else {
  17906. this.player_.pause();
  17907. }
  17908. };
  17909. return PosterImage;
  17910. }(_clickableComponent2['default']);
  17911. _component2['default'].registerComponent('PosterImage', PosterImage);
  17912. exports['default'] = PosterImage;
  17913. },{"./clickable-component.js":45,"./component.js":47,"./utils/browser.js":120,"./utils/dom.js":123,"./utils/fn.js":125}],98:[function(require,module,exports){
  17914. 'use strict';
  17915. exports.__esModule = true;
  17916. exports.hasLoaded = exports.autoSetupTimeout = exports.autoSetup = undefined;
  17917. var _dom = require('./utils/dom');
  17918. var Dom = _interopRequireWildcard(_dom);
  17919. var _events = require('./utils/events.js');
  17920. var Events = _interopRequireWildcard(_events);
  17921. var _document = require('global/document');
  17922. var _document2 = _interopRequireDefault(_document);
  17923. var _window = require('global/window');
  17924. var _window2 = _interopRequireDefault(_window);
  17925. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  17926. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  17927. /**
  17928. * @file setup.js - Functions for setting up a player without
  17929. * user interaction based on the data-setup `attribute` of the video tag.
  17930. *
  17931. * @module setup
  17932. */
  17933. var _windowLoaded = false;
  17934. var videojs = void 0;
  17935. /**
  17936. * Set up any tags that have a data-setup `attribute` when the player is started.
  17937. */
  17938. var autoSetup = function autoSetup() {
  17939. // Protect against breakage in non-browser environments.
  17940. if (!Dom.isReal()) {
  17941. return;
  17942. }
  17943. // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
  17944. // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
  17945. // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
  17946. // var mediaEls = vids.concat(audios);
  17947. // Because IE8 doesn't support calling slice on a node list, we need to loop
  17948. // through each list of elements to build up a new, combined list of elements.
  17949. var vids = _document2['default'].getElementsByTagName('video');
  17950. var audios = _document2['default'].getElementsByTagName('audio');
  17951. var mediaEls = [];
  17952. if (vids && vids.length > 0) {
  17953. for (var i = 0, e = vids.length; i < e; i++) {
  17954. mediaEls.push(vids[i]);
  17955. }
  17956. }
  17957. if (audios && audios.length > 0) {
  17958. for (var _i = 0, _e = audios.length; _i < _e; _i++) {
  17959. mediaEls.push(audios[_i]);
  17960. }
  17961. }
  17962. // Check if any media elements exist
  17963. if (mediaEls && mediaEls.length > 0) {
  17964. for (var _i2 = 0, _e2 = mediaEls.length; _i2 < _e2; _i2++) {
  17965. var mediaEl = mediaEls[_i2];
  17966. // Check if element exists, has getAttribute func.
  17967. // IE seems to consider typeof el.getAttribute == 'object' instead of
  17968. // 'function' like expected, at least when loading the player immediately.
  17969. if (mediaEl && mediaEl.getAttribute) {
  17970. // Make sure this player hasn't already been set up.
  17971. if (mediaEl.player === undefined) {
  17972. var options = mediaEl.getAttribute('data-setup');
  17973. // Check if data-setup attr exists.
  17974. // We only auto-setup if they've added the data-setup attr.
  17975. if (options !== null) {
  17976. // Create new video.js instance.
  17977. videojs(mediaEl);
  17978. }
  17979. }
  17980. // If getAttribute isn't defined, we need to wait for the DOM.
  17981. } else {
  17982. autoSetupTimeout(1);
  17983. break;
  17984. }
  17985. }
  17986. // No videos were found, so keep looping unless page is finished loading.
  17987. } else if (!_windowLoaded) {
  17988. autoSetupTimeout(1);
  17989. }
  17990. };
  17991. /**
  17992. * Wait until the page is loaded before running autoSetup. This will be called in
  17993. * autoSetup if `hasLoaded` returns false.
  17994. *
  17995. * @param {number} wait
  17996. * How long to wait in ms
  17997. *
  17998. * @param {videojs} [vjs]
  17999. * The videojs library function
  18000. */
  18001. function autoSetupTimeout(wait, vjs) {
  18002. if (vjs) {
  18003. videojs = vjs;
  18004. }
  18005. _window2['default'].setTimeout(autoSetup, wait);
  18006. }
  18007. if (Dom.isReal() && _document2['default'].readyState === 'complete') {
  18008. _windowLoaded = true;
  18009. } else {
  18010. /**
  18011. * Listen for the load event on window, and set _windowLoaded to true.
  18012. *
  18013. * @listens load
  18014. */
  18015. Events.one(_window2['default'], 'load', function () {
  18016. _windowLoaded = true;
  18017. });
  18018. }
  18019. /**
  18020. * check if the document has been loaded
  18021. */
  18022. var hasLoaded = function hasLoaded() {
  18023. return _windowLoaded;
  18024. };
  18025. exports.autoSetup = autoSetup;
  18026. exports.autoSetupTimeout = autoSetupTimeout;
  18027. exports.hasLoaded = hasLoaded;
  18028. },{"./utils/dom":123,"./utils/events.js":124,"global/document":136,"global/window":137}],99:[function(require,module,exports){
  18029. 'use strict';
  18030. exports.__esModule = true;
  18031. var _component = require('../component.js');
  18032. var _component2 = _interopRequireDefault(_component);
  18033. var _dom = require('../utils/dom.js');
  18034. var Dom = _interopRequireWildcard(_dom);
  18035. var _obj = require('../utils/obj');
  18036. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  18037. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  18038. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  18039. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  18040. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  18041. * @file slider.js
  18042. */
  18043. /**
  18044. * The base functionality for a slider. Can be vertical or horizontal.
  18045. * For instance the volume bar or the seek bar on a video is a slider.
  18046. *
  18047. * @extends Component
  18048. */
  18049. var Slider = function (_Component) {
  18050. _inherits(Slider, _Component);
  18051. /**
  18052. * Create an instance of this class
  18053. *
  18054. * @param {Player} player
  18055. * The `Player` that this class should be attached to.
  18056. *
  18057. * @param {Object} [options]
  18058. * The key/value store of player options.
  18059. */
  18060. function Slider(player, options) {
  18061. _classCallCheck(this, Slider);
  18062. // Set property names to bar to match with the child Slider class is looking for
  18063. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  18064. _this.bar = _this.getChild(_this.options_.barName);
  18065. // Set a horizontal or vertical class on the slider depending on the slider type
  18066. _this.vertical(!!_this.options_.vertical);
  18067. _this.on('mousedown', _this.handleMouseDown);
  18068. _this.on('touchstart', _this.handleMouseDown);
  18069. _this.on('focus', _this.handleFocus);
  18070. _this.on('blur', _this.handleBlur);
  18071. _this.on('click', _this.handleClick);
  18072. _this.on(player, 'controlsvisible', _this.update);
  18073. _this.on(player, _this.playerEvent, _this.update);
  18074. return _this;
  18075. }
  18076. /**
  18077. * Create the `Button`s DOM element.
  18078. *
  18079. * @param {string} type
  18080. * Type of element to create.
  18081. *
  18082. * @param {Object} [props={}]
  18083. * List of properties in Object form.
  18084. *
  18085. * @param {Object} [attributes={}]
  18086. * list of attributes in Object form.
  18087. *
  18088. * @return {Element}
  18089. * The element that gets created.
  18090. */
  18091. Slider.prototype.createEl = function createEl(type) {
  18092. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  18093. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  18094. // Add the slider element class to all sub classes
  18095. props.className = props.className + ' vjs-slider';
  18096. props = (0, _obj.assign)({
  18097. tabIndex: 0
  18098. }, props);
  18099. attributes = (0, _obj.assign)({
  18100. 'role': 'slider',
  18101. 'aria-valuenow': 0,
  18102. 'aria-valuemin': 0,
  18103. 'aria-valuemax': 100,
  18104. 'tabIndex': 0
  18105. }, attributes);
  18106. return _Component.prototype.createEl.call(this, type, props, attributes);
  18107. };
  18108. /**
  18109. * Handle `mousedown` or `touchstart` events on the `Slider`.
  18110. *
  18111. * @param {EventTarget~Event} event
  18112. * `mousedown` or `touchstart` event that triggered this function
  18113. *
  18114. * @listens mousedown
  18115. * @listens touchstart
  18116. * @fires Slider#slideractive
  18117. */
  18118. Slider.prototype.handleMouseDown = function handleMouseDown(event) {
  18119. var doc = this.bar.el_.ownerDocument;
  18120. event.preventDefault();
  18121. Dom.blockTextSelection();
  18122. this.addClass('vjs-sliding');
  18123. /**
  18124. * Triggered when the slider is in an active state
  18125. *
  18126. * @event Slider#slideractive
  18127. * @type {EventTarget~Event}
  18128. */
  18129. this.trigger('slideractive');
  18130. this.on(doc, 'mousemove', this.handleMouseMove);
  18131. this.on(doc, 'mouseup', this.handleMouseUp);
  18132. this.on(doc, 'touchmove', this.handleMouseMove);
  18133. this.on(doc, 'touchend', this.handleMouseUp);
  18134. this.handleMouseMove(event);
  18135. };
  18136. /**
  18137. * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
  18138. * The `mousemove` and `touchmove` events will only only trigger this function during
  18139. * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
  18140. * {@link Slider#handleMouseUp}.
  18141. *
  18142. * @param {EventTarget~Event} event
  18143. * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
  18144. * this function
  18145. *
  18146. * @listens mousemove
  18147. * @listens touchmove
  18148. */
  18149. Slider.prototype.handleMouseMove = function handleMouseMove(event) {};
  18150. /**
  18151. * Handle `mouseup` or `touchend` events on the `Slider`.
  18152. *
  18153. * @param {EventTarget~Event} event
  18154. * `mouseup` or `touchend` event that triggered this function.
  18155. *
  18156. * @listens touchend
  18157. * @listens mouseup
  18158. * @fires Slider#sliderinactive
  18159. */
  18160. Slider.prototype.handleMouseUp = function handleMouseUp() {
  18161. var doc = this.bar.el_.ownerDocument;
  18162. Dom.unblockTextSelection();
  18163. this.removeClass('vjs-sliding');
  18164. /**
  18165. * Triggered when the slider is no longer in an active state.
  18166. *
  18167. * @event Slider#sliderinactive
  18168. * @type {EventTarget~Event}
  18169. */
  18170. this.trigger('sliderinactive');
  18171. this.off(doc, 'mousemove', this.handleMouseMove);
  18172. this.off(doc, 'mouseup', this.handleMouseUp);
  18173. this.off(doc, 'touchmove', this.handleMouseMove);
  18174. this.off(doc, 'touchend', this.handleMouseUp);
  18175. this.update();
  18176. };
  18177. /**
  18178. * Update the progress bar of the `Slider`.
  18179. */
  18180. Slider.prototype.update = function update() {
  18181. // In VolumeBar init we have a setTimeout for update that pops and update to the end of the
  18182. // execution stack. The player is destroyed before then update will cause an error
  18183. if (!this.el_) {
  18184. return;
  18185. }
  18186. // If scrubbing, we could use a cached value to make the handle keep up with the user's mouse.
  18187. // On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later.
  18188. // var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
  18189. var progress = this.getPercent();
  18190. var bar = this.bar;
  18191. // If there's no bar...
  18192. if (!bar) {
  18193. return;
  18194. }
  18195. // Protect against no duration and other division issues
  18196. if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
  18197. progress = 0;
  18198. }
  18199. // Convert to a percentage for setting
  18200. var percentage = (progress * 100).toFixed(2) + '%';
  18201. // Set the new bar width or height
  18202. if (this.vertical()) {
  18203. bar.el().style.height = percentage;
  18204. } else {
  18205. bar.el().style.width = percentage;
  18206. }
  18207. };
  18208. /**
  18209. * Calculate distance for slider
  18210. *
  18211. * @param {EventTarget~Event} event
  18212. * The event that caused this function to run.
  18213. *
  18214. * @return {number}
  18215. * The current position of the Slider.
  18216. * - postition.x for vertical `Slider`s
  18217. * - postition.y for horizontal `Slider`s
  18218. */
  18219. Slider.prototype.calculateDistance = function calculateDistance(event) {
  18220. var position = Dom.getPointerPosition(this.el_, event);
  18221. if (this.vertical()) {
  18222. return position.y;
  18223. }
  18224. return position.x;
  18225. };
  18226. /**
  18227. * Handle a `focus` event on this `Slider`.
  18228. *
  18229. * @param {EventTarget~Event} event
  18230. * The `focus` event that caused this function to run.
  18231. *
  18232. * @listens focus
  18233. */
  18234. Slider.prototype.handleFocus = function handleFocus() {
  18235. this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  18236. };
  18237. /**
  18238. * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
  18239. * arrow keys. This function will only be called when the slider has focus. See
  18240. * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
  18241. *
  18242. * @param {EventTarget~Event} event
  18243. * the `keydown` event that caused this function to run.
  18244. *
  18245. * @listens keydown
  18246. */
  18247. Slider.prototype.handleKeyPress = function handleKeyPress(event) {
  18248. // Left and Down Arrows
  18249. if (event.which === 37 || event.which === 40) {
  18250. event.preventDefault();
  18251. this.stepBack();
  18252. // Up and Right Arrows
  18253. } else if (event.which === 38 || event.which === 39) {
  18254. event.preventDefault();
  18255. this.stepForward();
  18256. }
  18257. };
  18258. /**
  18259. * Handle a `blur` event on this `Slider`.
  18260. *
  18261. * @param {EventTarget~Event} event
  18262. * The `blur` event that caused this function to run.
  18263. *
  18264. * @listens blur
  18265. */
  18266. Slider.prototype.handleBlur = function handleBlur() {
  18267. this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  18268. };
  18269. /**
  18270. * Listener for click events on slider, used to prevent clicks
  18271. * from bubbling up to parent elements like button menus.
  18272. *
  18273. * @param {Object} event
  18274. * Event that caused this object to run
  18275. */
  18276. Slider.prototype.handleClick = function handleClick(event) {
  18277. event.stopImmediatePropagation();
  18278. event.preventDefault();
  18279. };
  18280. /**
  18281. * Get/set if slider is horizontal for vertical
  18282. *
  18283. * @param {boolean} [bool]
  18284. * - true if slider is vertical,
  18285. * - false is horizontal
  18286. *
  18287. * @return {boolean|Slider}
  18288. * - true if slider is vertical, and getting
  18289. * - false is horizontal, and getting
  18290. * - a reference to this object when setting
  18291. */
  18292. Slider.prototype.vertical = function vertical(bool) {
  18293. if (bool === undefined) {
  18294. return this.vertical_ || false;
  18295. }
  18296. this.vertical_ = !!bool;
  18297. if (this.vertical_) {
  18298. this.addClass('vjs-slider-vertical');
  18299. } else {
  18300. this.addClass('vjs-slider-horizontal');
  18301. }
  18302. return this;
  18303. };
  18304. return Slider;
  18305. }(_component2['default']);
  18306. _component2['default'].registerComponent('Slider', Slider);
  18307. exports['default'] = Slider;
  18308. },{"../component.js":47,"../utils/dom.js":123,"../utils/obj":130}],100:[function(require,module,exports){
  18309. 'use strict';
  18310. exports.__esModule = true;
  18311. /**
  18312. * @file flash-rtmp.js
  18313. * @module flash-rtmp
  18314. */
  18315. /**
  18316. * Add RTMP properties to the {@link Flash} Tech.
  18317. *
  18318. * @param {Flash} Flash
  18319. * The flash tech class.
  18320. *
  18321. * @mixin FlashRtmpDecorator
  18322. */
  18323. function FlashRtmpDecorator(Flash) {
  18324. Flash.streamingFormats = {
  18325. 'rtmp/mp4': 'MP4',
  18326. 'rtmp/flv': 'FLV'
  18327. };
  18328. /**
  18329. * Join connection and stream with an ampersand.
  18330. *
  18331. * @param {string} connection
  18332. * The connection string.
  18333. *
  18334. * @param {string} stream
  18335. * The stream string.
  18336. */
  18337. Flash.streamFromParts = function (connection, stream) {
  18338. return connection + '&' + stream;
  18339. };
  18340. /**
  18341. * The flash parts object that contains connection and stream info.
  18342. *
  18343. * @typedef {Object} Flash~PartsObject
  18344. *
  18345. * @property {string} connection
  18346. * The connection string of a source, defaults to an empty string.
  18347. *
  18348. * @property {string} stream
  18349. * The stream string of the source, defaults to an empty string.
  18350. */
  18351. /**
  18352. * Convert a source url into a stream and connection parts.
  18353. *
  18354. * @param {string} src
  18355. * the source url
  18356. *
  18357. * @return {Flash~PartsObject}
  18358. * The parts object that contains a connection and a stream
  18359. */
  18360. Flash.streamToParts = function (src) {
  18361. var parts = {
  18362. connection: '',
  18363. stream: ''
  18364. };
  18365. if (!src) {
  18366. return parts;
  18367. }
  18368. // Look for the normal URL separator we expect, '&'.
  18369. // If found, we split the URL into two pieces around the
  18370. // first '&'.
  18371. var connEnd = src.search(/&(?!\w+=)/);
  18372. var streamBegin = void 0;
  18373. if (connEnd !== -1) {
  18374. streamBegin = connEnd + 1;
  18375. } else {
  18376. // If there's not a '&', we use the last '/' as the delimiter.
  18377. connEnd = streamBegin = src.lastIndexOf('/') + 1;
  18378. if (connEnd === 0) {
  18379. // really, there's not a '/'?
  18380. connEnd = streamBegin = src.length;
  18381. }
  18382. }
  18383. parts.connection = src.substring(0, connEnd);
  18384. parts.stream = src.substring(streamBegin, src.length);
  18385. return parts;
  18386. };
  18387. /**
  18388. * Check if the source type is a streaming type.
  18389. *
  18390. * @param {string} srcType
  18391. * The mime type to check.
  18392. *
  18393. * @return {boolean}
  18394. * - True if the source type is a streaming type.
  18395. * - False if the source type is not a streaming type.
  18396. */
  18397. Flash.isStreamingType = function (srcType) {
  18398. return srcType in Flash.streamingFormats;
  18399. };
  18400. // RTMP has four variations, any string starting
  18401. // with one of these protocols should be valid
  18402. /**
  18403. * Regular expression used to check if the source is an rtmp source.
  18404. *
  18405. * @property {RegExp} Flash.RTMP_RE
  18406. */
  18407. Flash.RTMP_RE = /^rtmp[set]?:\/\//i;
  18408. /**
  18409. * Check if the source itself is a streaming type.
  18410. *
  18411. * @param {string} src
  18412. * The url to the source.
  18413. *
  18414. * @return {boolean}
  18415. * - True if the source url indicates that the source is streaming.
  18416. * - False if the shource url indicates that the source url is not streaming.
  18417. */
  18418. Flash.isStreamingSrc = function (src) {
  18419. return Flash.RTMP_RE.test(src);
  18420. };
  18421. /**
  18422. * A source handler for RTMP urls
  18423. * @type {Object}
  18424. */
  18425. Flash.rtmpSourceHandler = {};
  18426. /**
  18427. * Check if Flash can play the given mime type.
  18428. *
  18429. * @param {string} type
  18430. * The mime type to check
  18431. *
  18432. * @return {string}
  18433. * 'maybe', or '' (empty string)
  18434. */
  18435. Flash.rtmpSourceHandler.canPlayType = function (type) {
  18436. if (Flash.isStreamingType(type)) {
  18437. return 'maybe';
  18438. }
  18439. return '';
  18440. };
  18441. /**
  18442. * Check if Flash can handle the source natively
  18443. *
  18444. * @param {Object} source
  18445. * The source object
  18446. *
  18447. * @param {Object} [options]
  18448. * The options passed to the tech
  18449. *
  18450. * @return {string}
  18451. * 'maybe', or '' (empty string)
  18452. */
  18453. Flash.rtmpSourceHandler.canHandleSource = function (source, options) {
  18454. var can = Flash.rtmpSourceHandler.canPlayType(source.type);
  18455. if (can) {
  18456. return can;
  18457. }
  18458. if (Flash.isStreamingSrc(source.src)) {
  18459. return 'maybe';
  18460. }
  18461. return '';
  18462. };
  18463. /**
  18464. * Pass the source to the flash object.
  18465. *
  18466. * @param {Object} source
  18467. * The source object
  18468. *
  18469. * @param {Flash} tech
  18470. * The instance of the Flash tech
  18471. *
  18472. * @param {Object} [options]
  18473. * The options to pass to the source
  18474. */
  18475. Flash.rtmpSourceHandler.handleSource = function (source, tech, options) {
  18476. var srcParts = Flash.streamToParts(source.src);
  18477. tech.setRtmpConnection(srcParts.connection);
  18478. tech.setRtmpStream(srcParts.stream);
  18479. };
  18480. // Register the native source handler
  18481. Flash.registerSourceHandler(Flash.rtmpSourceHandler);
  18482. return Flash;
  18483. }
  18484. exports['default'] = FlashRtmpDecorator;
  18485. },{}],101:[function(require,module,exports){
  18486. 'use strict';
  18487. exports.__esModule = true;
  18488. var _tech = require('./tech');
  18489. var _tech2 = _interopRequireDefault(_tech);
  18490. var _dom = require('../utils/dom.js');
  18491. var Dom = _interopRequireWildcard(_dom);
  18492. var _url = require('../utils/url.js');
  18493. var Url = _interopRequireWildcard(_url);
  18494. var _timeRanges = require('../utils/time-ranges.js');
  18495. var _flashRtmp = require('./flash-rtmp');
  18496. var _flashRtmp2 = _interopRequireDefault(_flashRtmp);
  18497. var _component = require('../component');
  18498. var _component2 = _interopRequireDefault(_component);
  18499. var _window = require('global/window');
  18500. var _window2 = _interopRequireDefault(_window);
  18501. var _obj = require('../utils/obj');
  18502. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  18503. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  18504. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  18505. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  18506. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  18507. * @file flash.js
  18508. * VideoJS-SWF - Custom Flash Player with HTML5-ish API
  18509. * https://github.com/zencoder/video-js-swf
  18510. * Not using setupTriggers. Using global onEvent func to distribute events
  18511. */
  18512. var navigator = _window2['default'].navigator;
  18513. /**
  18514. * Flash Media Controller - Wrapper for Flash Media API
  18515. *
  18516. * @mixes FlashRtmpDecorator
  18517. * @mixes Tech~SouceHandlerAdditions
  18518. * @extends Tech
  18519. */
  18520. var Flash = function (_Tech) {
  18521. _inherits(Flash, _Tech);
  18522. /**
  18523. * Create an instance of this Tech.
  18524. *
  18525. * @param {Object} [options]
  18526. * The key/value store of player options.
  18527. *
  18528. * @param {Component~ReadyCallback} ready
  18529. * Callback function to call when the `Flash` Tech is ready.
  18530. */
  18531. function Flash(options, ready) {
  18532. _classCallCheck(this, Flash);
  18533. // Set the source when ready
  18534. var _this = _possibleConstructorReturn(this, _Tech.call(this, options, ready));
  18535. if (options.source) {
  18536. _this.ready(function () {
  18537. this.setSource(options.source);
  18538. }, true);
  18539. }
  18540. // Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
  18541. // This allows resetting the playhead when we catch the reload
  18542. if (options.startTime) {
  18543. _this.ready(function () {
  18544. this.load();
  18545. this.play();
  18546. this.currentTime(options.startTime);
  18547. }, true);
  18548. }
  18549. // Add global window functions that the swf expects
  18550. // A 4.x workflow we weren't able to solve for in 5.0
  18551. // because of the need to hard code these functions
  18552. // into the swf for security reasons
  18553. _window2['default'].videojs = _window2['default'].videojs || {};
  18554. _window2['default'].videojs.Flash = _window2['default'].videojs.Flash || {};
  18555. _window2['default'].videojs.Flash.onReady = Flash.onReady;
  18556. _window2['default'].videojs.Flash.onEvent = Flash.onEvent;
  18557. _window2['default'].videojs.Flash.onError = Flash.onError;
  18558. _this.on('seeked', function () {
  18559. this.lastSeekTarget_ = undefined;
  18560. });
  18561. return _this;
  18562. }
  18563. /**
  18564. * Create the `Flash` Tech's DOM element.
  18565. *
  18566. * @return {Element}
  18567. * The element that gets created.
  18568. */
  18569. Flash.prototype.createEl = function createEl() {
  18570. var options = this.options_;
  18571. // If video.js is hosted locally you should also set the location
  18572. // for the hosted swf, which should be relative to the page (not video.js)
  18573. // Otherwise this adds a CDN url.
  18574. // The CDN also auto-adds a swf URL for that specific version.
  18575. if (!options.swf) {
  18576. var ver = '5.4.1';
  18577. options.swf = '//vjs.zencdn.net/swf/' + ver + '/video-js.swf';
  18578. }
  18579. // Generate ID for swf object
  18580. var objId = options.techId;
  18581. // Merge default flashvars with ones passed in to init
  18582. var flashVars = (0, _obj.assign)({
  18583. // SWF Callback Functions
  18584. readyFunction: 'videojs.Flash.onReady',
  18585. eventProxyFunction: 'videojs.Flash.onEvent',
  18586. errorEventProxyFunction: 'videojs.Flash.onError',
  18587. // Player Settings
  18588. autoplay: options.autoplay,
  18589. preload: options.preload,
  18590. loop: options.loop,
  18591. muted: options.muted
  18592. }, options.flashVars);
  18593. // Merge default parames with ones passed in
  18594. var params = (0, _obj.assign)({
  18595. // Opaque is needed to overlay controls, but can affect playback performance
  18596. wmode: 'opaque',
  18597. // Using bgcolor prevents a white flash when the object is loading
  18598. bgcolor: '#000000'
  18599. }, options.params);
  18600. // Merge default attributes with ones passed in
  18601. var attributes = (0, _obj.assign)({
  18602. // Both ID and Name needed or swf to identify itself
  18603. id: objId,
  18604. name: objId,
  18605. 'class': 'vjs-tech'
  18606. }, options.attributes);
  18607. this.el_ = Flash.embed(options.swf, flashVars, params, attributes);
  18608. this.el_.tech = this;
  18609. return this.el_;
  18610. };
  18611. /**
  18612. * Called by {@link Player#play} to play using the `Flash` `Tech`.
  18613. */
  18614. Flash.prototype.play = function play() {
  18615. if (this.ended()) {
  18616. this.setCurrentTime(0);
  18617. }
  18618. this.el_.vjs_play();
  18619. };
  18620. /**
  18621. * Called by {@link Player#pause} to pause using the `Flash` `Tech`.
  18622. */
  18623. Flash.prototype.pause = function pause() {
  18624. this.el_.vjs_pause();
  18625. };
  18626. /**
  18627. * A getter/setter for the `Flash` Tech's source object.
  18628. * > Note: Please use {@link Flash#setSource}
  18629. *
  18630. * @param {Tech~SourceObject} [src]
  18631. * The source object you want to set on the `Flash` techs.
  18632. *
  18633. * @return {Tech~SourceObject|undefined}
  18634. * - The current source object when a source is not passed in.
  18635. * - undefined when setting
  18636. *
  18637. * @deprecated Since version 5.
  18638. */
  18639. Flash.prototype.src = function src(_src) {
  18640. if (_src === undefined) {
  18641. return this.currentSrc();
  18642. }
  18643. // Setting src through `src` not `setSrc` will be deprecated
  18644. return this.setSrc(_src);
  18645. };
  18646. /**
  18647. * A getter/setter for the `Flash` Tech's source object.
  18648. *
  18649. * @param {Tech~SourceObject} [src]
  18650. * The source object you want to set on the `Flash` techs.
  18651. *
  18652. * @return {Tech~SourceObject|undefined}
  18653. * - The current source object when a source is not passed in.
  18654. * - undefined when setting
  18655. */
  18656. Flash.prototype.setSrc = function setSrc(src) {
  18657. var _this2 = this;
  18658. // Make sure source URL is absolute.
  18659. src = Url.getAbsoluteURL(src);
  18660. this.el_.vjs_src(src);
  18661. // Currently the SWF doesn't autoplay if you load a source later.
  18662. // e.g. Load player w/ no source, wait 2s, set src.
  18663. if (this.autoplay()) {
  18664. this.setTimeout(function () {
  18665. return _this2.play();
  18666. }, 0);
  18667. }
  18668. };
  18669. /**
  18670. * Indicates whether the media is currently seeking to a new position or not.
  18671. *
  18672. * @return {boolean}
  18673. * - True if seeking to a new position
  18674. * - False otherwise
  18675. */
  18676. Flash.prototype.seeking = function seeking() {
  18677. return this.lastSeekTarget_ !== undefined;
  18678. };
  18679. /**
  18680. * Returns the current time in seconds that the media is at in playback.
  18681. *
  18682. * @param {number} time
  18683. * Current playtime of the media in seconds.
  18684. */
  18685. Flash.prototype.setCurrentTime = function setCurrentTime(time) {
  18686. var seekable = this.seekable();
  18687. if (seekable.length) {
  18688. // clamp to the current seekable range
  18689. time = time > seekable.start(0) ? time : seekable.start(0);
  18690. time = time < seekable.end(seekable.length - 1) ? time : seekable.end(seekable.length - 1);
  18691. this.lastSeekTarget_ = time;
  18692. this.trigger('seeking');
  18693. this.el_.vjs_setProperty('currentTime', time);
  18694. _Tech.prototype.setCurrentTime.call(this);
  18695. }
  18696. };
  18697. /**
  18698. * Get the current playback time in seconds
  18699. *
  18700. * @return {number}
  18701. * The current time of playback in seconds.
  18702. */
  18703. Flash.prototype.currentTime = function currentTime() {
  18704. // when seeking make the reported time keep up with the requested time
  18705. // by reading the time we're seeking to
  18706. if (this.seeking()) {
  18707. return this.lastSeekTarget_ || 0;
  18708. }
  18709. return this.el_.vjs_getProperty('currentTime');
  18710. };
  18711. /**
  18712. * Get the current source
  18713. *
  18714. * @method currentSrc
  18715. * @return {Tech~SourceObject}
  18716. * The current source
  18717. */
  18718. Flash.prototype.currentSrc = function currentSrc() {
  18719. if (this.currentSource_) {
  18720. return this.currentSource_.src;
  18721. }
  18722. return this.el_.vjs_getProperty('currentSrc');
  18723. };
  18724. /**
  18725. * Get the total duration of the current media.
  18726. *
  18727. * @return {number}
  18728. 8 The total duration of the current media.
  18729. */
  18730. Flash.prototype.duration = function duration() {
  18731. if (this.readyState() === 0) {
  18732. return NaN;
  18733. }
  18734. var duration = this.el_.vjs_getProperty('duration');
  18735. return duration >= 0 ? duration : Infinity;
  18736. };
  18737. /**
  18738. * Load media into Tech.
  18739. */
  18740. Flash.prototype.load = function load() {
  18741. this.el_.vjs_load();
  18742. };
  18743. /**
  18744. * Get the poster image that was set on the tech.
  18745. */
  18746. Flash.prototype.poster = function poster() {
  18747. this.el_.vjs_getProperty('poster');
  18748. };
  18749. /**
  18750. * Poster images are not handled by the Flash tech so make this is a no-op.
  18751. */
  18752. Flash.prototype.setPoster = function setPoster() {};
  18753. /**
  18754. * Determine the time ranges that can be seeked to in the media.
  18755. *
  18756. * @return {TimeRange}
  18757. * Returns the time ranges that can be seeked to.
  18758. */
  18759. Flash.prototype.seekable = function seekable() {
  18760. var duration = this.duration();
  18761. if (duration === 0) {
  18762. return (0, _timeRanges.createTimeRange)();
  18763. }
  18764. return (0, _timeRanges.createTimeRange)(0, duration);
  18765. };
  18766. /**
  18767. * Get and create a `TimeRange` object for buffering.
  18768. *
  18769. * @return {TimeRange}
  18770. * The time range object that was created.
  18771. */
  18772. Flash.prototype.buffered = function buffered() {
  18773. var ranges = this.el_.vjs_getProperty('buffered');
  18774. if (ranges.length === 0) {
  18775. return (0, _timeRanges.createTimeRange)();
  18776. }
  18777. return (0, _timeRanges.createTimeRange)(ranges[0][0], ranges[0][1]);
  18778. };
  18779. /**
  18780. * Get fullscreen support -
  18781. *
  18782. * Flash does not allow fullscreen through javascript
  18783. * so this always returns false.
  18784. *
  18785. * @return {boolean}
  18786. * The Flash tech does not support fullscreen, so it will always return false.
  18787. */
  18788. Flash.prototype.supportsFullScreen = function supportsFullScreen() {
  18789. // Flash does not allow fullscreen through javascript
  18790. return false;
  18791. };
  18792. /**
  18793. * Flash does not allow fullscreen through javascript
  18794. * so this always returns false.
  18795. *
  18796. * @return {boolean}
  18797. * The Flash tech does not support fullscreen, so it will always return false.
  18798. */
  18799. Flash.prototype.enterFullScreen = function enterFullScreen() {
  18800. return false;
  18801. };
  18802. /**
  18803. * Gets available media playback quality metrics as specified by the W3C's Media
  18804. * Playback Quality API.
  18805. *
  18806. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  18807. *
  18808. * @return {Object}
  18809. * An object with supported media playback quality metrics
  18810. */
  18811. Flash.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  18812. var videoPlaybackQuality = this.el_.vjs_getProperty('getVideoPlaybackQuality');
  18813. if (_window2['default'].performance && typeof _window2['default'].performance.now === 'function') {
  18814. videoPlaybackQuality.creationTime = _window2['default'].performance.now();
  18815. } else if (_window2['default'].performance && _window2['default'].performance.timing && typeof _window2['default'].performance.timing.navigationStart === 'number') {
  18816. videoPlaybackQuality.creationTime = _window2['default'].Date.now() - _window2['default'].performance.timing.navigationStart;
  18817. }
  18818. return videoPlaybackQuality;
  18819. };
  18820. return Flash;
  18821. }(_tech2['default']);
  18822. // Create setters and getters for attributes
  18823. var _api = Flash.prototype;
  18824. var _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(',');
  18825. var _readOnly = 'networkState,readyState,initialTime,startOffsetTime,paused,ended,videoWidth,videoHeight'.split(',');
  18826. function _createSetter(attr) {
  18827. var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
  18828. _api['set' + attrUpper] = function (val) {
  18829. return this.el_.vjs_setProperty(attr, val);
  18830. };
  18831. }
  18832. function _createGetter(attr) {
  18833. _api[attr] = function () {
  18834. return this.el_.vjs_getProperty(attr);
  18835. };
  18836. }
  18837. // Create getter and setters for all read/write attributes
  18838. for (var i = 0; i < _readWrite.length; i++) {
  18839. _createGetter(_readWrite[i]);
  18840. _createSetter(_readWrite[i]);
  18841. }
  18842. // Create getters for read-only attributes
  18843. for (var _i = 0; _i < _readOnly.length; _i++) {
  18844. _createGetter(_readOnly[_i]);
  18845. }
  18846. /** ------------------------------ Getters ------------------------------ **/
  18847. /**
  18848. * Get the value of `rtmpConnection` from the swf.
  18849. *
  18850. * @method Flash#rtmpConnection
  18851. * @return {string}
  18852. * The current value of `rtmpConnection` on the swf.
  18853. */
  18854. /**
  18855. * Get the value of `rtmpStream` from the swf.
  18856. *
  18857. * @method Flash#rtmpStream
  18858. * @return {string}
  18859. * The current value of `rtmpStream` on the swf.
  18860. */
  18861. /**
  18862. * Get the value of `preload` from the swf. `preload` indicates
  18863. * what should download before the media is interacted with. It can have the following
  18864. * values:
  18865. * - none: nothing should be downloaded
  18866. * - metadata: poster and the first few frames of the media may be downloaded to get
  18867. * media dimensions and other metadata
  18868. * - auto: allow the media and metadata for the media to be downloaded before
  18869. * interaction
  18870. *
  18871. * @method Flash#preload
  18872. * @return {string}
  18873. * The value of `preload` from the swf. Will be 'none', 'metadata',
  18874. * or 'auto'.
  18875. */
  18876. /**
  18877. * Get the value of `defaultPlaybackRate` from the swf.
  18878. *
  18879. * @method Flash#defaultPlaybackRate
  18880. * @return {number}
  18881. * The current value of `defaultPlaybackRate` on the swf.
  18882. */
  18883. /**
  18884. * Get the value of `playbackRate` from the swf. `playbackRate` indicates
  18885. * the rate at which the media is currently playing back. Examples:
  18886. * - if playbackRate is set to 2, media will play twice as fast.
  18887. * - if playbackRate is set to 0.5, media will play half as fast.
  18888. *
  18889. * @method Flash#playbackRate
  18890. * @return {number}
  18891. * The value of `playbackRate` from the swf. A number indicating
  18892. * the current playback speed of the media, where 1 is normal speed.
  18893. */
  18894. /**
  18895. * Get the value of `autoplay` from the swf. `autoplay` indicates
  18896. * that the media should start to play as soon as the page is ready.
  18897. *
  18898. * @method Flash#autoplay
  18899. * @return {boolean}
  18900. * - The value of `autoplay` from the swf.
  18901. * - True indicates that the media ashould start as soon as the page loads.
  18902. * - False indicates that the media should not start as soon as the page loads.
  18903. */
  18904. /**
  18905. * Get the value of `loop` from the swf. `loop` indicates
  18906. * that the media should return to the start of the media and continue playing once
  18907. * it reaches the end.
  18908. *
  18909. * @method Flash#loop
  18910. * @return {boolean}
  18911. * - The value of `loop` from the swf.
  18912. * - True indicates that playback should seek back to start once
  18913. * the end of a media is reached.
  18914. * - False indicates that playback should not loop back to the start when the
  18915. * end of the media is reached.
  18916. */
  18917. /**
  18918. * Get the value of `mediaGroup` from the swf.
  18919. *
  18920. * @method Flash#mediaGroup
  18921. * @return {string}
  18922. * The current value of `mediaGroup` on the swf.
  18923. */
  18924. /**
  18925. * Get the value of `controller` from the swf.
  18926. *
  18927. * @method Flash#controller
  18928. * @return {string}
  18929. * The current value of `controller` on the swf.
  18930. */
  18931. /**
  18932. * Get the value of `controls` from the swf. `controls` indicates
  18933. * whether the native flash controls should be shown or hidden.
  18934. *
  18935. * @method Flash#controls
  18936. * @return {boolean}
  18937. * - The value of `controls` from the swf.
  18938. * - True indicates that native controls should be showing.
  18939. * - False indicates that native controls should be hidden.
  18940. */
  18941. /**
  18942. * Get the value of the `volume` from the swf. `volume` indicates the current
  18943. * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
  18944. * so on.
  18945. *
  18946. * @method Flash#volume
  18947. * @return {number}
  18948. * The volume percent as a decimal. Value will be between 0-1.
  18949. */
  18950. /**
  18951. * Get the value of the `muted` from the swf. `muted` indicates the current
  18952. * audio level should be silent.
  18953. *
  18954. * @method Flash#muted
  18955. * @return {boolean}
  18956. * - True if the audio should be set to silent
  18957. * - False otherwise
  18958. */
  18959. /**
  18960. * Get the value of `defaultMuted` from the swf. `defaultMuted` indicates
  18961. * whether the media should start muted or not. Only changes the default state of the
  18962. * media. `muted` and `defaultMuted` can have different values. `muted` indicates the
  18963. * current state.
  18964. *
  18965. * @method Flash#defaultMuted
  18966. * @return {boolean}
  18967. * - The value of `defaultMuted` from the swf.
  18968. * - True indicates that the media should start muted.
  18969. * - False indicates that the media should not start muted.
  18970. */
  18971. /**
  18972. * Get the value of `networkState` from the swf. `networkState` indicates
  18973. * the current network state. It returns an enumeration from the following list:
  18974. * - 0: NETWORK_EMPTY
  18975. * - 1: NEWORK_IDLE
  18976. * - 2: NETWORK_LOADING
  18977. * - 3: NETWORK_NO_SOURCE
  18978. *
  18979. * @method Flash#networkState
  18980. * @return {number}
  18981. * The value of `networkState` from the swf. This will be a number
  18982. * from the list in the description.
  18983. */
  18984. /**
  18985. * Get the value of `readyState` from the swf. `readyState` indicates
  18986. * the current state of the media element. It returns an enumeration from the
  18987. * following list:
  18988. * - 0: HAVE_NOTHING
  18989. * - 1: HAVE_METADATA
  18990. * - 2: HAVE_CURRENT_DATA
  18991. * - 3: HAVE_FUTURE_DATA
  18992. * - 4: HAVE_ENOUGH_DATA
  18993. *
  18994. * @method Flash#readyState
  18995. * @return {number}
  18996. * The value of `readyState` from the swf. This will be a number
  18997. * from the list in the description.
  18998. */
  18999. /**
  19000. * Get the value of `readyState` from the swf. `readyState` indicates
  19001. * the current state of the media element. It returns an enumeration from the
  19002. * following list:
  19003. * - 0: HAVE_NOTHING
  19004. * - 1: HAVE_METADATA
  19005. * - 2: HAVE_CURRENT_DATA
  19006. * - 3: HAVE_FUTURE_DATA
  19007. * - 4: HAVE_ENOUGH_DATA
  19008. *
  19009. * @method Flash#readyState
  19010. * @return {number}
  19011. * The value of `readyState` from the swf. This will be a number
  19012. * from the list in the description.
  19013. */
  19014. /**
  19015. * Get the value of `initialTime` from the swf.
  19016. *
  19017. * @method Flash#initialTime
  19018. * @return {number}
  19019. * The `initialTime` proprety on the swf.
  19020. */
  19021. /**
  19022. * Get the value of `startOffsetTime` from the swf.
  19023. *
  19024. * @method Flash#startOffsetTime
  19025. * @return {number}
  19026. * The `startOffsetTime` proprety on the swf.
  19027. */
  19028. /**
  19029. * Get the value of `paused` from the swf. `paused` indicates whether the swf
  19030. * is current paused or not.
  19031. *
  19032. * @method Flash#paused
  19033. * @return {boolean}
  19034. * The value of `paused` from the swf.
  19035. */
  19036. /**
  19037. * Get the value of `ended` from the swf. `ended` indicates whether
  19038. * the media has reached the end or not.
  19039. *
  19040. * @method Flash#ended
  19041. * @return {boolean}
  19042. * - True indicates that the media has ended.
  19043. * - False indicates that the media has not ended.
  19044. *
  19045. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
  19046. */
  19047. /**
  19048. * Get the value of `videoWidth` from the swf. `videoWidth` indicates
  19049. * the current width of the media in css pixels.
  19050. *
  19051. * @method Flash#videoWidth
  19052. * @return {number}
  19053. * The value of `videoWidth` from the swf. This will be a number
  19054. * in css pixels.
  19055. */
  19056. /**
  19057. * Get the value of `videoHeight` from the swf. `videoHeigth` indicates
  19058. * the current height of the media in css pixels.
  19059. *
  19060. * @method Flassh.prototype.videoHeight
  19061. * @return {number}
  19062. * The value of `videoHeight` from the swf. This will be a number
  19063. * in css pixels.
  19064. */
  19065. /** ------------------------------ Setters ------------------------------ **/
  19066. /**
  19067. * Set the value of `rtmpConnection` on the swf.
  19068. *
  19069. * @method Flash#setRtmpConnection
  19070. * @param {string} rtmpConnection
  19071. * New value to set the `rtmpConnection` property to.
  19072. */
  19073. /**
  19074. * Set the value of `rtmpStream` on the swf.
  19075. *
  19076. * @method Flash#setRtmpStream
  19077. * @param {string} rtmpStream
  19078. * New value to set the `rtmpStream` property to.
  19079. */
  19080. /**
  19081. * Set the value of `preload` on the swf. `preload` indicates
  19082. * what should download before the media is interacted with. It can have the following
  19083. * values:
  19084. * - none: nothing should be downloaded
  19085. * - metadata: poster and the first few frames of the media may be downloaded to get
  19086. * media dimensions and other metadata
  19087. * - auto: allow the media and metadata for the media to be downloaded before
  19088. * interaction
  19089. *
  19090. * @method Flash#setPreload
  19091. * @param {string} preload
  19092. * The value of `preload` to set on the swf. Should be 'none', 'metadata',
  19093. * or 'auto'.
  19094. */
  19095. /**
  19096. * Set the value of `defaultPlaybackRate` on the swf.
  19097. *
  19098. * @method Flash#setDefaultPlaybackRate
  19099. * @param {number} defaultPlaybackRate
  19100. * New value to set the `defaultPlaybackRate` property to.
  19101. */
  19102. /**
  19103. * Set the value of `playbackRate` on the swf. `playbackRate` indicates
  19104. * the rate at which the media is currently playing back. Examples:
  19105. * - if playbackRate is set to 2, media will play twice as fast.
  19106. * - if playbackRate is set to 0.5, media will play half as fast.
  19107. *
  19108. * @method Flash#setPlaybackRate
  19109. * @param {number} playbackRate
  19110. * New value of `playbackRate` on the swf. A number indicating
  19111. * the current playback speed of the media, where 1 is normal speed.
  19112. */
  19113. /**
  19114. * Set the value of `autoplay` on the swf. `autoplay` indicates
  19115. * that the media should start to play as soon as the page is ready.
  19116. *
  19117. * @method Flash#setAutoplay
  19118. * @param {boolean} autoplay
  19119. * - The value of `autoplay` from the swf.
  19120. * - True indicates that the media ashould start as soon as the page loads.
  19121. * - False indicates that the media should not start as soon as the page loads.
  19122. */
  19123. /**
  19124. * Set the value of `loop` on the swf. `loop` indicates
  19125. * that the media should return to the start of the media and continue playing once
  19126. * it reaches the end.
  19127. *
  19128. * @method Flash#setLoop
  19129. * @param {boolean} loop
  19130. * - True indicates that playback should seek back to start once
  19131. * the end of a media is reached.
  19132. * - False indicates that playback should not loop back to the start when the
  19133. * end of the media is reached.
  19134. */
  19135. /**
  19136. * Set the value of `mediaGroup` on the swf.
  19137. *
  19138. * @method Flash#setMediaGroup
  19139. * @param {string} mediaGroup
  19140. * New value of `mediaGroup` to set on the swf.
  19141. */
  19142. /**
  19143. * Set the value of `controller` on the swf.
  19144. *
  19145. * @method Flash#setController
  19146. * @param {string} controller
  19147. * New value the current value of `controller` on the swf.
  19148. */
  19149. /**
  19150. * Get the value of `controls` from the swf. `controls` indicates
  19151. * whether the native flash controls should be shown or hidden.
  19152. *
  19153. * @method Flash#controls
  19154. * @return {boolean}
  19155. * - The value of `controls` from the swf.
  19156. * - True indicates that native controls should be showing.
  19157. * - False indicates that native controls should be hidden.
  19158. */
  19159. /**
  19160. * Set the value of the `volume` on the swf. `volume` indicates the current
  19161. * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
  19162. * so on.
  19163. *
  19164. * @method Flash#setVolume
  19165. * @param {number} percentAsDecimal
  19166. * The volume percent as a decimal. Value will be between 0-1.
  19167. */
  19168. /**
  19169. * Set the value of the `muted` on the swf. `muted` indicates that the current
  19170. * audio level should be silent.
  19171. *
  19172. * @method Flash#setMuted
  19173. * @param {boolean} muted
  19174. * - True if the audio should be set to silent
  19175. * - False otherwise
  19176. */
  19177. /**
  19178. * Set the value of `defaultMuted` on the swf. `defaultMuted` indicates
  19179. * whether the media should start muted or not. Only changes the default state of the
  19180. * media. `muted` and `defaultMuted` can have different values. `muted` indicates the
  19181. * current state.
  19182. *
  19183. * @method Flash#setDefaultMuted
  19184. * @param {boolean} defaultMuted
  19185. * - True indicates that the media should start muted.
  19186. * - False indicates that the media should not start muted.
  19187. */
  19188. /* Flash Support Testing -------------------------------------------------------- */
  19189. /**
  19190. * Check if the Flash tech is currently supported.
  19191. *
  19192. * @return {boolean}
  19193. * - True if the flash tech is supported.
  19194. * - False otherwise.
  19195. */
  19196. Flash.isSupported = function () {
  19197. return Flash.version()[0] >= 10;
  19198. // return swfobject.hasFlashPlayerVersion('10');
  19199. };
  19200. // Add Source Handler pattern functions to this tech
  19201. _tech2['default'].withSourceHandlers(Flash);
  19202. /*
  19203. * Native source handler for flash, simply passes the source to the swf element.
  19204. *
  19205. * @property {Tech~SourceObject} source
  19206. * The source object
  19207. *
  19208. * @property {Flash} tech
  19209. * The instance of the Flash tech
  19210. */
  19211. Flash.nativeSourceHandler = {};
  19212. /**
  19213. * Check if the Flash can play the given mime type.
  19214. *
  19215. * @param {string} type
  19216. * The mimetype to check
  19217. *
  19218. * @return {string}
  19219. * 'maybe', or '' (empty string)
  19220. */
  19221. Flash.nativeSourceHandler.canPlayType = function (type) {
  19222. if (type in Flash.formats) {
  19223. return 'maybe';
  19224. }
  19225. return '';
  19226. };
  19227. /**
  19228. * Check if the media element can handle a source natively.
  19229. *
  19230. * @param {Tech~SourceObject} source
  19231. * The source object
  19232. *
  19233. * @param {Object} [options]
  19234. * Options to be passed to the tech.
  19235. *
  19236. * @return {string}
  19237. * 'maybe', or '' (empty string).
  19238. */
  19239. Flash.nativeSourceHandler.canHandleSource = function (source, options) {
  19240. var type = void 0;
  19241. function guessMimeType(src) {
  19242. var ext = Url.getFileExtension(src);
  19243. if (ext) {
  19244. return 'video/' + ext;
  19245. }
  19246. return '';
  19247. }
  19248. if (!source.type) {
  19249. type = guessMimeType(source.src);
  19250. } else {
  19251. // Strip code information from the type because we don't get that specific
  19252. type = source.type.replace(/;.*/, '').toLowerCase();
  19253. }
  19254. return Flash.nativeSourceHandler.canPlayType(type);
  19255. };
  19256. /**
  19257. * Pass the source to the swf.
  19258. *
  19259. * @param {Tech~SourceObject} source
  19260. * The source object
  19261. *
  19262. * @param {Flash} tech
  19263. * The instance of the Flash tech
  19264. *
  19265. * @param {Object} [options]
  19266. * The options to pass to the source
  19267. */
  19268. Flash.nativeSourceHandler.handleSource = function (source, tech, options) {
  19269. tech.setSrc(source.src);
  19270. };
  19271. /**
  19272. * noop for native source handler dispose, as cleanup will happen automatically.
  19273. */
  19274. Flash.nativeSourceHandler.dispose = function () {};
  19275. // Register the native source handler
  19276. Flash.registerSourceHandler(Flash.nativeSourceHandler);
  19277. /**
  19278. * Flash supported mime types.
  19279. *
  19280. * @constant {Object}
  19281. */
  19282. Flash.formats = {
  19283. 'video/flv': 'FLV',
  19284. 'video/x-flv': 'FLV',
  19285. 'video/mp4': 'MP4',
  19286. 'video/m4v': 'MP4'
  19287. };
  19288. /**
  19289. * Called when the the swf is "ready", and makes sure that the swf is really
  19290. * ready using {@link Flash#checkReady}
  19291. */
  19292. Flash.onReady = function (currSwf) {
  19293. var el = Dom.getEl(currSwf);
  19294. var tech = el && el.tech;
  19295. // if there is no el then the tech has been disposed
  19296. // and the tech element was removed from the player div
  19297. if (tech && tech.el()) {
  19298. // check that the flash object is really ready
  19299. Flash.checkReady(tech);
  19300. }
  19301. };
  19302. /**
  19303. * The SWF isn't always ready when it says it is. Sometimes the API functions still
  19304. * need to be added to the object. If it's not ready, we set a timeout to check again
  19305. * shortly.
  19306. *
  19307. * @param {Flash} tech
  19308. * The instance of the flash tech to check.
  19309. */
  19310. Flash.checkReady = function (tech) {
  19311. // stop worrying if the tech has been disposed
  19312. if (!tech.el()) {
  19313. return;
  19314. }
  19315. // check if API property exists
  19316. if (tech.el().vjs_getProperty) {
  19317. // tell tech it's ready
  19318. tech.triggerReady();
  19319. } else {
  19320. // wait longer
  19321. this.setTimeout(function () {
  19322. Flash.checkReady(tech);
  19323. }, 50);
  19324. }
  19325. };
  19326. /**
  19327. * Trigger events from the swf on the Flash Tech.
  19328. *
  19329. * @param {number} swfID
  19330. * The id of the swf that had the event
  19331. *
  19332. * @param {string} eventName
  19333. * The name of the event to trigger
  19334. */
  19335. Flash.onEvent = function (swfID, eventName) {
  19336. var tech = Dom.getEl(swfID).tech;
  19337. var args = Array.prototype.slice.call(arguments, 2);
  19338. // dispatch Flash events asynchronously for two reasons:
  19339. // - Flash swallows any exceptions generated by javascript it
  19340. // invokes
  19341. // - Flash is suspended until the javascript returns which may cause
  19342. // playback performance issues
  19343. tech.setTimeout(function () {
  19344. tech.trigger(eventName, args);
  19345. }, 1);
  19346. };
  19347. /**
  19348. * Log errors from the swf on the Flash tech.
  19349. *
  19350. * @param {number} swfID
  19351. * The id of the swf that had an error.
  19352. *
  19353. * @param {string} The error string
  19354. * The error to set on the Flash Tech.
  19355. *
  19356. * @return {MediaError|undefined}
  19357. * - Returns a MediaError when err is 'srcnotfound'
  19358. * - Returns undefined otherwise.
  19359. */
  19360. Flash.onError = function (swfID, err) {
  19361. var tech = Dom.getEl(swfID).tech;
  19362. // trigger MEDIA_ERR_SRC_NOT_SUPPORTED
  19363. if (err === 'srcnotfound') {
  19364. return tech.error(4);
  19365. }
  19366. // trigger a custom error
  19367. tech.error('FLASH: ' + err);
  19368. };
  19369. /**
  19370. * Get the current version of Flash that is in use on the page.
  19371. *
  19372. * @return {Array}
  19373. * an array of versions that are available.
  19374. */
  19375. Flash.version = function () {
  19376. var version = '0,0,0';
  19377. // IE
  19378. try {
  19379. version = new _window2['default'].ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
  19380. // other browsers
  19381. } catch (e) {
  19382. try {
  19383. if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin) {
  19384. version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
  19385. }
  19386. } catch (err) {
  19387. // satisfy linter
  19388. }
  19389. }
  19390. return version.split(',');
  19391. };
  19392. /**
  19393. * Only use for non-iframe embeds.
  19394. *
  19395. * @param {Object} swf
  19396. * The videojs-swf object.
  19397. *
  19398. * @param {Object} flashVars
  19399. * Names and values to use as flash option variables.
  19400. *
  19401. * @param {Object} params
  19402. * Style parameters to set on the object.
  19403. *
  19404. * @param {Object} attributes
  19405. * Attributes to set on the element.
  19406. *
  19407. * @return {Element}
  19408. * The embeded Flash DOM element.
  19409. */
  19410. Flash.embed = function (swf, flashVars, params, attributes) {
  19411. var code = Flash.getEmbedCode(swf, flashVars, params, attributes);
  19412. // Get element by embedding code and retrieving created element
  19413. var obj = Dom.createEl('div', { innerHTML: code }).childNodes[0];
  19414. return obj;
  19415. };
  19416. /**
  19417. * Only use for non-iframe embeds.
  19418. *
  19419. * @param {Object} swf
  19420. * The videojs-swf object.
  19421. *
  19422. * @param {Object} flashVars
  19423. * Names and values to use as flash option variables.
  19424. *
  19425. * @param {Object} params
  19426. * Style parameters to set on the object.
  19427. *
  19428. * @param {Object} attributes
  19429. * Attributes to set on the element.
  19430. *
  19431. * @return {Element}
  19432. * The embeded Flash DOM element.
  19433. */
  19434. Flash.getEmbedCode = function (swf, flashVars, params, attributes) {
  19435. var objTag = '<object type="application/x-shockwave-flash" ';
  19436. var flashVarsString = '';
  19437. var paramsString = '';
  19438. var attrsString = '';
  19439. // Convert flash vars to string
  19440. if (flashVars) {
  19441. Object.getOwnPropertyNames(flashVars).forEach(function (key) {
  19442. flashVarsString += key + '=' + flashVars[key] + '&amp;';
  19443. });
  19444. }
  19445. // Add swf, flashVars, and other default params
  19446. params = (0, _obj.assign)({
  19447. movie: swf,
  19448. flashvars: flashVarsString,
  19449. // Required to talk to swf
  19450. allowScriptAccess: 'always',
  19451. // All should be default, but having security issues.
  19452. allowNetworking: 'all'
  19453. }, params);
  19454. // Create param tags string
  19455. Object.getOwnPropertyNames(params).forEach(function (key) {
  19456. paramsString += '<param name="' + key + '" value="' + params[key] + '" />';
  19457. });
  19458. attributes = (0, _obj.assign)({
  19459. // Add swf to attributes (need both for IE and Others to work)
  19460. data: swf,
  19461. // Default to 100% width/height
  19462. width: '100%',
  19463. height: '100%'
  19464. }, attributes);
  19465. // Create Attributes string
  19466. Object.getOwnPropertyNames(attributes).forEach(function (key) {
  19467. attrsString += key + '="' + attributes[key] + '" ';
  19468. });
  19469. return '' + objTag + attrsString + '>' + paramsString + '</object>';
  19470. };
  19471. // Run Flash through the RTMP decorator
  19472. (0, _flashRtmp2['default'])(Flash);
  19473. _component2['default'].registerComponent('Flash', Flash);
  19474. _tech2['default'].registerTech('Flash', Flash);
  19475. exports['default'] = Flash;
  19476. },{"../component":47,"../utils/dom.js":123,"../utils/obj":130,"../utils/time-ranges.js":132,"../utils/url.js":134,"./flash-rtmp":100,"./tech":104,"global/window":137}],102:[function(require,module,exports){
  19477. 'use strict';
  19478. exports.__esModule = true;
  19479. var _templateObject = _taggedTemplateLiteralLoose(['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n This may prevent text tracks from loading.'], ['Text Tracks are being loaded from another origin but the crossorigin attribute isn\'t used.\n This may prevent text tracks from loading.']);
  19480. var _tech = require('./tech.js');
  19481. var _tech2 = _interopRequireDefault(_tech);
  19482. var _component = require('../component');
  19483. var _component2 = _interopRequireDefault(_component);
  19484. var _dom = require('../utils/dom.js');
  19485. var Dom = _interopRequireWildcard(_dom);
  19486. var _url = require('../utils/url.js');
  19487. var Url = _interopRequireWildcard(_url);
  19488. var _fn = require('../utils/fn.js');
  19489. var Fn = _interopRequireWildcard(_fn);
  19490. var _log = require('../utils/log.js');
  19491. var _log2 = _interopRequireDefault(_log);
  19492. var _tsml = require('tsml');
  19493. var _tsml2 = _interopRequireDefault(_tsml);
  19494. var _browser = require('../utils/browser.js');
  19495. var browser = _interopRequireWildcard(_browser);
  19496. var _document = require('global/document');
  19497. var _document2 = _interopRequireDefault(_document);
  19498. var _window = require('global/window');
  19499. var _window2 = _interopRequireDefault(_window);
  19500. var _obj = require('../utils/obj');
  19501. var _mergeOptions = require('../utils/merge-options.js');
  19502. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  19503. var _toTitleCase = require('../utils/to-title-case.js');
  19504. var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
  19505. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  19506. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  19507. function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; }
  19508. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  19509. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  19510. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  19511. * @file html5.js
  19512. */
  19513. /**
  19514. * HTML5 Media Controller - Wrapper for HTML5 Media API
  19515. *
  19516. * @mixes Tech~SouceHandlerAdditions
  19517. * @extends Tech
  19518. */
  19519. var Html5 = function (_Tech) {
  19520. _inherits(Html5, _Tech);
  19521. /**
  19522. * Create an instance of this Tech.
  19523. *
  19524. * @param {Object} [options]
  19525. * The key/value store of player options.
  19526. *
  19527. * @param {Component~ReadyCallback} ready
  19528. * Callback function to call when the `HTML5` Tech is ready.
  19529. */
  19530. function Html5(options, ready) {
  19531. _classCallCheck(this, Html5);
  19532. var _this = _possibleConstructorReturn(this, _Tech.call(this, options, ready));
  19533. var source = options.source;
  19534. var crossoriginTracks = false;
  19535. // Set the source if one is provided
  19536. // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
  19537. // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
  19538. // anyway so the error gets fired.
  19539. if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
  19540. _this.setSource(source);
  19541. } else {
  19542. _this.handleLateInit_(_this.el_);
  19543. }
  19544. if (_this.el_.hasChildNodes()) {
  19545. var nodes = _this.el_.childNodes;
  19546. var nodesLength = nodes.length;
  19547. var removeNodes = [];
  19548. while (nodesLength--) {
  19549. var node = nodes[nodesLength];
  19550. var nodeName = node.nodeName.toLowerCase();
  19551. if (nodeName === 'track') {
  19552. if (!_this.featuresNativeTextTracks) {
  19553. // Empty video tag tracks so the built-in player doesn't use them also.
  19554. // This may not be fast enough to stop HTML5 browsers from reading the tags
  19555. // so we'll need to turn off any default tracks if we're manually doing
  19556. // captions and subtitles. videoElement.textTracks
  19557. removeNodes.push(node);
  19558. } else {
  19559. // store HTMLTrackElement and TextTrack to remote list
  19560. _this.remoteTextTrackEls().addTrackElement_(node);
  19561. _this.remoteTextTracks().addTrack_(node.track);
  19562. if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && Url.isCrossOrigin(node.src)) {
  19563. crossoriginTracks = true;
  19564. }
  19565. }
  19566. }
  19567. }
  19568. for (var i = 0; i < removeNodes.length; i++) {
  19569. _this.el_.removeChild(removeNodes[i]);
  19570. }
  19571. }
  19572. // TODO: add text tracks into this list
  19573. var trackTypes = ['audio', 'video'];
  19574. // ProxyNative Video/Audio Track
  19575. trackTypes.forEach(function (type) {
  19576. var elTracks = _this.el()[type + 'Tracks'];
  19577. var techTracks = _this[type + 'Tracks']();
  19578. var capitalType = (0, _toTitleCase2['default'])(type);
  19579. if (!_this['featuresNative' + capitalType + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
  19580. return;
  19581. }
  19582. _this['handle' + capitalType + 'TrackChange_'] = function (e) {
  19583. techTracks.trigger({
  19584. type: 'change',
  19585. target: techTracks,
  19586. currentTarget: techTracks,
  19587. srcElement: techTracks
  19588. });
  19589. };
  19590. _this['handle' + capitalType + 'TrackAdd_'] = function (e) {
  19591. return techTracks.addTrack(e.track);
  19592. };
  19593. _this['handle' + capitalType + 'TrackRemove_'] = function (e) {
  19594. return techTracks.removeTrack(e.track);
  19595. };
  19596. elTracks.addEventListener('change', _this['handle' + capitalType + 'TrackChange_']);
  19597. elTracks.addEventListener('addtrack', _this['handle' + capitalType + 'TrackAdd_']);
  19598. elTracks.addEventListener('removetrack', _this['handle' + capitalType + 'TrackRemove_']);
  19599. _this['removeOld' + capitalType + 'Tracks_'] = function (e) {
  19600. return _this.removeOldTracks_(techTracks, elTracks);
  19601. };
  19602. // Remove (native) tracks that are not used anymore
  19603. _this.on('loadstart', _this['removeOld' + capitalType + 'Tracks_']);
  19604. });
  19605. if (_this.featuresNativeTextTracks) {
  19606. if (crossoriginTracks) {
  19607. _log2['default'].warn((0, _tsml2['default'])(_templateObject));
  19608. }
  19609. _this.handleTextTrackChange_ = Fn.bind(_this, _this.handleTextTrackChange);
  19610. _this.handleTextTrackAdd_ = Fn.bind(_this, _this.handleTextTrackAdd);
  19611. _this.handleTextTrackRemove_ = Fn.bind(_this, _this.handleTextTrackRemove);
  19612. _this.proxyNativeTextTracks_();
  19613. }
  19614. // prevent iOS Safari from disabling metadata text tracks during native playback
  19615. _this.restoreMetadataTracksInIOSNativePlayer_();
  19616. // Determine if native controls should be used
  19617. // Our goal should be to get the custom controls on mobile solid everywhere
  19618. // so we can remove this all together. Right now this will block custom
  19619. // controls on touch enabled laptops like the Chrome Pixel
  19620. if ((browser.TOUCH_ENABLED || browser.IS_IPHONE || browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
  19621. _this.setControls(true);
  19622. }
  19623. // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
  19624. // into a `fullscreenchange` event
  19625. _this.proxyWebkitFullscreen_();
  19626. _this.triggerReady();
  19627. return _this;
  19628. }
  19629. /**
  19630. * Dispose of `HTML5` media element and remove all tracks.
  19631. */
  19632. Html5.prototype.dispose = function dispose() {
  19633. var _this2 = this;
  19634. // Un-ProxyNativeTracks
  19635. ['audio', 'video', 'text'].forEach(function (type) {
  19636. var capitalType = (0, _toTitleCase2['default'])(type);
  19637. var tl = _this2.el_[type + 'Tracks'];
  19638. if (tl && tl.removeEventListener) {
  19639. tl.removeEventListener('change', _this2['handle' + capitalType + 'TrackChange_']);
  19640. tl.removeEventListener('addtrack', _this2['handle' + capitalType + 'TrackAdd_']);
  19641. tl.removeEventListener('removetrack', _this2['handle' + capitalType + 'TrackRemove_']);
  19642. }
  19643. // Stop removing old text tracks
  19644. if (tl) {
  19645. _this2.off('loadstart', _this2['removeOld' + capitalType + 'Tracks_']);
  19646. }
  19647. });
  19648. Html5.disposeMediaElement(this.el_);
  19649. // tech will handle clearing of the emulated track list
  19650. _Tech.prototype.dispose.call(this);
  19651. };
  19652. /**
  19653. * When a captions track is enabled in the iOS Safari native player, all other
  19654. * tracks are disabled (including metadata tracks), which nulls all of their
  19655. * associated cue points. This will restore metadata tracks to their pre-fullscreen
  19656. * state in those cases so that cue points are not needlessly lost.
  19657. *
  19658. * @private
  19659. */
  19660. Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
  19661. var textTracks = this.textTracks();
  19662. var metadataTracksPreFullscreenState = void 0;
  19663. // captures a snapshot of every metadata track's current state
  19664. var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
  19665. metadataTracksPreFullscreenState = [];
  19666. for (var i = 0; i < textTracks.length; i++) {
  19667. var track = textTracks[i];
  19668. if (track.kind === 'metadata') {
  19669. metadataTracksPreFullscreenState.push({
  19670. track: track,
  19671. storedMode: track.mode
  19672. });
  19673. }
  19674. }
  19675. };
  19676. // snapshot each metadata track's initial state, and update the snapshot
  19677. // each time there is a track 'change' event
  19678. takeMetadataTrackSnapshot();
  19679. textTracks.addEventListener('change', takeMetadataTrackSnapshot);
  19680. var restoreTrackMode = function restoreTrackMode() {
  19681. for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
  19682. var storedTrack = metadataTracksPreFullscreenState[i];
  19683. if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
  19684. storedTrack.track.mode = storedTrack.storedMode;
  19685. }
  19686. }
  19687. // we only want this handler to be executed on the first 'change' event
  19688. textTracks.removeEventListener('change', restoreTrackMode);
  19689. };
  19690. // when we enter fullscreen playback, stop updating the snapshot and
  19691. // restore all track modes to their pre-fullscreen state
  19692. this.on('webkitbeginfullscreen', function () {
  19693. textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  19694. // remove the listener before adding it just in case it wasn't previously removed
  19695. textTracks.removeEventListener('change', restoreTrackMode);
  19696. textTracks.addEventListener('change', restoreTrackMode);
  19697. });
  19698. // start updating the snapshot again after leaving fullscreen
  19699. this.on('webkitendfullscreen', function () {
  19700. // remove the listener before adding it just in case it wasn't previously removed
  19701. textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  19702. textTracks.addEventListener('change', takeMetadataTrackSnapshot);
  19703. // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
  19704. textTracks.removeEventListener('change', restoreTrackMode);
  19705. });
  19706. };
  19707. /**
  19708. * Create the `Html5` Tech's DOM element.
  19709. *
  19710. * @return {Element}
  19711. * The element that gets created.
  19712. */
  19713. Html5.prototype.createEl = function createEl() {
  19714. var el = this.options_.tag;
  19715. // Check if this browser supports moving the element into the box.
  19716. // On the iPhone video will break if you move the element,
  19717. // So we have to create a brand new element.
  19718. // If we ingested the player div, we do not need to move the media element.
  19719. if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
  19720. // If the original tag is still there, clone and remove it.
  19721. if (el) {
  19722. var clone = el.cloneNode(true);
  19723. if (el.parentNode) {
  19724. el.parentNode.insertBefore(clone, el);
  19725. }
  19726. Html5.disposeMediaElement(el);
  19727. el = clone;
  19728. } else {
  19729. el = _document2['default'].createElement('video');
  19730. // determine if native controls should be used
  19731. var tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag);
  19732. var attributes = (0, _mergeOptions2['default'])({}, tagAttributes);
  19733. if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
  19734. delete attributes.controls;
  19735. }
  19736. Dom.setElAttributes(el, (0, _obj.assign)(attributes, {
  19737. id: this.options_.techId,
  19738. 'class': 'vjs-tech'
  19739. }));
  19740. }
  19741. el.playerId = this.options_.playerId;
  19742. }
  19743. // Update specific tag settings, in case they were overridden
  19744. var settingsAttrs = ['autoplay', 'preload', 'loop', 'muted', 'playsinline'];
  19745. for (var i = settingsAttrs.length - 1; i >= 0; i--) {
  19746. var attr = settingsAttrs[i];
  19747. var overwriteAttrs = {};
  19748. if (typeof this.options_[attr] !== 'undefined') {
  19749. overwriteAttrs[attr] = this.options_[attr];
  19750. }
  19751. Dom.setElAttributes(el, overwriteAttrs);
  19752. }
  19753. return el;
  19754. };
  19755. /**
  19756. * This will be triggered if the loadstart event has already fired, before videojs was
  19757. * ready. Two known examples of when this can happen are:
  19758. * 1. If we're loading the playback object after it has started loading
  19759. * 2. The media is already playing the (often with autoplay on) then
  19760. *
  19761. * This function will fire another loadstart so that videojs can catchup.
  19762. *
  19763. * @fires Tech#loadstart
  19764. *
  19765. * @return {undefined}
  19766. * returns nothing.
  19767. */
  19768. Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
  19769. if (el.networkState === 0 || el.networkState === 3) {
  19770. // The video element hasn't started loading the source yet
  19771. // or didn't find a source
  19772. return;
  19773. }
  19774. if (el.readyState === 0) {
  19775. // NetworkState is set synchronously BUT loadstart is fired at the
  19776. // end of the current stack, usually before setInterval(fn, 0).
  19777. // So at this point we know loadstart may have already fired or is
  19778. // about to fire, and either way the player hasn't seen it yet.
  19779. // We don't want to fire loadstart prematurely here and cause a
  19780. // double loadstart so we'll wait and see if it happens between now
  19781. // and the next loop, and fire it if not.
  19782. // HOWEVER, we also want to make sure it fires before loadedmetadata
  19783. // which could also happen between now and the next loop, so we'll
  19784. // watch for that also.
  19785. var loadstartFired = false;
  19786. var setLoadstartFired = function setLoadstartFired() {
  19787. loadstartFired = true;
  19788. };
  19789. this.on('loadstart', setLoadstartFired);
  19790. var triggerLoadstart = function triggerLoadstart() {
  19791. // We did miss the original loadstart. Make sure the player
  19792. // sees loadstart before loadedmetadata
  19793. if (!loadstartFired) {
  19794. this.trigger('loadstart');
  19795. }
  19796. };
  19797. this.on('loadedmetadata', triggerLoadstart);
  19798. this.ready(function () {
  19799. this.off('loadstart', setLoadstartFired);
  19800. this.off('loadedmetadata', triggerLoadstart);
  19801. if (!loadstartFired) {
  19802. // We did miss the original native loadstart. Fire it now.
  19803. this.trigger('loadstart');
  19804. }
  19805. });
  19806. return;
  19807. }
  19808. // From here on we know that loadstart already fired and we missed it.
  19809. // The other readyState events aren't as much of a problem if we double
  19810. // them, so not going to go to as much trouble as loadstart to prevent
  19811. // that unless we find reason to.
  19812. var eventsToTrigger = ['loadstart'];
  19813. // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
  19814. eventsToTrigger.push('loadedmetadata');
  19815. // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
  19816. if (el.readyState >= 2) {
  19817. eventsToTrigger.push('loadeddata');
  19818. }
  19819. // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
  19820. if (el.readyState >= 3) {
  19821. eventsToTrigger.push('canplay');
  19822. }
  19823. // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
  19824. if (el.readyState >= 4) {
  19825. eventsToTrigger.push('canplaythrough');
  19826. }
  19827. // We still need to give the player time to add event listeners
  19828. this.ready(function () {
  19829. eventsToTrigger.forEach(function (type) {
  19830. this.trigger(type);
  19831. }, this);
  19832. });
  19833. };
  19834. /**
  19835. * Add event listeners to native text track events. This adds the native text tracks
  19836. * to our emulated {@link TextTrackList}.
  19837. */
  19838. Html5.prototype.proxyNativeTextTracks_ = function proxyNativeTextTracks_() {
  19839. var tt = this.el().textTracks;
  19840. if (tt) {
  19841. // Add tracks - if player is initialised after DOM loaded, textTracks
  19842. // will not trigger addtrack
  19843. for (var i = 0; i < tt.length; i++) {
  19844. this.textTracks().addTrack_(tt[i]);
  19845. }
  19846. if (tt.addEventListener) {
  19847. tt.addEventListener('change', this.handleTextTrackChange_);
  19848. tt.addEventListener('addtrack', this.handleTextTrackAdd_);
  19849. tt.addEventListener('removetrack', this.handleTextTrackRemove_);
  19850. }
  19851. // Remove (native) texttracks that are not used anymore
  19852. this.on('loadstart', this.removeOldTextTracks_);
  19853. }
  19854. };
  19855. /**
  19856. * Handle any {@link TextTrackList} `change` event.
  19857. *
  19858. * @param {EventTarget~Event} e
  19859. * The `change` event that caused this to run.
  19860. *
  19861. * @listens TextTrackList#change
  19862. */
  19863. Html5.prototype.handleTextTrackChange = function handleTextTrackChange(e) {
  19864. var tt = this.textTracks();
  19865. this.textTracks().trigger({
  19866. type: 'change',
  19867. target: tt,
  19868. currentTarget: tt,
  19869. srcElement: tt
  19870. });
  19871. };
  19872. /**
  19873. * Handle any {@link TextTrackList} `addtrack` event.
  19874. *
  19875. * @param {EventTarget~Event} e
  19876. * The `addtrack` event that caused this to run.
  19877. *
  19878. * @listens TextTrackList#addtrack
  19879. */
  19880. Html5.prototype.handleTextTrackAdd = function handleTextTrackAdd(e) {
  19881. this.textTracks().addTrack_(e.track);
  19882. };
  19883. /**
  19884. * Handle any {@link TextTrackList} `removetrack` event.
  19885. *
  19886. * @param {EventTarget~Event} e
  19887. * The `removetrack` event that caused this to run.
  19888. *
  19889. * @listens TextTrackList#removetrack
  19890. */
  19891. Html5.prototype.handleTextTrackRemove = function handleTextTrackRemove(e) {
  19892. this.textTracks().removeTrack_(e.track);
  19893. };
  19894. /**
  19895. * This function removes any {@link AudioTrack}s, {@link VideoTrack}s, or
  19896. * {@link TextTrack}s that are not in the media elements TrackList.
  19897. *
  19898. * @param {TrackList} techTracks
  19899. * HTML5 Tech's TrackList to search through
  19900. *
  19901. * @param {TrackList} elTracks
  19902. * HTML5 media elements TrackList to search trough.
  19903. *
  19904. * @private
  19905. */
  19906. Html5.prototype.removeOldTracks_ = function removeOldTracks_(techTracks, elTracks) {
  19907. // This will loop over the techTracks and check if they are still used by the HTML5 media element
  19908. // If not, they will be removed from the emulated list
  19909. var removeTracks = [];
  19910. if (!elTracks) {
  19911. return;
  19912. }
  19913. for (var i = 0; i < techTracks.length; i++) {
  19914. var techTrack = techTracks[i];
  19915. var found = false;
  19916. for (var j = 0; j < elTracks.length; j++) {
  19917. if (elTracks[j] === techTrack) {
  19918. found = true;
  19919. break;
  19920. }
  19921. }
  19922. if (!found) {
  19923. removeTracks.push(techTrack);
  19924. }
  19925. }
  19926. for (var _i = 0; _i < removeTracks.length; _i++) {
  19927. var track = removeTracks[_i];
  19928. techTracks.removeTrack_(track);
  19929. }
  19930. };
  19931. /**
  19932. * Remove {@link TextTrack}s that dont exist in the native track list from our
  19933. * emulated {@link TextTrackList}.
  19934. *
  19935. * @listens Tech#loadstart
  19936. */
  19937. Html5.prototype.removeOldTextTracks_ = function removeOldTextTracks_(e) {
  19938. var techTracks = this.textTracks();
  19939. var elTracks = this.el().textTracks;
  19940. this.removeOldTracks_(techTracks, elTracks);
  19941. };
  19942. /**
  19943. * Called by {@link Player#play} to play using the `Html5` `Tech`.
  19944. */
  19945. Html5.prototype.play = function play() {
  19946. var playPromise = this.el_.play();
  19947. // Catch/silence error when a pause interrupts a play request
  19948. // on browsers which return a promise
  19949. if (playPromise !== undefined && typeof playPromise.then === 'function') {
  19950. playPromise.then(null, function (e) {});
  19951. }
  19952. };
  19953. /**
  19954. * Set current time for the `HTML5` tech.
  19955. *
  19956. * @param {number} seconds
  19957. * Set the current time of the media to this.
  19958. */
  19959. Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
  19960. try {
  19961. this.el_.currentTime = seconds;
  19962. } catch (e) {
  19963. (0, _log2['default'])(e, 'Video is not ready. (Video.js)');
  19964. // this.warning(VideoJS.warnings.videoNotReady);
  19965. }
  19966. };
  19967. /**
  19968. * Get the current duration of the HTML5 media element.
  19969. *
  19970. * @return {number}
  19971. * The duration of the media or 0 if there is no duration.
  19972. */
  19973. Html5.prototype.duration = function duration() {
  19974. var _this3 = this;
  19975. // Android Chrome will report duration as Infinity for VOD HLS until after
  19976. // playback has started, which triggers the live display erroneously.
  19977. // Return NaN if playback has not started and trigger a durationupdate once
  19978. // the duration can be reliably known.
  19979. if (this.el_.duration === Infinity && browser.IS_ANDROID && browser.IS_CHROME) {
  19980. if (this.el_.currentTime === 0) {
  19981. // Wait for the first `timeupdate` with currentTime > 0 - there may be
  19982. // several with 0
  19983. var checkProgress = function checkProgress() {
  19984. if (_this3.el_.currentTime > 0) {
  19985. // Trigger durationchange for genuinely live video
  19986. if (_this3.el_.duration === Infinity) {
  19987. _this3.trigger('durationchange');
  19988. }
  19989. _this3.off('timeupdate', checkProgress);
  19990. }
  19991. };
  19992. this.on('timeupdate', checkProgress);
  19993. return NaN;
  19994. }
  19995. }
  19996. return this.el_.duration || NaN;
  19997. };
  19998. /**
  19999. * Get the current width of the HTML5 media element.
  20000. *
  20001. * @return {number}
  20002. * The width of the HTML5 media element.
  20003. */
  20004. Html5.prototype.width = function width() {
  20005. return this.el_.offsetWidth;
  20006. };
  20007. /**
  20008. * Get the current height of the HTML5 media element.
  20009. *
  20010. * @return {number}
  20011. * The heigth of the HTML5 media element.
  20012. */
  20013. Html5.prototype.height = function height() {
  20014. return this.el_.offsetHeight;
  20015. };
  20016. /**
  20017. * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
  20018. * `fullscreenchange` event.
  20019. *
  20020. * @private
  20021. * @fires fullscreenchange
  20022. * @listens webkitendfullscreen
  20023. * @listens webkitbeginfullscreen
  20024. * @listens webkitbeginfullscreen
  20025. */
  20026. Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
  20027. var _this4 = this;
  20028. if (!('webkitDisplayingFullscreen' in this.el_)) {
  20029. return;
  20030. }
  20031. var endFn = function endFn() {
  20032. this.trigger('fullscreenchange', { isFullscreen: false });
  20033. };
  20034. var beginFn = function beginFn() {
  20035. if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
  20036. this.one('webkitendfullscreen', endFn);
  20037. this.trigger('fullscreenchange', { isFullscreen: true });
  20038. }
  20039. };
  20040. this.on('webkitbeginfullscreen', beginFn);
  20041. this.on('dispose', function () {
  20042. _this4.off('webkitbeginfullscreen', beginFn);
  20043. _this4.off('webkitendfullscreen', endFn);
  20044. });
  20045. };
  20046. /**
  20047. * Check if fullscreen is supported on the current playback device.
  20048. *
  20049. * @return {boolean}
  20050. * - True if fullscreen is supported.
  20051. * - False if fullscreen is not supported.
  20052. */
  20053. Html5.prototype.supportsFullScreen = function supportsFullScreen() {
  20054. if (typeof this.el_.webkitEnterFullScreen === 'function') {
  20055. var userAgent = _window2['default'].navigator && _window2['default'].navigator.userAgent || '';
  20056. // Seems to be broken in Chromium/Chrome && Safari in Leopard
  20057. if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
  20058. return true;
  20059. }
  20060. }
  20061. return false;
  20062. };
  20063. /**
  20064. * Request that the `HTML5` Tech enter fullscreen.
  20065. */
  20066. Html5.prototype.enterFullScreen = function enterFullScreen() {
  20067. var video = this.el_;
  20068. if (video.paused && video.networkState <= video.HAVE_METADATA) {
  20069. // attempt to prime the video element for programmatic access
  20070. // this isn't necessary on the desktop but shouldn't hurt
  20071. this.el_.play();
  20072. // playing and pausing synchronously during the transition to fullscreen
  20073. // can get iOS ~6.1 devices into a play/pause loop
  20074. this.setTimeout(function () {
  20075. video.pause();
  20076. video.webkitEnterFullScreen();
  20077. }, 0);
  20078. } else {
  20079. video.webkitEnterFullScreen();
  20080. }
  20081. };
  20082. /**
  20083. * Request that the `HTML5` Tech exit fullscreen.
  20084. */
  20085. Html5.prototype.exitFullScreen = function exitFullScreen() {
  20086. this.el_.webkitExitFullScreen();
  20087. };
  20088. /**
  20089. * A getter/setter for the `Html5` Tech's source object.
  20090. * > Note: Please use {@link Html5#setSource}
  20091. *
  20092. * @param {Tech~SourceObject} [src]
  20093. * The source object you want to set on the `HTML5` techs element.
  20094. *
  20095. * @return {Tech~SourceObject|undefined}
  20096. * - The current source object when a source is not passed in.
  20097. * - undefined when setting
  20098. *
  20099. * @deprecated Since version 5.
  20100. */
  20101. Html5.prototype.src = function src(_src) {
  20102. if (_src === undefined) {
  20103. return this.el_.src;
  20104. }
  20105. // Setting src through `src` instead of `setSrc` will be deprecated
  20106. this.setSrc(_src);
  20107. };
  20108. /**
  20109. * Reset the tech by removing all sources and then calling
  20110. * {@link Html5.resetMediaElement}.
  20111. */
  20112. Html5.prototype.reset = function reset() {
  20113. Html5.resetMediaElement(this.el_);
  20114. };
  20115. /**
  20116. * Get the current source on the `HTML5` Tech. Falls back to returning the source from
  20117. * the HTML5 media element.
  20118. *
  20119. * @return {Tech~SourceObject}
  20120. * The current source object from the HTML5 tech. With a fallback to the
  20121. * elements source.
  20122. */
  20123. Html5.prototype.currentSrc = function currentSrc() {
  20124. if (this.currentSource_) {
  20125. return this.currentSource_.src;
  20126. }
  20127. return this.el_.currentSrc;
  20128. };
  20129. /**
  20130. * Set controls attribute for the HTML5 media Element.
  20131. *
  20132. * @param {string} val
  20133. * Value to set the controls attribute to
  20134. */
  20135. Html5.prototype.setControls = function setControls(val) {
  20136. this.el_.controls = !!val;
  20137. };
  20138. /**
  20139. * Create and returns a remote {@link TextTrack} object.
  20140. *
  20141. * @param {string} kind
  20142. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  20143. *
  20144. * @param {string} [label]
  20145. * Label to identify the text track
  20146. *
  20147. * @param {string} [language]
  20148. * Two letter language abbreviation
  20149. *
  20150. * @return {TextTrack}
  20151. * The TextTrack that gets created.
  20152. */
  20153. Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  20154. if (!this.featuresNativeTextTracks) {
  20155. return _Tech.prototype.addTextTrack.call(this, kind, label, language);
  20156. }
  20157. return this.el_.addTextTrack(kind, label, language);
  20158. };
  20159. /**
  20160. * Creates either native TextTrack or an emulated TextTrack depending
  20161. * on the value of `featuresNativeTextTracks`
  20162. *
  20163. * @param {Object} options
  20164. * The object should contain the options to intialize the TextTrack with.
  20165. *
  20166. * @param {string} [options.kind]
  20167. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
  20168. *
  20169. * @param {string} [options.label].
  20170. * Label to identify the text track
  20171. *
  20172. * @param {string} [options.language]
  20173. * Two letter language abbreviation.
  20174. *
  20175. * @param {boolean} [options.default]
  20176. * Default this track to on.
  20177. *
  20178. * @param {string} [options.id]
  20179. * The internal id to assign this track.
  20180. *
  20181. * @param {string} [options.src]
  20182. * A source url for the track.
  20183. *
  20184. * @return {HTMLTrackElement}
  20185. * The track element that gets created.
  20186. */
  20187. Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
  20188. if (!this.featuresNativeTextTracks) {
  20189. return _Tech.prototype.createRemoteTextTrack.call(this, options);
  20190. }
  20191. var htmlTrackElement = _document2['default'].createElement('track');
  20192. if (options.kind) {
  20193. htmlTrackElement.kind = options.kind;
  20194. }
  20195. if (options.label) {
  20196. htmlTrackElement.label = options.label;
  20197. }
  20198. if (options.language || options.srclang) {
  20199. htmlTrackElement.srclang = options.language || options.srclang;
  20200. }
  20201. if (options['default']) {
  20202. htmlTrackElement['default'] = options['default'];
  20203. }
  20204. if (options.id) {
  20205. htmlTrackElement.id = options.id;
  20206. }
  20207. if (options.src) {
  20208. htmlTrackElement.src = options.src;
  20209. }
  20210. return htmlTrackElement;
  20211. };
  20212. /**
  20213. * Creates a remote text track object and returns an html track element.
  20214. *
  20215. * @param {Object} options The object should contain values for
  20216. * kind, language, label, and src (location of the WebVTT file)
  20217. * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
  20218. * automatically removed from the video element whenever the source changes
  20219. * @return {HTMLTrackElement} An Html Track Element.
  20220. * This can be an emulated {@link HTMLTrackElement} or a native one.
  20221. * @deprecated The default value of the "manualCleanup" parameter will default
  20222. * to "false" in upcoming versions of Video.js
  20223. */
  20224. Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
  20225. var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
  20226. if (this.featuresNativeTextTracks) {
  20227. this.el().appendChild(htmlTrackElement);
  20228. }
  20229. return htmlTrackElement;
  20230. };
  20231. /**
  20232. * Remove remote `TextTrack` from `TextTrackList` object
  20233. *
  20234. * @param {TextTrack} track
  20235. * `TextTrack` object to remove
  20236. */
  20237. Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
  20238. _Tech.prototype.removeRemoteTextTrack.call(this, track);
  20239. if (this.featuresNativeTextTracks) {
  20240. var tracks = this.$$('track');
  20241. var i = tracks.length;
  20242. while (i--) {
  20243. if (track === tracks[i] || track === tracks[i].track) {
  20244. this.el().removeChild(tracks[i]);
  20245. }
  20246. }
  20247. }
  20248. };
  20249. /**
  20250. * Get the value of `playsinline` from the media element. `playsinline` indicates
  20251. * to the browser that non-fullscreen playback is preferred when fullscreen
  20252. * playback is the native default, such as in iOS Safari.
  20253. *
  20254. * @method Html5#playsinline
  20255. * @return {boolean}
  20256. * - The value of `playsinline` from the media element.
  20257. * - True indicates that the media should play inline.
  20258. * - False indicates that the media should not play inline.
  20259. *
  20260. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  20261. */
  20262. Html5.prototype.playsinline = function playsinline() {
  20263. return this.el_.hasAttribute('playsinline');
  20264. };
  20265. /**
  20266. * Set the value of `playsinline` from the media element. `playsinline` indicates
  20267. * to the browser that non-fullscreen playback is preferred when fullscreen
  20268. * playback is the native default, such as in iOS Safari.
  20269. *
  20270. * @method Html5#setPlaysinline
  20271. * @param {boolean} playsinline
  20272. * - True indicates that the media should play inline.
  20273. * - False indicates that the media should not play inline.
  20274. *
  20275. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  20276. */
  20277. Html5.prototype.setPlaysinline = function setPlaysinline(value) {
  20278. if (value) {
  20279. this.el_.setAttribute('playsinline', 'playsinline');
  20280. } else {
  20281. this.el_.removeAttribute('playsinline');
  20282. }
  20283. };
  20284. /**
  20285. * Gets available media playback quality metrics as specified by the W3C's Media
  20286. * Playback Quality API.
  20287. *
  20288. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  20289. *
  20290. * @return {Object}
  20291. * An object with supported media playback quality metrics
  20292. */
  20293. Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  20294. if (typeof this.el().getVideoPlaybackQuality === 'function') {
  20295. return this.el().getVideoPlaybackQuality();
  20296. }
  20297. var videoPlaybackQuality = {};
  20298. if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
  20299. videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
  20300. videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
  20301. }
  20302. if (_window2['default'].performance && typeof _window2['default'].performance.now === 'function') {
  20303. videoPlaybackQuality.creationTime = _window2['default'].performance.now();
  20304. } else if (_window2['default'].performance && _window2['default'].performance.timing && typeof _window2['default'].performance.timing.navigationStart === 'number') {
  20305. videoPlaybackQuality.creationTime = _window2['default'].Date.now() - _window2['default'].performance.timing.navigationStart;
  20306. }
  20307. return videoPlaybackQuality;
  20308. };
  20309. return Html5;
  20310. }(_tech2['default']);
  20311. /* HTML5 Support Testing ---------------------------------------------------- */
  20312. if (Dom.isReal()) {
  20313. /**
  20314. * Element for testing browser HTML5 media capabilities
  20315. *
  20316. * @type {Element}
  20317. * @constant
  20318. * @private
  20319. */
  20320. Html5.TEST_VID = _document2['default'].createElement('video');
  20321. var track = _document2['default'].createElement('track');
  20322. track.kind = 'captions';
  20323. track.srclang = 'en';
  20324. track.label = 'English';
  20325. Html5.TEST_VID.appendChild(track);
  20326. }
  20327. /**
  20328. * Check if HTML5 media is supported by this browser/device.
  20329. *
  20330. * @return {boolean}
  20331. * - True if HTML5 media is supported.
  20332. * - False if HTML5 media is not supported.
  20333. */
  20334. Html5.isSupported = function () {
  20335. // IE9 with no Media Player is a LIAR! (#984)
  20336. try {
  20337. Html5.TEST_VID.volume = 0.5;
  20338. } catch (e) {
  20339. return false;
  20340. }
  20341. return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
  20342. };
  20343. /**
  20344. * Check if the volume can be changed in this browser/device.
  20345. * Volume cannot be changed in a lot of mobile devices.
  20346. * Specifically, it can't be changed from 1 on iOS.
  20347. *
  20348. * @return {boolean}
  20349. * - True if volume can be controlled
  20350. * - False otherwise
  20351. */
  20352. Html5.canControlVolume = function () {
  20353. // IE will error if Windows Media Player not installed #3315
  20354. try {
  20355. var volume = Html5.TEST_VID.volume;
  20356. Html5.TEST_VID.volume = volume / 2 + 0.1;
  20357. return volume !== Html5.TEST_VID.volume;
  20358. } catch (e) {
  20359. return false;
  20360. }
  20361. };
  20362. /**
  20363. * Check if the playback rate can be changed in this browser/device.
  20364. *
  20365. * @return {boolean}
  20366. * - True if playback rate can be controlled
  20367. * - False otherwise
  20368. */
  20369. Html5.canControlPlaybackRate = function () {
  20370. // Playback rate API is implemented in Android Chrome, but doesn't do anything
  20371. // https://github.com/videojs/video.js/issues/3180
  20372. if (browser.IS_ANDROID && browser.IS_CHROME && browser.CHROME_VERSION < 58) {
  20373. return false;
  20374. }
  20375. // IE will error if Windows Media Player not installed #3315
  20376. try {
  20377. var playbackRate = Html5.TEST_VID.playbackRate;
  20378. Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
  20379. return playbackRate !== Html5.TEST_VID.playbackRate;
  20380. } catch (e) {
  20381. return false;
  20382. }
  20383. };
  20384. /**
  20385. * Check to see if native `TextTrack`s are supported by this browser/device.
  20386. *
  20387. * @return {boolean}
  20388. * - True if native `TextTrack`s are supported.
  20389. * - False otherwise
  20390. */
  20391. Html5.supportsNativeTextTracks = function () {
  20392. return browser.IS_ANY_SAFARI;
  20393. };
  20394. /**
  20395. * Check to see if native `VideoTrack`s are supported by this browser/device
  20396. *
  20397. * @return {boolean}
  20398. * - True if native `VideoTrack`s are supported.
  20399. * - False otherwise
  20400. */
  20401. Html5.supportsNativeVideoTracks = function () {
  20402. return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
  20403. };
  20404. /**
  20405. * Check to see if native `AudioTrack`s are supported by this browser/device
  20406. *
  20407. * @return {boolean}
  20408. * - True if native `AudioTrack`s are supported.
  20409. * - False otherwise
  20410. */
  20411. Html5.supportsNativeAudioTracks = function () {
  20412. return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
  20413. };
  20414. /**
  20415. * An array of events available on the Html5 tech.
  20416. *
  20417. * @private
  20418. * @type {Array}
  20419. */
  20420. Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'volumechange'];
  20421. /**
  20422. * Boolean indicating whether the `Tech` supports volume control.
  20423. *
  20424. * @type {boolean}
  20425. * @default {@link Html5.canControlVolume}
  20426. */
  20427. Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
  20428. /**
  20429. * Boolean indicating whether the `Tech` supports changing the speed at which the media
  20430. * plays. Examples:
  20431. * - Set player to play 2x (twice) as fast
  20432. * - Set player to play 0.5x (half) as fast
  20433. *
  20434. * @type {boolean}
  20435. * @default {@link Html5.canControlPlaybackRate}
  20436. */
  20437. Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
  20438. /**
  20439. * Boolean indicating whether the `HTML5` tech currently supports the media element
  20440. * moving in the DOM. iOS breaks if you move the media element, so this is set this to
  20441. * false there. Everywhere else this should be true.
  20442. *
  20443. * @type {boolean}
  20444. * @default
  20445. */
  20446. Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS;
  20447. // TODO: Previous comment: No longer appears to be used. Can probably be removed.
  20448. // Is this true?
  20449. /**
  20450. * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
  20451. * when going into fullscreen.
  20452. *
  20453. * @type {boolean}
  20454. * @default
  20455. */
  20456. Html5.prototype.featuresFullscreenResize = true;
  20457. /**
  20458. * Boolean indicating whether the `HTML5` tech currently supports the progress event.
  20459. * If this is false, manual `progress` events will be triggred instead.
  20460. *
  20461. * @type {boolean}
  20462. * @default
  20463. */
  20464. Html5.prototype.featuresProgressEvents = true;
  20465. /**
  20466. * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
  20467. * If this is false, manual `timeupdate` events will be triggred instead.
  20468. *
  20469. * @default
  20470. */
  20471. Html5.prototype.featuresTimeupdateEvents = true;
  20472. /**
  20473. * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
  20474. *
  20475. * @type {boolean}
  20476. * @default {@link Html5.supportsNativeTextTracks}
  20477. */
  20478. Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
  20479. /**
  20480. * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
  20481. *
  20482. * @type {boolean}
  20483. * @default {@link Html5.supportsNativeVideoTracks}
  20484. */
  20485. Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
  20486. /**
  20487. * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
  20488. *
  20489. * @type {boolean}
  20490. * @default {@link Html5.supportsNativeAudioTracks}
  20491. */
  20492. Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
  20493. // HTML5 Feature detection and Device Fixes --------------------------------- //
  20494. var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
  20495. var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
  20496. var mp4RE = /^video\/mp4/i;
  20497. Html5.patchCanPlayType = function () {
  20498. // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
  20499. if (browser.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX) {
  20500. Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
  20501. if (type && mpegurlRE.test(type)) {
  20502. return 'maybe';
  20503. }
  20504. return canPlayType.call(this, type);
  20505. };
  20506. // Override Android 2.2 and less canPlayType method which is broken
  20507. } else if (browser.IS_OLD_ANDROID) {
  20508. Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
  20509. if (type && mp4RE.test(type)) {
  20510. return 'maybe';
  20511. }
  20512. return canPlayType.call(this, type);
  20513. };
  20514. }
  20515. };
  20516. Html5.unpatchCanPlayType = function () {
  20517. var r = Html5.TEST_VID.constructor.prototype.canPlayType;
  20518. Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
  20519. return r;
  20520. };
  20521. // by default, patch the media element
  20522. Html5.patchCanPlayType();
  20523. Html5.disposeMediaElement = function (el) {
  20524. if (!el) {
  20525. return;
  20526. }
  20527. if (el.parentNode) {
  20528. el.parentNode.removeChild(el);
  20529. }
  20530. // remove any child track or source nodes to prevent their loading
  20531. while (el.hasChildNodes()) {
  20532. el.removeChild(el.firstChild);
  20533. }
  20534. // remove any src reference. not setting `src=''` because that causes a warning
  20535. // in firefox
  20536. el.removeAttribute('src');
  20537. // force the media element to update its loading state by calling load()
  20538. // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
  20539. if (typeof el.load === 'function') {
  20540. // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
  20541. (function () {
  20542. try {
  20543. el.load();
  20544. } catch (e) {
  20545. // not supported
  20546. }
  20547. })();
  20548. }
  20549. };
  20550. Html5.resetMediaElement = function (el) {
  20551. if (!el) {
  20552. return;
  20553. }
  20554. var sources = el.querySelectorAll('source');
  20555. var i = sources.length;
  20556. while (i--) {
  20557. el.removeChild(sources[i]);
  20558. }
  20559. // remove any src reference.
  20560. // not setting `src=''` because that throws an error
  20561. el.removeAttribute('src');
  20562. if (typeof el.load === 'function') {
  20563. // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
  20564. (function () {
  20565. try {
  20566. el.load();
  20567. } catch (e) {
  20568. // satisfy linter
  20569. }
  20570. })();
  20571. }
  20572. };
  20573. /* Native HTML5 element property wrapping ----------------------------------- */
  20574. // Wrap native properties with a getter
  20575. [
  20576. /**
  20577. * Get the value of `paused` from the media element. `paused` indicates whether the media element
  20578. * is currently paused or not.
  20579. *
  20580. * @method Html5#paused
  20581. * @return {boolean}
  20582. * The value of `paused` from the media element.
  20583. *
  20584. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
  20585. */
  20586. 'paused',
  20587. /**
  20588. * Get the value of `currentTime` from the media element. `currentTime` indicates
  20589. * the current second that the media is at in playback.
  20590. *
  20591. * @method Html5#currentTime
  20592. * @return {number}
  20593. * The value of `currentTime` from the media element.
  20594. *
  20595. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
  20596. */
  20597. 'currentTime',
  20598. /**
  20599. * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
  20600. * object that represents the parts of the media that are already downloaded and
  20601. * available for playback.
  20602. *
  20603. * @method Html5#buffered
  20604. * @return {TimeRange}
  20605. * The value of `buffered` from the media element.
  20606. *
  20607. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
  20608. */
  20609. 'buffered',
  20610. /**
  20611. * Get the value of `volume` from the media element. `volume` indicates
  20612. * the current playback volume of audio for a media. `volume` will be a value from 0
  20613. * (silent) to 1 (loudest and default).
  20614. *
  20615. * @method Html5#volume
  20616. * @return {number}
  20617. * The value of `volume` from the media element. Value will be between 0-1.
  20618. *
  20619. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
  20620. */
  20621. 'volume',
  20622. /**
  20623. * Get the value of `muted` from the media element. `muted` indicates
  20624. * that the volume for the media should be set to silent. This does not actually change
  20625. * the `volume` attribute.
  20626. *
  20627. * @method Html5#muted
  20628. * @return {boolean}
  20629. * - True if the value of `volume` should be ignored and the audio set to silent.
  20630. * - False if the value of `volume` should be used.
  20631. *
  20632. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
  20633. */
  20634. 'muted',
  20635. /**
  20636. * Get the value of `poster` from the media element. `poster` indicates
  20637. * that the url of an image file that can/will be shown when no media data is available.
  20638. *
  20639. * @method Html5#poster
  20640. * @return {string}
  20641. * The value of `poster` from the media element. Value will be a url to an
  20642. * image.
  20643. *
  20644. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
  20645. */
  20646. 'poster',
  20647. /**
  20648. * Get the value of `preload` from the media element. `preload` indicates
  20649. * what should download before the media is interacted with. It can have the following
  20650. * values:
  20651. * - none: nothing should be downloaded
  20652. * - metadata: poster and the first few frames of the media may be downloaded to get
  20653. * media dimensions and other metadata
  20654. * - auto: allow the media and metadata for the media to be downloaded before
  20655. * interaction
  20656. *
  20657. * @method Html5#preload
  20658. * @return {string}
  20659. * The value of `preload` from the media element. Will be 'none', 'metadata',
  20660. * or 'auto'.
  20661. *
  20662. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
  20663. */
  20664. 'preload',
  20665. /**
  20666. * Get the value of `autoplay` from the media element. `autoplay` indicates
  20667. * that the media should start to play as soon as the page is ready.
  20668. *
  20669. * @method Html5#autoplay
  20670. * @return {boolean}
  20671. * - The value of `autoplay` from the media element.
  20672. * - True indicates that the media should start as soon as the page loads.
  20673. * - False indicates that the media should not start as soon as the page loads.
  20674. *
  20675. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
  20676. */
  20677. 'autoplay',
  20678. /**
  20679. * Get the value of `controls` from the media element. `controls` indicates
  20680. * whether the native media controls should be shown or hidden.
  20681. *
  20682. * @method Html5#controls
  20683. * @return {boolean}
  20684. * - The value of `controls` from the media element.
  20685. * - True indicates that native controls should be showing.
  20686. * - False indicates that native controls should be hidden.
  20687. *
  20688. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
  20689. */
  20690. 'controls',
  20691. /**
  20692. * Get the value of `loop` from the media element. `loop` indicates
  20693. * that the media should return to the start of the media and continue playing once
  20694. * it reaches the end.
  20695. *
  20696. * @method Html5#loop
  20697. * @return {boolean}
  20698. * - The value of `loop` from the media element.
  20699. * - True indicates that playback should seek back to start once
  20700. * the end of a media is reached.
  20701. * - False indicates that playback should not loop back to the start when the
  20702. * end of the media is reached.
  20703. *
  20704. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
  20705. */
  20706. 'loop',
  20707. /**
  20708. * Get the value of the `error` from the media element. `error` indicates any
  20709. * MediaError that may have occured during playback. If error returns null there is no
  20710. * current error.
  20711. *
  20712. * @method Html5#error
  20713. * @return {MediaError|null}
  20714. * The value of `error` from the media element. Will be `MediaError` if there
  20715. * is a current error and null otherwise.
  20716. *
  20717. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
  20718. */
  20719. 'error',
  20720. /**
  20721. * Get the value of `seeking` from the media element. `seeking` indicates whether the
  20722. * media is currently seeking to a new position or not.
  20723. *
  20724. * @method Html5#seeking
  20725. * @return {boolean}
  20726. * - The value of `seeking` from the media element.
  20727. * - True indicates that the media is currently seeking to a new position.
  20728. * - Flase indicates that the media is not seeking to a new position at this time.
  20729. *
  20730. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
  20731. */
  20732. 'seeking',
  20733. /**
  20734. * Get the value of `seekable` from the media element. `seekable` returns a
  20735. * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
  20736. *
  20737. * @method Html5#seekable
  20738. * @return {TimeRange}
  20739. * The value of `seekable` from the media element. A `TimeRange` object
  20740. * indicating the current ranges of time that can be seeked to.
  20741. *
  20742. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
  20743. */
  20744. 'seekable',
  20745. /**
  20746. * Get the value of `ended` from the media element. `ended` indicates whether
  20747. * the media has reached the end or not.
  20748. *
  20749. * @method Html5#ended
  20750. * @return {boolean}
  20751. * - The value of `ended` from the media element.
  20752. * - True indicates that the media has ended.
  20753. * - False indicates that the media has not ended.
  20754. *
  20755. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
  20756. */
  20757. 'ended',
  20758. /**
  20759. * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
  20760. * whether the media should start muted or not. Only changes the default state of the
  20761. * media. `muted` and `defaultMuted` can have different values. `muted` indicates the
  20762. * current state.
  20763. *
  20764. * @method Html5#defaultMuted
  20765. * @return {boolean}
  20766. * - The value of `defaultMuted` from the media element.
  20767. * - True indicates that the media should start muted.
  20768. * - False indicates that the media should not start muted
  20769. *
  20770. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
  20771. */
  20772. 'defaultMuted',
  20773. /**
  20774. * Get the value of `playbackRate` from the media element. `playbackRate` indicates
  20775. * the rate at which the media is currently playing back. Examples:
  20776. * - if playbackRate is set to 2, media will play twice as fast.
  20777. * - if playbackRate is set to 0.5, media will play half as fast.
  20778. *
  20779. * @method Html5#playbackRate
  20780. * @return {number}
  20781. * The value of `playbackRate` from the media element. A number indicating
  20782. * the current playback speed of the media, where 1 is normal speed.
  20783. *
  20784. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  20785. */
  20786. 'playbackRate',
  20787. /**
  20788. * Get the value of `played` from the media element. `played` returns a `TimeRange`
  20789. * object representing points in the media timeline that have been played.
  20790. *
  20791. * @method Html5#played
  20792. * @return {TimeRange}
  20793. * The value of `played` from the media element. A `TimeRange` object indicating
  20794. * the ranges of time that have been played.
  20795. *
  20796. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
  20797. */
  20798. 'played',
  20799. /**
  20800. * Get the value of `networkState` from the media element. `networkState` indicates
  20801. * the current network state. It returns an enumeration from the following list:
  20802. * - 0: NETWORK_EMPTY
  20803. * - 1: NEWORK_IDLE
  20804. * - 2: NETWORK_LOADING
  20805. * - 3: NETWORK_NO_SOURCE
  20806. *
  20807. * @method Html5#networkState
  20808. * @return {number}
  20809. * The value of `networkState` from the media element. This will be a number
  20810. * from the list in the description.
  20811. *
  20812. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
  20813. */
  20814. 'networkState',
  20815. /**
  20816. * Get the value of `readyState` from the media element. `readyState` indicates
  20817. * the current state of the media element. It returns an enumeration from the
  20818. * following list:
  20819. * - 0: HAVE_NOTHING
  20820. * - 1: HAVE_METADATA
  20821. * - 2: HAVE_CURRENT_DATA
  20822. * - 3: HAVE_FUTURE_DATA
  20823. * - 4: HAVE_ENOUGH_DATA
  20824. *
  20825. * @method Html5#readyState
  20826. * @return {number}
  20827. * The value of `readyState` from the media element. This will be a number
  20828. * from the list in the description.
  20829. *
  20830. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
  20831. */
  20832. 'readyState',
  20833. /**
  20834. * Get the value of `videoWidth` from the video element. `videoWidth` indicates
  20835. * the current width of the video in css pixels.
  20836. *
  20837. * @method Html5#videoWidth
  20838. * @return {number}
  20839. * The value of `videoWidth` from the video element. This will be a number
  20840. * in css pixels.
  20841. *
  20842. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
  20843. */
  20844. 'videoWidth',
  20845. /**
  20846. * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
  20847. * the current height of the video in css pixels.
  20848. *
  20849. * @method Html5#videoHeight
  20850. * @return {number}
  20851. * The value of `videoHeight` from the video element. This will be a number
  20852. * in css pixels.
  20853. *
  20854. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
  20855. */
  20856. 'videoHeight'].forEach(function (prop) {
  20857. Html5.prototype[prop] = function () {
  20858. return this.el_[prop];
  20859. };
  20860. });
  20861. // Wrap native properties with a setter in this format:
  20862. // set + toTitleCase(name)
  20863. [
  20864. /**
  20865. * Set the value of `volume` on the media element. `volume` indicates the current
  20866. * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
  20867. * so on.
  20868. *
  20869. * @method Html5#setVolume
  20870. * @param {number} percentAsDecimal
  20871. * The volume percent as a decimal. Valid range is from 0-1.
  20872. *
  20873. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
  20874. */
  20875. 'volume',
  20876. /**
  20877. * Set the value of `muted` on the media element. `muted` indicates the current
  20878. * audio level should be silent.
  20879. *
  20880. * @method Html5#setMuted
  20881. * @param {boolean} muted
  20882. * - True if the audio should be set to silent
  20883. * - False otherwise
  20884. *
  20885. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
  20886. */
  20887. 'muted',
  20888. /**
  20889. * Set the value of `src` on the media element. `src` indicates the current
  20890. * {@link Tech~SourceObject} for the media.
  20891. *
  20892. * @method Html5#setSrc
  20893. * @param {Tech~SourceObject} src
  20894. * The source object to set as the current source.
  20895. *
  20896. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
  20897. */
  20898. 'src',
  20899. /**
  20900. * Set the value of `poster` on the media element. `poster` is the url to
  20901. * an image file that can/will be shown when no media data is available.
  20902. *
  20903. * @method Html5#setPoster
  20904. * @param {string} poster
  20905. * The url to an image that should be used as the `poster` for the media
  20906. * element.
  20907. *
  20908. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
  20909. */
  20910. 'poster',
  20911. /**
  20912. * Set the value of `preload` on the media element. `preload` indicates
  20913. * what should download before the media is interacted with. It can have the following
  20914. * values:
  20915. * - none: nothing should be downloaded
  20916. * - metadata: poster and the first few frames of the media may be downloaded to get
  20917. * media dimensions and other metadata
  20918. * - auto: allow the media and metadata for the media to be downloaded before
  20919. * interaction
  20920. *
  20921. * @method Html5#setPreload
  20922. * @param {string} preload
  20923. * The value of `preload` to set on the media element. Must be 'none', 'metadata',
  20924. * or 'auto'.
  20925. *
  20926. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
  20927. */
  20928. 'preload',
  20929. /**
  20930. * Set the value of `autoplay` on the media element. `autoplay` indicates
  20931. * that the media should start to play as soon as the page is ready.
  20932. *
  20933. * @method Html5#setAutoplay
  20934. * @param {boolean} autoplay
  20935. * - True indicates that the media should start as soon as the page loads.
  20936. * - False indicates that the media should not start as soon as the page loads.
  20937. *
  20938. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
  20939. */
  20940. 'autoplay',
  20941. /**
  20942. * Set the value of `loop` on the media element. `loop` indicates
  20943. * that the media should return to the start of the media and continue playing once
  20944. * it reaches the end.
  20945. *
  20946. * @method Html5#setLoop
  20947. * @param {boolean} loop
  20948. * - True indicates that playback should seek back to start once
  20949. * the end of a media is reached.
  20950. * - False indicates that playback should not loop back to the start when the
  20951. * end of the media is reached.
  20952. *
  20953. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
  20954. */
  20955. 'loop',
  20956. /**
  20957. * Set the value of `playbackRate` on the media element. `playbackRate` indicates
  20958. * the rate at which the media should play back. Examples:
  20959. * - if playbackRate is set to 2, media will play twice as fast.
  20960. * - if playbackRate is set to 0.5, media will play half as fast.
  20961. *
  20962. * @method Html5#setPlaybackRate
  20963. * @return {number}
  20964. * The value of `playbackRate` from the media element. A number indicating
  20965. * the current playback speed of the media, where 1 is normal speed.
  20966. *
  20967. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  20968. */
  20969. 'playbackRate'].forEach(function (prop) {
  20970. Html5.prototype['set' + (0, _toTitleCase2['default'])(prop)] = function (v) {
  20971. this.el_[prop] = v;
  20972. };
  20973. });
  20974. // wrap native functions with a function
  20975. [
  20976. /**
  20977. * A wrapper around the media elements `pause` function. This will call the `HTML5`
  20978. * media elements `pause` function.
  20979. *
  20980. * @method Html5#pause
  20981. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
  20982. */
  20983. 'pause',
  20984. /**
  20985. * A wrapper around the media elements `load` function. This will call the `HTML5`s
  20986. * media element `load` function.
  20987. *
  20988. * @method Html5#load
  20989. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
  20990. */
  20991. 'load'].forEach(function (prop) {
  20992. Html5.prototype[prop] = function () {
  20993. return this.el_[prop]();
  20994. };
  20995. });
  20996. _tech2['default'].withSourceHandlers(Html5);
  20997. /**
  20998. * Native source handler for Html5, simply passes the source to the media element.
  20999. *
  21000. * @proprety {Tech~SourceObject} source
  21001. * The source object
  21002. *
  21003. * @proprety {Html5} tech
  21004. * The instance of the HTML5 tech.
  21005. */
  21006. Html5.nativeSourceHandler = {};
  21007. /**
  21008. * Check if the media element can play the given mime type.
  21009. *
  21010. * @param {string} type
  21011. * The mimetype to check
  21012. *
  21013. * @return {string}
  21014. * 'probably', 'maybe', or '' (empty string)
  21015. */
  21016. Html5.nativeSourceHandler.canPlayType = function (type) {
  21017. // IE9 on Windows 7 without MediaPlayer throws an error here
  21018. // https://github.com/videojs/video.js/issues/519
  21019. try {
  21020. return Html5.TEST_VID.canPlayType(type);
  21021. } catch (e) {
  21022. return '';
  21023. }
  21024. };
  21025. /**
  21026. * Check if the media element can handle a source natively.
  21027. *
  21028. * @param {Tech~SourceObject} source
  21029. * The source object
  21030. *
  21031. * @param {Object} [options]
  21032. * Options to be passed to the tech.
  21033. *
  21034. * @return {string}
  21035. * 'probably', 'maybe', or '' (empty string).
  21036. */
  21037. Html5.nativeSourceHandler.canHandleSource = function (source, options) {
  21038. // If a type was provided we should rely on that
  21039. if (source.type) {
  21040. return Html5.nativeSourceHandler.canPlayType(source.type);
  21041. // If no type, fall back to checking 'video/[EXTENSION]'
  21042. } else if (source.src) {
  21043. var ext = Url.getFileExtension(source.src);
  21044. return Html5.nativeSourceHandler.canPlayType('video/' + ext);
  21045. }
  21046. return '';
  21047. };
  21048. /**
  21049. * Pass the source to the native media element.
  21050. *
  21051. * @param {Tech~SourceObject} source
  21052. * The source object
  21053. *
  21054. * @param {Html5} tech
  21055. * The instance of the Html5 tech
  21056. *
  21057. * @param {Object} [options]
  21058. * The options to pass to the source
  21059. */
  21060. Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
  21061. tech.setSrc(source.src);
  21062. };
  21063. /**
  21064. * A noop for the native dispose function, as cleanup is not needed.
  21065. */
  21066. Html5.nativeSourceHandler.dispose = function () {};
  21067. // Register the native source handler
  21068. Html5.registerSourceHandler(Html5.nativeSourceHandler);
  21069. _component2['default'].registerComponent('Html5', Html5);
  21070. _tech2['default'].registerTech('Html5', Html5);
  21071. exports['default'] = Html5;
  21072. },{"../component":47,"../utils/browser.js":120,"../utils/dom.js":123,"../utils/fn.js":125,"../utils/log.js":128,"../utils/merge-options.js":129,"../utils/obj":130,"../utils/to-title-case.js":133,"../utils/url.js":134,"./tech.js":104,"global/document":136,"global/window":137,"tsml":42}],103:[function(require,module,exports){
  21073. 'use strict';
  21074. exports.__esModule = true;
  21075. var _component = require('../component.js');
  21076. var _component2 = _interopRequireDefault(_component);
  21077. var _tech = require('./tech.js');
  21078. var _tech2 = _interopRequireDefault(_tech);
  21079. var _toTitleCase = require('../utils/to-title-case.js');
  21080. var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
  21081. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  21082. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  21083. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  21084. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  21085. * @file loader.js
  21086. */
  21087. /**
  21088. * The `MediaLoader` is the `Component` that decides which playback technology to load
  21089. * when a player is initialized.
  21090. *
  21091. * @extends Component
  21092. */
  21093. var MediaLoader = function (_Component) {
  21094. _inherits(MediaLoader, _Component);
  21095. /**
  21096. * Create an instance of this class.
  21097. *
  21098. * @param {Player} player
  21099. * The `Player` that this class should attach to.
  21100. *
  21101. * @param {Object} [options]
  21102. * The key/value stroe of player options.
  21103. *
  21104. * @param {Component~ReadyCallback} [ready]
  21105. * The function that is run when this component is ready.
  21106. */
  21107. function MediaLoader(player, options, ready) {
  21108. _classCallCheck(this, MediaLoader);
  21109. // If there are no sources when the player is initialized,
  21110. // load the first supported playback technology.
  21111. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options, ready));
  21112. if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
  21113. for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
  21114. var techName = (0, _toTitleCase2['default'])(j[i]);
  21115. var tech = _tech2['default'].getTech(techName);
  21116. // Support old behavior of techs being registered as components.
  21117. // Remove once that deprecated behavior is removed.
  21118. if (!techName) {
  21119. tech = _component2['default'].getComponent(techName);
  21120. }
  21121. // Check if the browser supports this technology
  21122. if (tech && tech.isSupported()) {
  21123. player.loadTech_(techName);
  21124. break;
  21125. }
  21126. }
  21127. } else {
  21128. // Loop through playback technologies (HTML5, Flash) and check for support.
  21129. // Then load the best source.
  21130. // A few assumptions here:
  21131. // All playback technologies respect preload false.
  21132. player.src(options.playerOptions.sources);
  21133. }
  21134. return _this;
  21135. }
  21136. return MediaLoader;
  21137. }(_component2['default']);
  21138. _component2['default'].registerComponent('MediaLoader', MediaLoader);
  21139. exports['default'] = MediaLoader;
  21140. },{"../component.js":47,"../utils/to-title-case.js":133,"./tech.js":104}],104:[function(require,module,exports){
  21141. 'use strict';
  21142. exports.__esModule = true;
  21143. var _component = require('../component');
  21144. var _component2 = _interopRequireDefault(_component);
  21145. var _htmlTrackElement = require('../tracks/html-track-element');
  21146. var _htmlTrackElement2 = _interopRequireDefault(_htmlTrackElement);
  21147. var _htmlTrackElementList = require('../tracks/html-track-element-list');
  21148. var _htmlTrackElementList2 = _interopRequireDefault(_htmlTrackElementList);
  21149. var _mergeOptions = require('../utils/merge-options.js');
  21150. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  21151. var _textTrack = require('../tracks/text-track');
  21152. var _textTrack2 = _interopRequireDefault(_textTrack);
  21153. var _textTrackList = require('../tracks/text-track-list');
  21154. var _textTrackList2 = _interopRequireDefault(_textTrackList);
  21155. var _videoTrackList = require('../tracks/video-track-list');
  21156. var _videoTrackList2 = _interopRequireDefault(_videoTrackList);
  21157. var _audioTrackList = require('../tracks/audio-track-list');
  21158. var _audioTrackList2 = _interopRequireDefault(_audioTrackList);
  21159. var _fn = require('../utils/fn.js');
  21160. var Fn = _interopRequireWildcard(_fn);
  21161. var _log = require('../utils/log.js');
  21162. var _log2 = _interopRequireDefault(_log);
  21163. var _timeRanges = require('../utils/time-ranges.js');
  21164. var _buffer = require('../utils/buffer.js');
  21165. var _mediaError = require('../media-error.js');
  21166. var _mediaError2 = _interopRequireDefault(_mediaError);
  21167. var _window = require('global/window');
  21168. var _window2 = _interopRequireDefault(_window);
  21169. var _document = require('global/document');
  21170. var _document2 = _interopRequireDefault(_document);
  21171. var _obj = require('../utils/obj');
  21172. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  21173. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  21174. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  21175. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  21176. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  21177. * @file tech.js
  21178. */
  21179. /**
  21180. * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
  21181. * that just contains the src url alone.
  21182. * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
  21183. * `var SourceString = 'http://example.com/some-video.mp4';`
  21184. *
  21185. * @typedef {Object|string} Tech~SourceObject
  21186. *
  21187. * @property {string} src
  21188. * The url to the source
  21189. *
  21190. * @property {string} type
  21191. * The mime type of the source
  21192. */
  21193. /**
  21194. * A function used by {@link Tech} to create a new {@link TextTrack}.
  21195. *
  21196. * @param {Tech} self
  21197. * An instance of the Tech class.
  21198. *
  21199. * @param {string} kind
  21200. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  21201. *
  21202. * @param {string} [label]
  21203. * Label to identify the text track
  21204. *
  21205. * @param {string} [language]
  21206. * Two letter language abbreviation
  21207. *
  21208. * @param {Object} [options={}]
  21209. * An object with additional text track options
  21210. *
  21211. * @return {TextTrack}
  21212. * The text track that was created.
  21213. */
  21214. function createTrackHelper(self, kind, label, language) {
  21215. var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
  21216. var tracks = self.textTracks();
  21217. options.kind = kind;
  21218. if (label) {
  21219. options.label = label;
  21220. }
  21221. if (language) {
  21222. options.language = language;
  21223. }
  21224. options.tech = self;
  21225. var track = new _textTrack2['default'](options);
  21226. tracks.addTrack_(track);
  21227. return track;
  21228. }
  21229. /**
  21230. * This is the base class for media playback technology controllers, such as
  21231. * {@link Flash} and {@link HTML5}
  21232. *
  21233. * @extends Component
  21234. */
  21235. var Tech = function (_Component) {
  21236. _inherits(Tech, _Component);
  21237. /**
  21238. * Create an instance of this Tech.
  21239. *
  21240. * @param {Object} [options]
  21241. * The key/value store of player options.
  21242. *
  21243. * @param {Component~ReadyCallback} ready
  21244. * Callback function to call when the `HTML5` Tech is ready.
  21245. */
  21246. function Tech() {
  21247. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  21248. var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
  21249. _classCallCheck(this, Tech);
  21250. // we don't want the tech to report user activity automatically.
  21251. // This is done manually in addControlsListeners
  21252. options.reportTouchActivity = false;
  21253. // keep track of whether the current source has played at all to
  21254. // implement a very limited played()
  21255. var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready));
  21256. _this.hasStarted_ = false;
  21257. _this.on('playing', function () {
  21258. this.hasStarted_ = true;
  21259. });
  21260. _this.on('loadstart', function () {
  21261. this.hasStarted_ = false;
  21262. });
  21263. _this.textTracks_ = options.textTracks;
  21264. _this.videoTracks_ = options.videoTracks;
  21265. _this.audioTracks_ = options.audioTracks;
  21266. // Manually track progress in cases where the browser/flash player doesn't report it.
  21267. if (!_this.featuresProgressEvents) {
  21268. _this.manualProgressOn();
  21269. }
  21270. // Manually track timeupdates in cases where the browser/flash player doesn't report it.
  21271. if (!_this.featuresTimeupdateEvents) {
  21272. _this.manualTimeUpdatesOn();
  21273. }
  21274. ['Text', 'Audio', 'Video'].forEach(function (track) {
  21275. if (options['native' + track + 'Tracks'] === false) {
  21276. _this['featuresNative' + track + 'Tracks'] = false;
  21277. }
  21278. });
  21279. if (options.nativeCaptions === false) {
  21280. _this.featuresNativeTextTracks = false;
  21281. }
  21282. if (!_this.featuresNativeTextTracks) {
  21283. _this.emulateTextTracks();
  21284. }
  21285. _this.autoRemoteTextTracks_ = new _textTrackList2['default']();
  21286. _this.initTextTrackListeners();
  21287. _this.initTrackListeners();
  21288. // Turn on component tap events only if not using native controls
  21289. if (!options.nativeControlsForTouch) {
  21290. _this.emitTapEvents();
  21291. }
  21292. if (_this.constructor) {
  21293. _this.name_ = _this.constructor.name || 'Unknown Tech';
  21294. }
  21295. return _this;
  21296. }
  21297. /* Fallbacks for unsupported event types
  21298. ================================================================================ */
  21299. /**
  21300. * Polyfill the `progress` event for browsers that don't support it natively.
  21301. *
  21302. * @see {@link Tech#trackProgress}
  21303. */
  21304. Tech.prototype.manualProgressOn = function manualProgressOn() {
  21305. this.on('durationchange', this.onDurationChange);
  21306. this.manualProgress = true;
  21307. // Trigger progress watching when a source begins loading
  21308. this.one('ready', this.trackProgress);
  21309. };
  21310. /**
  21311. * Turn off the polyfill for `progress` events that was created in
  21312. * {@link Tech#manualProgressOn}
  21313. */
  21314. Tech.prototype.manualProgressOff = function manualProgressOff() {
  21315. this.manualProgress = false;
  21316. this.stopTrackingProgress();
  21317. this.off('durationchange', this.onDurationChange);
  21318. };
  21319. /**
  21320. * This is used to trigger a `progress` event when the buffered percent changes. It
  21321. * sets an interval function that will be called every 500 milliseconds to check if the
  21322. * buffer end percent has changed.
  21323. *
  21324. * > This function is called by {@link Tech#manualProgressOn}
  21325. *
  21326. * @param {EventTarget~Event} event
  21327. * The `ready` event that caused this to run.
  21328. *
  21329. * @listens Tech#ready
  21330. * @fires Tech#progress
  21331. */
  21332. Tech.prototype.trackProgress = function trackProgress(event) {
  21333. this.stopTrackingProgress();
  21334. this.progressInterval = this.setInterval(Fn.bind(this, function () {
  21335. // Don't trigger unless buffered amount is greater than last time
  21336. var numBufferedPercent = this.bufferedPercent();
  21337. if (this.bufferedPercent_ !== numBufferedPercent) {
  21338. /**
  21339. * See {@link Player#progress}
  21340. *
  21341. * @event Tech#progress
  21342. * @type {EventTarget~Event}
  21343. */
  21344. this.trigger('progress');
  21345. }
  21346. this.bufferedPercent_ = numBufferedPercent;
  21347. if (numBufferedPercent === 1) {
  21348. this.stopTrackingProgress();
  21349. }
  21350. }), 500);
  21351. };
  21352. /**
  21353. * Update our internal duration on a `durationchange` event by calling
  21354. * {@link Tech#duration}.
  21355. *
  21356. * @param {EventTarget~Event} event
  21357. * The `durationchange` event that caused this to run.
  21358. *
  21359. * @listens Tech#durationchange
  21360. */
  21361. Tech.prototype.onDurationChange = function onDurationChange(event) {
  21362. this.duration_ = this.duration();
  21363. };
  21364. /**
  21365. * Get and create a `TimeRange` object for buffering.
  21366. *
  21367. * @return {TimeRange}
  21368. * The time range object that was created.
  21369. */
  21370. Tech.prototype.buffered = function buffered() {
  21371. return (0, _timeRanges.createTimeRange)(0, 0);
  21372. };
  21373. /**
  21374. * Get the percentage of the current video that is currently buffered.
  21375. *
  21376. * @return {number}
  21377. * A number from 0 to 1 that represents the decimal percentage of the
  21378. * video that is buffered.
  21379. *
  21380. */
  21381. Tech.prototype.bufferedPercent = function bufferedPercent() {
  21382. return (0, _buffer.bufferedPercent)(this.buffered(), this.duration_);
  21383. };
  21384. /**
  21385. * Turn off the polyfill for `progress` events that was created in
  21386. * {@link Tech#manualProgressOn}
  21387. * Stop manually tracking progress events by clearing the interval that was set in
  21388. * {@link Tech#trackProgress}.
  21389. */
  21390. Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
  21391. this.clearInterval(this.progressInterval);
  21392. };
  21393. /**
  21394. * Polyfill the `timeupdate` event for browsers that don't support it.
  21395. *
  21396. * @see {@link Tech#trackCurrentTime}
  21397. */
  21398. Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
  21399. this.manualTimeUpdates = true;
  21400. this.on('play', this.trackCurrentTime);
  21401. this.on('pause', this.stopTrackingCurrentTime);
  21402. };
  21403. /**
  21404. * Turn off the polyfill for `timeupdate` events that was created in
  21405. * {@link Tech#manualTimeUpdatesOn}
  21406. */
  21407. Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
  21408. this.manualTimeUpdates = false;
  21409. this.stopTrackingCurrentTime();
  21410. this.off('play', this.trackCurrentTime);
  21411. this.off('pause', this.stopTrackingCurrentTime);
  21412. };
  21413. /**
  21414. * Sets up an interval function to track current time and trigger `timeupdate` every
  21415. * 250 milliseconds.
  21416. *
  21417. * @listens Tech#play
  21418. * @triggers Tech#timeupdate
  21419. */
  21420. Tech.prototype.trackCurrentTime = function trackCurrentTime() {
  21421. if (this.currentTimeInterval) {
  21422. this.stopTrackingCurrentTime();
  21423. }
  21424. this.currentTimeInterval = this.setInterval(function () {
  21425. /**
  21426. * Triggered at an interval of 250ms to indicated that time is passing in the video.
  21427. *
  21428. * @event Tech#timeupdate
  21429. * @type {EventTarget~Event}
  21430. */
  21431. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  21432. // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
  21433. }, 250);
  21434. };
  21435. /**
  21436. * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
  21437. * `timeupdate` event is no longer triggered.
  21438. *
  21439. * @listens {Tech#pause}
  21440. */
  21441. Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
  21442. this.clearInterval(this.currentTimeInterval);
  21443. // #1002 - if the video ends right before the next timeupdate would happen,
  21444. // the progress bar won't make it all the way to the end
  21445. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  21446. };
  21447. /**
  21448. * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
  21449. * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
  21450. *
  21451. * @fires Component#dispose
  21452. */
  21453. Tech.prototype.dispose = function dispose() {
  21454. // clear out all tracks because we can't reuse them between techs
  21455. this.clearTracks(['audio', 'video', 'text']);
  21456. // Turn off any manual progress or timeupdate tracking
  21457. if (this.manualProgress) {
  21458. this.manualProgressOff();
  21459. }
  21460. if (this.manualTimeUpdates) {
  21461. this.manualTimeUpdatesOff();
  21462. }
  21463. _Component.prototype.dispose.call(this);
  21464. };
  21465. /**
  21466. * Clear out a single `TrackList` or an array of `TrackLists` given their names.
  21467. *
  21468. * > Note: Techs without source handlers should call this between sources for `video`
  21469. * & `audio` tracks. You don't want to use them between tracks!
  21470. *
  21471. * @param {string[]|string} types
  21472. * TrackList names to clear, valid names are `video`, `audio`, and
  21473. * `text`.
  21474. */
  21475. Tech.prototype.clearTracks = function clearTracks(types) {
  21476. var _this2 = this;
  21477. types = [].concat(types);
  21478. // clear out all tracks because we can't reuse them between techs
  21479. types.forEach(function (type) {
  21480. var list = _this2[type + 'Tracks']() || [];
  21481. var i = list.length;
  21482. while (i--) {
  21483. var track = list[i];
  21484. if (type === 'text') {
  21485. _this2.removeRemoteTextTrack(track);
  21486. }
  21487. list.removeTrack_(track);
  21488. }
  21489. });
  21490. };
  21491. /**
  21492. * Remove any TextTracks added via addRemoteTextTrack that are
  21493. * flagged for automatic garbage collection
  21494. */
  21495. Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
  21496. var list = this.autoRemoteTextTracks_ || [];
  21497. var i = list.length;
  21498. while (i--) {
  21499. var track = list[i];
  21500. this.removeRemoteTextTrack(track);
  21501. }
  21502. };
  21503. /**
  21504. * Reset the tech, which will removes all sources and reset the internal readyState.
  21505. *
  21506. * @abstract
  21507. */
  21508. Tech.prototype.reset = function reset() {};
  21509. /**
  21510. * Get or set an error on the Tech.
  21511. *
  21512. * @param {MediaError} [err]
  21513. * Error to set on the Tech
  21514. *
  21515. * @return {MediaError|null}
  21516. * The current error object on the tech, or null if there isn't one.
  21517. */
  21518. Tech.prototype.error = function error(err) {
  21519. if (err !== undefined) {
  21520. this.error_ = new _mediaError2['default'](err);
  21521. this.trigger('error');
  21522. }
  21523. return this.error_;
  21524. };
  21525. /**
  21526. * Returns the `TimeRange`s that have been played through for the current source.
  21527. *
  21528. * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
  21529. * It only checks wether the source has played at all or not.
  21530. *
  21531. * @return {TimeRange}
  21532. * - A single time range if this video has played
  21533. * - An empty set of ranges if not.
  21534. */
  21535. Tech.prototype.played = function played() {
  21536. if (this.hasStarted_) {
  21537. return (0, _timeRanges.createTimeRange)(0, 0);
  21538. }
  21539. return (0, _timeRanges.createTimeRange)();
  21540. };
  21541. /**
  21542. * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
  21543. * previously called.
  21544. *
  21545. * @fires Tech#timeupdate
  21546. */
  21547. Tech.prototype.setCurrentTime = function setCurrentTime() {
  21548. // improve the accuracy of manual timeupdates
  21549. if (this.manualTimeUpdates) {
  21550. /**
  21551. * A manual `timeupdate` event.
  21552. *
  21553. * @event Tech#timeupdate
  21554. * @type {EventTarget~Event}
  21555. */
  21556. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  21557. }
  21558. };
  21559. /**
  21560. * Turn on listeners for {@link TextTrackList} events. This adds
  21561. * {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and
  21562. * `removetrack`.
  21563. *
  21564. * @fires Tech#texttrackchange
  21565. */
  21566. Tech.prototype.initTextTrackListeners = function initTextTrackListeners() {
  21567. var textTrackListChanges = Fn.bind(this, function () {
  21568. /**
  21569. * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
  21570. *
  21571. * @event Tech#texttrackchange
  21572. * @type {EventTarget~Event}
  21573. */
  21574. this.trigger('texttrackchange');
  21575. });
  21576. var tracks = this.textTracks();
  21577. if (!tracks) {
  21578. return;
  21579. }
  21580. tracks.addEventListener('removetrack', textTrackListChanges);
  21581. tracks.addEventListener('addtrack', textTrackListChanges);
  21582. this.on('dispose', Fn.bind(this, function () {
  21583. tracks.removeEventListener('removetrack', textTrackListChanges);
  21584. tracks.removeEventListener('addtrack', textTrackListChanges);
  21585. }));
  21586. };
  21587. /**
  21588. * Turn on listeners for {@link VideoTrackList} and {@link {AudioTrackList} events.
  21589. * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`.
  21590. *
  21591. * @fires Tech#audiotrackchange
  21592. * @fires Tech#videotrackchange
  21593. */
  21594. Tech.prototype.initTrackListeners = function initTrackListeners() {
  21595. var _this3 = this;
  21596. var trackTypes = ['video', 'audio'];
  21597. trackTypes.forEach(function (type) {
  21598. /**
  21599. * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
  21600. *
  21601. * @event Tech#audiotrackchange
  21602. * @type {EventTarget~Event}
  21603. */
  21604. /**
  21605. * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
  21606. *
  21607. * @event Tech#videotrackchange
  21608. * @type {EventTarget~Event}
  21609. */
  21610. var trackListChanges = function trackListChanges() {
  21611. _this3.trigger(type + 'trackchange');
  21612. };
  21613. var tracks = _this3[type + 'Tracks']();
  21614. tracks.addEventListener('removetrack', trackListChanges);
  21615. tracks.addEventListener('addtrack', trackListChanges);
  21616. _this3.on('dispose', function () {
  21617. tracks.removeEventListener('removetrack', trackListChanges);
  21618. tracks.removeEventListener('addtrack', trackListChanges);
  21619. });
  21620. });
  21621. };
  21622. /**
  21623. * Emulate TextTracks using vtt.js if necessary
  21624. *
  21625. * @fires Tech#vttjsloaded
  21626. * @fires Tech#vttjserror
  21627. */
  21628. Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
  21629. var _this4 = this;
  21630. if (_window2['default'].WebVTT) {
  21631. return;
  21632. }
  21633. // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
  21634. // signals that the Tech is ready at which point Tech.el_ is part of the DOM
  21635. // before inserting the WebVTT script
  21636. if (_document2['default'].body.contains(this.el())) {
  21637. var vtt = require('videojs-vtt.js');
  21638. // load via require if available and vtt.js script location was not passed in
  21639. // as an option. novtt builds will turn the above require call into an empty object
  21640. // which will cause this if check to always fail.
  21641. if (!this.options_['vtt.js'] && (0, _obj.isPlain)(vtt) && Object.keys(vtt).length > 0) {
  21642. this.trigger('vttjsloaded');
  21643. return;
  21644. }
  21645. // load vtt.js via the script location option or the cdn of no location was
  21646. // passed in
  21647. var script = _document2['default'].createElement('script');
  21648. script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
  21649. script.onload = function () {
  21650. /**
  21651. * Fired when vtt.js is loaded.
  21652. *
  21653. * @event Tech#vttjsloaded
  21654. * @type {EventTarget~Event}
  21655. */
  21656. _this4.trigger('vttjsloaded');
  21657. };
  21658. script.onerror = function () {
  21659. /**
  21660. * Fired when vtt.js was not loaded due to an error
  21661. *
  21662. * @event Tech#vttjsloaded
  21663. * @type {EventTarget~Event}
  21664. */
  21665. _this4.trigger('vttjserror');
  21666. };
  21667. this.on('dispose', function () {
  21668. script.onload = null;
  21669. script.onerror = null;
  21670. });
  21671. // but have not loaded yet and we set it to true before the inject so that
  21672. // we don't overwrite the injected window.WebVTT if it loads right away
  21673. _window2['default'].WebVTT = true;
  21674. this.el().parentNode.appendChild(script);
  21675. } else {
  21676. this.ready(this.addWebVttScript_);
  21677. }
  21678. };
  21679. /**
  21680. * Emulate texttracks
  21681. *
  21682. * @method emulateTextTracks
  21683. */
  21684. Tech.prototype.emulateTextTracks = function emulateTextTracks() {
  21685. var _this5 = this;
  21686. var tracks = this.textTracks();
  21687. if (!tracks) {
  21688. return;
  21689. }
  21690. var remoteTracks = this.remoteTextTracks();
  21691. var handleAddTrack = function handleAddTrack(e) {
  21692. return tracks.addTrack_(e.track);
  21693. };
  21694. var handleRemoveTrack = function handleRemoveTrack(e) {
  21695. return tracks.removeTrack_(e.track);
  21696. };
  21697. remoteTracks.on('addtrack', handleAddTrack);
  21698. remoteTracks.on('removetrack', handleRemoveTrack);
  21699. this.addWebVttScript_();
  21700. var updateDisplay = function updateDisplay() {
  21701. return _this5.trigger('texttrackchange');
  21702. };
  21703. var textTracksChanges = function textTracksChanges() {
  21704. updateDisplay();
  21705. for (var i = 0; i < tracks.length; i++) {
  21706. var track = tracks[i];
  21707. track.removeEventListener('cuechange', updateDisplay);
  21708. if (track.mode === 'showing') {
  21709. track.addEventListener('cuechange', updateDisplay);
  21710. }
  21711. }
  21712. };
  21713. textTracksChanges();
  21714. tracks.addEventListener('change', textTracksChanges);
  21715. tracks.addEventListener('addtrack', textTracksChanges);
  21716. tracks.addEventListener('removetrack', textTracksChanges);
  21717. this.on('dispose', function () {
  21718. remoteTracks.off('addtrack', handleAddTrack);
  21719. remoteTracks.off('removetrack', handleRemoveTrack);
  21720. tracks.removeEventListener('change', textTracksChanges);
  21721. tracks.removeEventListener('addtrack', textTracksChanges);
  21722. tracks.removeEventListener('removetrack', textTracksChanges);
  21723. for (var i = 0; i < tracks.length; i++) {
  21724. var track = tracks[i];
  21725. track.removeEventListener('cuechange', updateDisplay);
  21726. }
  21727. });
  21728. };
  21729. /**
  21730. * Get the `Tech`s {@link VideoTrackList}.
  21731. *
  21732. * @return {VideoTrackList}
  21733. * The video track list that the Tech is currently using.
  21734. */
  21735. Tech.prototype.videoTracks = function videoTracks() {
  21736. this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default']();
  21737. return this.videoTracks_;
  21738. };
  21739. /**
  21740. * Get the `Tech`s {@link AudioTrackList}.
  21741. *
  21742. * @return {AudioTrackList}
  21743. * The audio track list that the Tech is currently using.
  21744. */
  21745. Tech.prototype.audioTracks = function audioTracks() {
  21746. this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default']();
  21747. return this.audioTracks_;
  21748. };
  21749. /**
  21750. * Get the `Tech`s {@link TextTrackList}.
  21751. *
  21752. * @return {TextTrackList}
  21753. * The text track list that the Tech is currently using.
  21754. */
  21755. Tech.prototype.textTracks = function textTracks() {
  21756. this.textTracks_ = this.textTracks_ || new _textTrackList2['default']();
  21757. return this.textTracks_;
  21758. };
  21759. /**
  21760. * Get the `Tech`s remote {@link TextTrackList}, which is created from elements
  21761. * that were added to the DOM.
  21762. *
  21763. * @return {TextTrackList}
  21764. * The remote text track list that the Tech is currently using.
  21765. */
  21766. Tech.prototype.remoteTextTracks = function remoteTextTracks() {
  21767. this.remoteTextTracks_ = this.remoteTextTracks_ || new _textTrackList2['default']();
  21768. return this.remoteTextTracks_;
  21769. };
  21770. /**
  21771. * Get The `Tech`s {HTMLTrackElementList}, which are the elements in the DOM that are
  21772. * being used as TextTracks.
  21773. *
  21774. * @return {HTMLTrackElementList}
  21775. * The current HTML track elements that exist for the tech.
  21776. */
  21777. Tech.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
  21778. this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new _htmlTrackElementList2['default']();
  21779. return this.remoteTextTrackEls_;
  21780. };
  21781. /**
  21782. * Create and returns a remote {@link TextTrack} object.
  21783. *
  21784. * @param {string} kind
  21785. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  21786. *
  21787. * @param {string} [label]
  21788. * Label to identify the text track
  21789. *
  21790. * @param {string} [language]
  21791. * Two letter language abbreviation
  21792. *
  21793. * @return {TextTrack}
  21794. * The TextTrack that gets created.
  21795. */
  21796. Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  21797. if (!kind) {
  21798. throw new Error('TextTrack kind is required but was not provided');
  21799. }
  21800. return createTrackHelper(this, kind, label, language);
  21801. };
  21802. /**
  21803. * Create an emulated TextTrack for use by addRemoteTextTrack
  21804. *
  21805. * This is intended to be overridden by classes that inherit from
  21806. * Tech in order to create native or custom TextTracks.
  21807. *
  21808. * @param {Object} options
  21809. * The object should contain the options to initialize the TextTrack with.
  21810. *
  21811. * @param {string} [options.kind]
  21812. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
  21813. *
  21814. * @param {string} [options.label].
  21815. * Label to identify the text track
  21816. *
  21817. * @param {string} [options.language]
  21818. * Two letter language abbreviation.
  21819. *
  21820. * @return {HTMLTrackElement}
  21821. * The track element that gets created.
  21822. */
  21823. Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
  21824. var track = (0, _mergeOptions2['default'])(options, {
  21825. tech: this
  21826. });
  21827. return new _htmlTrackElement2['default'](track);
  21828. };
  21829. /**
  21830. * Creates a remote text track object and returns an html track element.
  21831. *
  21832. * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
  21833. *
  21834. * @param {Object} options
  21835. * See {@link Tech#createRemoteTextTrack} for more detailed properties.
  21836. *
  21837. * @param {boolean} [manualCleanup=true]
  21838. * - When false: the TextTrack will be automatically removed from the video
  21839. * element whenever the source changes
  21840. * - When True: The TextTrack will have to be cleaned up manually
  21841. *
  21842. * @return {HTMLTrackElement}
  21843. * An Html Track Element.
  21844. *
  21845. * @deprecated The default functionality for this function will be equivalent
  21846. * to "manualCleanup=false" in the future. The manualCleanup parameter will
  21847. * also be removed.
  21848. */
  21849. Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
  21850. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  21851. var manualCleanup = arguments[1];
  21852. var htmlTrackElement = this.createRemoteTextTrack(options);
  21853. if (manualCleanup !== true && manualCleanup !== false) {
  21854. // deprecation warning
  21855. _log2['default'].warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
  21856. manualCleanup = true;
  21857. }
  21858. // store HTMLTrackElement and TextTrack to remote list
  21859. this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
  21860. this.remoteTextTracks().addTrack_(htmlTrackElement.track);
  21861. if (manualCleanup !== true) {
  21862. // create the TextTrackList if it doesn't exist
  21863. this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track);
  21864. }
  21865. return htmlTrackElement;
  21866. };
  21867. /**
  21868. * Remove a remote text track from the remote `TextTrackList`.
  21869. *
  21870. * @param {TextTrack} track
  21871. * `TextTrack` to remove from the `TextTrackList`
  21872. */
  21873. Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
  21874. var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
  21875. // remove HTMLTrackElement and TextTrack from remote list
  21876. this.remoteTextTrackEls().removeTrackElement_(trackElement);
  21877. this.remoteTextTracks().removeTrack_(track);
  21878. this.autoRemoteTextTracks_.removeTrack_(track);
  21879. };
  21880. /**
  21881. * Gets available media playback quality metrics as specified by the W3C's Media
  21882. * Playback Quality API.
  21883. *
  21884. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  21885. *
  21886. * @return {Object}
  21887. * An object with supported media playback quality metrics
  21888. *
  21889. * @abstract
  21890. */
  21891. Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  21892. return {};
  21893. };
  21894. /**
  21895. * A method to set a poster from a `Tech`.
  21896. *
  21897. * @abstract
  21898. */
  21899. Tech.prototype.setPoster = function setPoster() {};
  21900. /**
  21901. * A method to check for the presence of the 'playsinine' <video> attribute.
  21902. *
  21903. * @abstract
  21904. */
  21905. Tech.prototype.playsinline = function playsinline() {};
  21906. /**
  21907. * A method to set or unset the 'playsinine' <video> attribute.
  21908. *
  21909. * @abstract
  21910. */
  21911. Tech.prototype.setPlaysinline = function setPlaysinline() {};
  21912. /*
  21913. * Check if the tech can support the given mime-type.
  21914. *
  21915. * The base tech does not support any type, but source handlers might
  21916. * overwrite this.
  21917. *
  21918. * @param {string} type
  21919. * The mimetype to check for support
  21920. *
  21921. * @return {string}
  21922. * 'probably', 'maybe', or empty string
  21923. *
  21924. * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
  21925. *
  21926. * @abstract
  21927. */
  21928. Tech.prototype.canPlayType = function canPlayType() {
  21929. return '';
  21930. };
  21931. /*
  21932. * Return whether the argument is a Tech or not.
  21933. * Can be passed either a Class like `Html5` or a instance like `player.tech_`
  21934. *
  21935. * @param {Object} component
  21936. * The item to check
  21937. *
  21938. * @return {boolean}
  21939. * Whether it is a tech or not
  21940. * - True if it is a tech
  21941. * - False if it is not
  21942. */
  21943. Tech.isTech = function isTech(component) {
  21944. return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
  21945. };
  21946. /**
  21947. * Registers a `Tech` into a shared list for videojs.
  21948. *
  21949. * @param {string} name
  21950. * Name of the `Tech` to register.
  21951. *
  21952. * @param {Object} tech
  21953. * The `Tech` class to register.
  21954. */
  21955. Tech.registerTech = function registerTech(name, tech) {
  21956. if (!Tech.techs_) {
  21957. Tech.techs_ = {};
  21958. }
  21959. if (!Tech.isTech(tech)) {
  21960. throw new Error('Tech ' + name + ' must be a Tech');
  21961. }
  21962. Tech.techs_[name] = tech;
  21963. return tech;
  21964. };
  21965. /**
  21966. * Get a `Tech` from the shared list by name.
  21967. *
  21968. * @param {string} name
  21969. * Name of the component to get
  21970. *
  21971. * @return {Tech|undefined}
  21972. * The `Tech` or undefined if there was no tech with the name requsted.
  21973. */
  21974. Tech.getTech = function getTech(name) {
  21975. if (Tech.techs_ && Tech.techs_[name]) {
  21976. return Tech.techs_[name];
  21977. }
  21978. if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) {
  21979. _log2['default'].warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
  21980. return _window2['default'].videojs[name];
  21981. }
  21982. };
  21983. return Tech;
  21984. }(_component2['default']);
  21985. /**
  21986. * List of associated text tracks.
  21987. *
  21988. * @type {TextTrackList}
  21989. * @private
  21990. */
  21991. Tech.prototype.textTracks_; // eslint-disable-line
  21992. /**
  21993. * List of associated audio tracks.
  21994. *
  21995. * @type {AudioTrackList}
  21996. * @private
  21997. */
  21998. Tech.prototype.audioTracks_; // eslint-disable-line
  21999. /**
  22000. * List of associated video tracks.
  22001. *
  22002. * @type {VideoTrackList}
  22003. * @private
  22004. */
  22005. Tech.prototype.videoTracks_; // eslint-disable-line
  22006. /**
  22007. * Boolean indicating wether the `Tech` supports volume control.
  22008. *
  22009. * @type {boolean}
  22010. * @default
  22011. */
  22012. Tech.prototype.featuresVolumeControl = true;
  22013. /**
  22014. * Boolean indicating wether the `Tech` support fullscreen resize control.
  22015. * Resizing plugins using request fullscreen reloads the plugin
  22016. *
  22017. * @type {boolean}
  22018. * @default
  22019. */
  22020. Tech.prototype.featuresFullscreenResize = false;
  22021. /**
  22022. * Boolean indicating wether the `Tech` supports changing the speed at which the video
  22023. * plays. Examples:
  22024. * - Set player to play 2x (twice) as fast
  22025. * - Set player to play 0.5x (half) as fast
  22026. *
  22027. * @type {boolean}
  22028. * @default
  22029. */
  22030. Tech.prototype.featuresPlaybackRate = false;
  22031. /**
  22032. * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
  22033. * not triggered by video-js-swf. This will be used to determine if
  22034. * {@link Tech#manualProgressOn} should be called.
  22035. *
  22036. * @type {boolean}
  22037. * @default
  22038. */
  22039. Tech.prototype.featuresProgressEvents = false;
  22040. /**
  22041. * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
  22042. * not triggered by video-js-swf. This will be used to determine if
  22043. * {@link Tech#manualTimeUpdates} should be called.
  22044. *
  22045. * @type {boolean}
  22046. * @default
  22047. */
  22048. Tech.prototype.featuresTimeupdateEvents = false;
  22049. /**
  22050. * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
  22051. * This will help us integrate with native `TextTrack`s if the browser supports them.
  22052. *
  22053. * @type {boolean}
  22054. * @default
  22055. */
  22056. Tech.prototype.featuresNativeTextTracks = false;
  22057. /**
  22058. * A functional mixin for techs that want to use the Source Handler pattern.
  22059. * Source handlers are scripts for handling specific formats.
  22060. * The source handler pattern is used for adaptive formats (HLS, DASH) that
  22061. * manually load video data and feed it into a Source Buffer (Media Source Extensions)
  22062. * Example: `Tech.withSourceHandlers.call(MyTech);`
  22063. *
  22064. * @param {Tech} _Tech
  22065. * The tech to add source handler functions to.
  22066. *
  22067. * @mixes Tech~SourceHandlerAdditions
  22068. */
  22069. Tech.withSourceHandlers = function (_Tech) {
  22070. /**
  22071. * Register a source handler
  22072. *
  22073. * @param {Function} handler
  22074. * The source handler class
  22075. *
  22076. * @param {number} [index]
  22077. * Register it at the following index
  22078. */
  22079. _Tech.registerSourceHandler = function (handler, index) {
  22080. var handlers = _Tech.sourceHandlers;
  22081. if (!handlers) {
  22082. handlers = _Tech.sourceHandlers = [];
  22083. }
  22084. if (index === undefined) {
  22085. // add to the end of the list
  22086. index = handlers.length;
  22087. }
  22088. handlers.splice(index, 0, handler);
  22089. };
  22090. /**
  22091. * Check if the tech can support the given type. Also checks the
  22092. * Techs sourceHandlers.
  22093. *
  22094. * @param {string} type
  22095. * The mimetype to check.
  22096. *
  22097. * @return {string}
  22098. * 'probably', 'maybe', or '' (empty string)
  22099. */
  22100. _Tech.canPlayType = function (type) {
  22101. var handlers = _Tech.sourceHandlers || [];
  22102. var can = void 0;
  22103. for (var i = 0; i < handlers.length; i++) {
  22104. can = handlers[i].canPlayType(type);
  22105. if (can) {
  22106. return can;
  22107. }
  22108. }
  22109. return '';
  22110. };
  22111. /**
  22112. * Returns the first source handler that supports the source.
  22113. *
  22114. * TODO: Answer question: should 'probably' be prioritized over 'maybe'
  22115. *
  22116. * @param {Tech~SourceObject} source
  22117. * The source object
  22118. *
  22119. * @param {Object} options
  22120. * The options passed to the tech
  22121. *
  22122. * @return {SourceHandler|null}
  22123. * The first source handler that supports the source or null if
  22124. * no SourceHandler supports the source
  22125. */
  22126. _Tech.selectSourceHandler = function (source, options) {
  22127. var handlers = _Tech.sourceHandlers || [];
  22128. var can = void 0;
  22129. for (var i = 0; i < handlers.length; i++) {
  22130. can = handlers[i].canHandleSource(source, options);
  22131. if (can) {
  22132. return handlers[i];
  22133. }
  22134. }
  22135. return null;
  22136. };
  22137. /**
  22138. * Check if the tech can support the given source.
  22139. *
  22140. * @param {Tech~SourceObject} srcObj
  22141. * The source object
  22142. *
  22143. * @param {Object} options
  22144. * The options passed to the tech
  22145. *
  22146. * @return {string}
  22147. * 'probably', 'maybe', or '' (empty string)
  22148. */
  22149. _Tech.canPlaySource = function (srcObj, options) {
  22150. var sh = _Tech.selectSourceHandler(srcObj, options);
  22151. if (sh) {
  22152. return sh.canHandleSource(srcObj, options);
  22153. }
  22154. return '';
  22155. };
  22156. /**
  22157. * When using a source handler, prefer its implementation of
  22158. * any function normally provided by the tech.
  22159. */
  22160. var deferrable = ['seekable', 'duration'];
  22161. /**
  22162. * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
  22163. * function if it exists, with a fallback to the Techs seekable function.
  22164. *
  22165. * @method _Tech.seekable
  22166. */
  22167. /**
  22168. * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
  22169. * function if it exists, otherwise it will fallback to the techs duration function.
  22170. *
  22171. * @method _Tech.duration
  22172. */
  22173. deferrable.forEach(function (fnName) {
  22174. var originalFn = this[fnName];
  22175. if (typeof originalFn !== 'function') {
  22176. return;
  22177. }
  22178. this[fnName] = function () {
  22179. if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
  22180. return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
  22181. }
  22182. return originalFn.apply(this, arguments);
  22183. };
  22184. }, _Tech.prototype);
  22185. /**
  22186. * Create a function for setting the source using a source object
  22187. * and source handlers.
  22188. * Should never be called unless a source handler was found.
  22189. *
  22190. * @param {Tech~SourceObject} source
  22191. * A source object with src and type keys
  22192. *
  22193. * @return {Tech}
  22194. * Returns itself; this method is chainable
  22195. */
  22196. _Tech.prototype.setSource = function (source) {
  22197. var sh = _Tech.selectSourceHandler(source, this.options_);
  22198. if (!sh) {
  22199. // Fall back to a native source hander when unsupported sources are
  22200. // deliberately set
  22201. if (_Tech.nativeSourceHandler) {
  22202. sh = _Tech.nativeSourceHandler;
  22203. } else {
  22204. _log2['default'].error('No source hander found for the current source.');
  22205. }
  22206. }
  22207. // Dispose any existing source handler
  22208. this.disposeSourceHandler();
  22209. this.off('dispose', this.disposeSourceHandler);
  22210. if (sh !== _Tech.nativeSourceHandler) {
  22211. this.currentSource_ = source;
  22212. // Catch if someone replaced the src without calling setSource.
  22213. // If they do, set currentSource_ to null and dispose our source handler.
  22214. this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
  22215. this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
  22216. this.one(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
  22217. }
  22218. this.sourceHandler_ = sh.handleSource(source, this, this.options_);
  22219. this.on('dispose', this.disposeSourceHandler);
  22220. return this;
  22221. };
  22222. /**
  22223. * Called once for the first loadstart of a video.
  22224. *
  22225. * @listens Tech#loadstart
  22226. */
  22227. _Tech.prototype.firstLoadStartListener_ = function () {
  22228. this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
  22229. };
  22230. // On successive loadstarts when setSource has not been called again
  22231. /**
  22232. * Called after the first loadstart for a video occurs.
  22233. *
  22234. * @listens Tech#loadstart
  22235. */
  22236. _Tech.prototype.successiveLoadStartListener_ = function () {
  22237. this.disposeSourceHandler();
  22238. this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
  22239. };
  22240. /**
  22241. * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
  22242. *
  22243. * @listens Tech#dispose
  22244. */
  22245. _Tech.prototype.disposeSourceHandler = function () {
  22246. // if we have a source and get another one
  22247. // then we are loading something new
  22248. // than clear all of our current tracks
  22249. if (this.currentSource_) {
  22250. this.clearTracks(['audio', 'video']);
  22251. this.currentSource_ = null;
  22252. }
  22253. // always clean up auto-text tracks
  22254. this.cleanupAutoTextTracks();
  22255. if (this.sourceHandler_) {
  22256. this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
  22257. this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
  22258. if (this.sourceHandler_.dispose) {
  22259. this.sourceHandler_.dispose();
  22260. }
  22261. this.sourceHandler_ = null;
  22262. }
  22263. };
  22264. };
  22265. _component2['default'].registerComponent('Tech', Tech);
  22266. // Old name for Tech
  22267. // @deprecated
  22268. _component2['default'].registerComponent('MediaTechController', Tech);
  22269. Tech.registerTech('Tech', Tech);
  22270. exports['default'] = Tech;
  22271. },{"../component":47,"../media-error.js":88,"../tracks/audio-track-list":105,"../tracks/html-track-element":108,"../tracks/html-track-element-list":107,"../tracks/text-track":114,"../tracks/text-track-list":112,"../tracks/video-track-list":118,"../utils/buffer.js":121,"../utils/fn.js":125,"../utils/log.js":128,"../utils/merge-options.js":129,"../utils/obj":130,"../utils/time-ranges.js":132,"global/document":136,"global/window":137,"videojs-vtt.js":138}],105:[function(require,module,exports){
  22272. 'use strict';
  22273. exports.__esModule = true;
  22274. var _trackList = require('./track-list');
  22275. var _trackList2 = _interopRequireDefault(_trackList);
  22276. var _browser = require('../utils/browser.js');
  22277. var browser = _interopRequireWildcard(_browser);
  22278. var _document = require('global/document');
  22279. var _document2 = _interopRequireDefault(_document);
  22280. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  22281. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  22282. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  22283. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  22284. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  22285. * @file audio-track-list.js
  22286. */
  22287. /**
  22288. * Anywhere we call this function we diverge from the spec
  22289. * as we only support one enabled audiotrack at a time
  22290. *
  22291. * @param {AudioTrackList} list
  22292. * list to work on
  22293. *
  22294. * @param {AudioTrack} track
  22295. * The track to skip
  22296. *
  22297. * @private
  22298. */
  22299. var disableOthers = function disableOthers(list, track) {
  22300. for (var i = 0; i < list.length; i++) {
  22301. if (track.id === list[i].id) {
  22302. continue;
  22303. }
  22304. // another audio track is enabled, disable it
  22305. list[i].enabled = false;
  22306. }
  22307. };
  22308. /**
  22309. * The current list of {@link AudioTrack} for a media file.
  22310. *
  22311. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
  22312. * @extends TrackList
  22313. */
  22314. var AudioTrackList = function (_TrackList) {
  22315. _inherits(AudioTrackList, _TrackList);
  22316. /**
  22317. * Create an instance of this class.
  22318. *
  22319. * @param {AudioTrack[]} [tracks=[]]
  22320. * A list of `AudioTrack` to instantiate the list with.
  22321. */
  22322. function AudioTrackList() {
  22323. var _this, _ret;
  22324. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  22325. _classCallCheck(this, AudioTrackList);
  22326. var list = void 0;
  22327. // make sure only 1 track is enabled
  22328. // sorted from last index to first index
  22329. for (var i = tracks.length - 1; i >= 0; i--) {
  22330. if (tracks[i].enabled) {
  22331. disableOthers(tracks, tracks[i]);
  22332. break;
  22333. }
  22334. }
  22335. // IE8 forces us to implement inheritance ourselves
  22336. // as it does not support Object.defineProperty properly
  22337. if (browser.IS_IE8) {
  22338. list = _document2['default'].createElement('custom');
  22339. for (var prop in _trackList2['default'].prototype) {
  22340. if (prop !== 'constructor') {
  22341. list[prop] = _trackList2['default'].prototype[prop];
  22342. }
  22343. }
  22344. for (var _prop in AudioTrackList.prototype) {
  22345. if (_prop !== 'constructor') {
  22346. list[_prop] = AudioTrackList.prototype[_prop];
  22347. }
  22348. }
  22349. }
  22350. list = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  22351. list.changing_ = false;
  22352. return _ret = list, _possibleConstructorReturn(_this, _ret);
  22353. }
  22354. /**
  22355. * Add an {@link AudioTrack} to the `AudioTrackList`.
  22356. *
  22357. * @param {AudioTrack} track
  22358. * The AudioTrack to add to the list
  22359. *
  22360. * @fires Track#addtrack
  22361. * @private
  22362. */
  22363. AudioTrackList.prototype.addTrack_ = function addTrack_(track) {
  22364. var _this2 = this;
  22365. if (track.enabled) {
  22366. disableOthers(this, track);
  22367. }
  22368. _TrackList.prototype.addTrack_.call(this, track);
  22369. // native tracks don't have this
  22370. if (!track.addEventListener) {
  22371. return;
  22372. }
  22373. /**
  22374. * @listens AudioTrack#enabledchange
  22375. * @fires TrackList#change
  22376. */
  22377. track.addEventListener('enabledchange', function () {
  22378. // when we are disabling other tracks (since we don't support
  22379. // more than one track at a time) we will set changing_
  22380. // to true so that we don't trigger additional change events
  22381. if (_this2.changing_) {
  22382. return;
  22383. }
  22384. _this2.changing_ = true;
  22385. disableOthers(_this2, track);
  22386. _this2.changing_ = false;
  22387. _this2.trigger('change');
  22388. });
  22389. };
  22390. /**
  22391. * Add an {@link AudioTrack} to the `AudioTrackList`.
  22392. *
  22393. * @param {AudioTrack} track
  22394. * The AudioTrack to add to the list
  22395. *
  22396. * @fires Track#addtrack
  22397. */
  22398. AudioTrackList.prototype.addTrack = function addTrack(track) {
  22399. this.addTrack_(track);
  22400. };
  22401. /**
  22402. * Remove an {@link AudioTrack} from the `AudioTrackList`.
  22403. *
  22404. * @param {AudioTrack} track
  22405. * The AudioTrack to remove from the list
  22406. *
  22407. * @fires Track#removetrack
  22408. */
  22409. AudioTrackList.prototype.removeTrack = function removeTrack(track) {
  22410. _TrackList.prototype.removeTrack_.call(this, track);
  22411. };
  22412. return AudioTrackList;
  22413. }(_trackList2['default']);
  22414. exports['default'] = AudioTrackList;
  22415. },{"../utils/browser.js":120,"./track-list":116,"global/document":136}],106:[function(require,module,exports){
  22416. 'use strict';
  22417. exports.__esModule = true;
  22418. var _trackEnums = require('./track-enums');
  22419. var _track = require('./track');
  22420. var _track2 = _interopRequireDefault(_track);
  22421. var _mergeOptions = require('../utils/merge-options');
  22422. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  22423. var _browser = require('../utils/browser.js');
  22424. var browser = _interopRequireWildcard(_browser);
  22425. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  22426. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  22427. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  22428. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  22429. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  22430. /**
  22431. * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
  22432. * only one `AudioTrack` in the list will be enabled at a time.
  22433. *
  22434. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
  22435. * @extends Track
  22436. */
  22437. var AudioTrack = function (_Track) {
  22438. _inherits(AudioTrack, _Track);
  22439. /**
  22440. * Create an instance of this class.
  22441. *
  22442. * @param {Object} [options={}]
  22443. * Object of option names and values
  22444. *
  22445. * @param {AudioTrack~Kind} [options.kind='']
  22446. * A valid audio track kind
  22447. *
  22448. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  22449. * A unique id for this AudioTrack.
  22450. *
  22451. * @param {string} [options.label='']
  22452. * The menu label for this track.
  22453. *
  22454. * @param {string} [options.language='']
  22455. * A valid two character language code.
  22456. *
  22457. * @param {boolean} [options.enabled]
  22458. * If this track is the one that is currently playing. If this track is part of
  22459. * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
  22460. */
  22461. function AudioTrack() {
  22462. var _this, _ret;
  22463. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  22464. _classCallCheck(this, AudioTrack);
  22465. var settings = (0, _mergeOptions2['default'])(options, {
  22466. kind: _trackEnums.AudioTrackKind[options.kind] || ''
  22467. });
  22468. // on IE8 this will be a document element
  22469. // for every other browser this will be a normal object
  22470. var track = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  22471. var enabled = false;
  22472. if (browser.IS_IE8) {
  22473. for (var prop in AudioTrack.prototype) {
  22474. if (prop !== 'constructor') {
  22475. track[prop] = AudioTrack.prototype[prop];
  22476. }
  22477. }
  22478. }
  22479. /**
  22480. * @member {boolean} enabled
  22481. * If this `AudioTrack` is enabled or not. When setting this will
  22482. * fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
  22483. *
  22484. * @fires VideoTrack#selectedchange
  22485. */
  22486. Object.defineProperty(track, 'enabled', {
  22487. get: function get() {
  22488. return enabled;
  22489. },
  22490. set: function set(newEnabled) {
  22491. // an invalid or unchanged value
  22492. if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
  22493. return;
  22494. }
  22495. enabled = newEnabled;
  22496. /**
  22497. * An event that fires when enabled changes on this track. This allows
  22498. * the AudioTrackList that holds this track to act accordingly.
  22499. *
  22500. * > Note: This is not part of the spec! Native tracks will do
  22501. * this internally without an event.
  22502. *
  22503. * @event AudioTrack#enabledchange
  22504. * @type {EventTarget~Event}
  22505. */
  22506. this.trigger('enabledchange');
  22507. }
  22508. });
  22509. // if the user sets this track to selected then
  22510. // set selected to that true value otherwise
  22511. // we keep it false
  22512. if (settings.enabled) {
  22513. track.enabled = settings.enabled;
  22514. }
  22515. track.loaded_ = true;
  22516. return _ret = track, _possibleConstructorReturn(_this, _ret);
  22517. }
  22518. return AudioTrack;
  22519. }(_track2['default']);
  22520. exports['default'] = AudioTrack;
  22521. },{"../utils/browser.js":120,"../utils/merge-options":129,"./track":117,"./track-enums":115}],107:[function(require,module,exports){
  22522. 'use strict';
  22523. exports.__esModule = true;
  22524. var _browser = require('../utils/browser.js');
  22525. var browser = _interopRequireWildcard(_browser);
  22526. var _document = require('global/document');
  22527. var _document2 = _interopRequireDefault(_document);
  22528. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  22529. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  22530. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /**
  22531. * @file html-track-element-list.js
  22532. */
  22533. /**
  22534. * The current list of {@link HtmlTrackElement}s.
  22535. */
  22536. var HtmlTrackElementList = function () {
  22537. /**
  22538. * Create an instance of this class.
  22539. *
  22540. * @param {HtmlTrackElement[]} [tracks=[]]
  22541. * A list of `HtmlTrackElement` to instantiate the list with.
  22542. */
  22543. function HtmlTrackElementList() {
  22544. var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  22545. _classCallCheck(this, HtmlTrackElementList);
  22546. var list = this; // eslint-disable-line
  22547. if (browser.IS_IE8) {
  22548. list = _document2['default'].createElement('custom');
  22549. for (var prop in HtmlTrackElementList.prototype) {
  22550. if (prop !== 'constructor') {
  22551. list[prop] = HtmlTrackElementList.prototype[prop];
  22552. }
  22553. }
  22554. }
  22555. list.trackElements_ = [];
  22556. /**
  22557. * @member {number} length
  22558. * The current number of `Track`s in the this Trackist.
  22559. */
  22560. Object.defineProperty(list, 'length', {
  22561. get: function get() {
  22562. return this.trackElements_.length;
  22563. }
  22564. });
  22565. for (var i = 0, length = trackElements.length; i < length; i++) {
  22566. list.addTrackElement_(trackElements[i]);
  22567. }
  22568. if (browser.IS_IE8) {
  22569. return list;
  22570. }
  22571. }
  22572. /**
  22573. * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
  22574. *
  22575. * @param {HtmlTrackElement} trackElement
  22576. * The track element to add to the list.
  22577. *
  22578. * @private
  22579. */
  22580. HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
  22581. var index = this.trackElements_.length;
  22582. if (!('' + index in this)) {
  22583. Object.defineProperty(this, index, {
  22584. get: function get() {
  22585. return this.trackElements_[index];
  22586. }
  22587. });
  22588. }
  22589. // Do not add duplicate elements
  22590. if (this.trackElements_.indexOf(trackElement) === -1) {
  22591. this.trackElements_.push(trackElement);
  22592. }
  22593. };
  22594. /**
  22595. * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
  22596. * {@link TextTrack}.
  22597. *
  22598. * @param {TextTrack} track
  22599. * The track associated with a track element.
  22600. *
  22601. * @return {HtmlTrackElement|undefined}
  22602. * The track element that was found or undefined.
  22603. *
  22604. * @private
  22605. */
  22606. HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
  22607. var trackElement_ = void 0;
  22608. for (var i = 0, length = this.trackElements_.length; i < length; i++) {
  22609. if (track === this.trackElements_[i].track) {
  22610. trackElement_ = this.trackElements_[i];
  22611. break;
  22612. }
  22613. }
  22614. return trackElement_;
  22615. };
  22616. /**
  22617. * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
  22618. *
  22619. * @param {HtmlTrackElement} trackElement
  22620. * The track element to remove from the list.
  22621. *
  22622. * @private
  22623. */
  22624. HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
  22625. for (var i = 0, length = this.trackElements_.length; i < length; i++) {
  22626. if (trackElement === this.trackElements_[i]) {
  22627. this.trackElements_.splice(i, 1);
  22628. break;
  22629. }
  22630. }
  22631. };
  22632. return HtmlTrackElementList;
  22633. }();
  22634. exports['default'] = HtmlTrackElementList;
  22635. },{"../utils/browser.js":120,"global/document":136}],108:[function(require,module,exports){
  22636. 'use strict';
  22637. exports.__esModule = true;
  22638. var _browser = require('../utils/browser.js');
  22639. var browser = _interopRequireWildcard(_browser);
  22640. var _document = require('global/document');
  22641. var _document2 = _interopRequireDefault(_document);
  22642. var _eventTarget = require('../event-target');
  22643. var _eventTarget2 = _interopRequireDefault(_eventTarget);
  22644. var _textTrack = require('../tracks/text-track');
  22645. var _textTrack2 = _interopRequireDefault(_textTrack);
  22646. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  22647. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  22648. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  22649. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  22650. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  22651. * @file html-track-element.js
  22652. */
  22653. /**
  22654. * @typedef {HTMLTrackElement~ReadyState}
  22655. * @enum {number}
  22656. */
  22657. var NONE = 0;
  22658. var LOADING = 1;
  22659. var LOADED = 2;
  22660. var ERROR = 3;
  22661. /**
  22662. * A single track represented in the DOM.
  22663. *
  22664. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
  22665. * @extends EventTarget
  22666. */
  22667. var HTMLTrackElement = function (_EventTarget) {
  22668. _inherits(HTMLTrackElement, _EventTarget);
  22669. /**
  22670. * Create an instance of this class.
  22671. *
  22672. * @param {Object} options={}
  22673. * Object of option names and values
  22674. *
  22675. * @param {Tech} options.tech
  22676. * A reference to the tech that owns this HTMLTrackElement.
  22677. *
  22678. * @param {TextTrack~Kind} [options.kind='subtitles']
  22679. * A valid text track kind.
  22680. *
  22681. * @param {TextTrack~Mode} [options.mode='disabled']
  22682. * A valid text track mode.
  22683. *
  22684. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  22685. * A unique id for this TextTrack.
  22686. *
  22687. * @param {string} [options.label='']
  22688. * The menu label for this track.
  22689. *
  22690. * @param {string} [options.language='']
  22691. * A valid two character language code.
  22692. *
  22693. * @param {string} [options.srclang='']
  22694. * A valid two character language code. An alternative, but deprioritized
  22695. * vesion of `options.language`
  22696. *
  22697. * @param {string} [options.src]
  22698. * A url to TextTrack cues.
  22699. *
  22700. * @param {boolean} [options.default]
  22701. * If this track should default to on or off.
  22702. */
  22703. function HTMLTrackElement() {
  22704. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  22705. _classCallCheck(this, HTMLTrackElement);
  22706. var _this = _possibleConstructorReturn(this, _EventTarget.call(this));
  22707. var readyState = void 0;
  22708. var trackElement = _this; // eslint-disable-line
  22709. if (browser.IS_IE8) {
  22710. trackElement = _document2['default'].createElement('custom');
  22711. for (var prop in HTMLTrackElement.prototype) {
  22712. if (prop !== 'constructor') {
  22713. trackElement[prop] = HTMLTrackElement.prototype[prop];
  22714. }
  22715. }
  22716. }
  22717. var track = new _textTrack2['default'](options);
  22718. trackElement.kind = track.kind;
  22719. trackElement.src = track.src;
  22720. trackElement.srclang = track.language;
  22721. trackElement.label = track.label;
  22722. trackElement['default'] = track['default'];
  22723. /**
  22724. * @member {HTMLTrackElement~ReadyState} readyState
  22725. * The current ready state of the track element.
  22726. */
  22727. Object.defineProperty(trackElement, 'readyState', {
  22728. get: function get() {
  22729. return readyState;
  22730. }
  22731. });
  22732. /**
  22733. * @member {TextTrack} track
  22734. * The underlying TextTrack object.
  22735. */
  22736. Object.defineProperty(trackElement, 'track', {
  22737. get: function get() {
  22738. return track;
  22739. }
  22740. });
  22741. readyState = NONE;
  22742. /**
  22743. * @listens TextTrack#loadeddata
  22744. * @fires HTMLTrackElement#load
  22745. */
  22746. track.addEventListener('loadeddata', function () {
  22747. readyState = LOADED;
  22748. trackElement.trigger({
  22749. type: 'load',
  22750. target: trackElement
  22751. });
  22752. });
  22753. if (browser.IS_IE8) {
  22754. var _ret;
  22755. return _ret = trackElement, _possibleConstructorReturn(_this, _ret);
  22756. }
  22757. return _this;
  22758. }
  22759. return HTMLTrackElement;
  22760. }(_eventTarget2['default']);
  22761. HTMLTrackElement.prototype.allowedEvents_ = {
  22762. load: 'load'
  22763. };
  22764. HTMLTrackElement.NONE = NONE;
  22765. HTMLTrackElement.LOADING = LOADING;
  22766. HTMLTrackElement.LOADED = LOADED;
  22767. HTMLTrackElement.ERROR = ERROR;
  22768. exports['default'] = HTMLTrackElement;
  22769. },{"../event-target":84,"../tracks/text-track":114,"../utils/browser.js":120,"global/document":136}],109:[function(require,module,exports){
  22770. 'use strict';
  22771. exports.__esModule = true;
  22772. var _browser = require('../utils/browser.js');
  22773. var browser = _interopRequireWildcard(_browser);
  22774. var _document = require('global/document');
  22775. var _document2 = _interopRequireDefault(_document);
  22776. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  22777. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  22778. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /**
  22779. * @file text-track-cue-list.js
  22780. */
  22781. /**
  22782. * @typedef {Object} TextTrackCue
  22783. *
  22784. * @property {string} id
  22785. * The unique id for this text track cue
  22786. *
  22787. * @property {number} startTime
  22788. * The start time for this text track cue
  22789. *
  22790. * @property {number} endTime
  22791. * The end time for this text track cue
  22792. *
  22793. * @property {boolean} pauseOnExit
  22794. * Pause when the end time is reached if true.
  22795. *
  22796. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
  22797. */
  22798. /**
  22799. * A List of TextTrackCues.
  22800. *
  22801. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
  22802. */
  22803. var TextTrackCueList = function () {
  22804. /**
  22805. * Create an instance of this class..
  22806. *
  22807. * @param {Array} cues
  22808. * A list of cues to be initialized with
  22809. */
  22810. function TextTrackCueList(cues) {
  22811. _classCallCheck(this, TextTrackCueList);
  22812. var list = this; // eslint-disable-line
  22813. if (browser.IS_IE8) {
  22814. list = _document2['default'].createElement('custom');
  22815. for (var prop in TextTrackCueList.prototype) {
  22816. if (prop !== 'constructor') {
  22817. list[prop] = TextTrackCueList.prototype[prop];
  22818. }
  22819. }
  22820. }
  22821. TextTrackCueList.prototype.setCues_.call(list, cues);
  22822. /**
  22823. * @member {number} length
  22824. * The current number of `TextTrackCue`s in the TextTrackCueList.
  22825. */
  22826. Object.defineProperty(list, 'length', {
  22827. get: function get() {
  22828. return this.length_;
  22829. }
  22830. });
  22831. if (browser.IS_IE8) {
  22832. return list;
  22833. }
  22834. }
  22835. /**
  22836. * A setter for cues in this list. Creates getters
  22837. * an an index for the cues.
  22838. *
  22839. * @param {Array} cues
  22840. * An array of cues to set
  22841. *
  22842. * @private
  22843. */
  22844. TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
  22845. var oldLength = this.length || 0;
  22846. var i = 0;
  22847. var l = cues.length;
  22848. this.cues_ = cues;
  22849. this.length_ = cues.length;
  22850. var defineProp = function defineProp(index) {
  22851. if (!('' + index in this)) {
  22852. Object.defineProperty(this, '' + index, {
  22853. get: function get() {
  22854. return this.cues_[index];
  22855. }
  22856. });
  22857. }
  22858. };
  22859. if (oldLength < l) {
  22860. i = oldLength;
  22861. for (; i < l; i++) {
  22862. defineProp.call(this, i);
  22863. }
  22864. }
  22865. };
  22866. /**
  22867. * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
  22868. *
  22869. * @param {string} id
  22870. * The id of the cue that should be searched for.
  22871. *
  22872. * @return {TextTrackCue|null}
  22873. * A single cue or null if none was found.
  22874. */
  22875. TextTrackCueList.prototype.getCueById = function getCueById(id) {
  22876. var result = null;
  22877. for (var i = 0, l = this.length; i < l; i++) {
  22878. var cue = this[i];
  22879. if (cue.id === id) {
  22880. result = cue;
  22881. break;
  22882. }
  22883. }
  22884. return result;
  22885. };
  22886. return TextTrackCueList;
  22887. }();
  22888. exports['default'] = TextTrackCueList;
  22889. },{"../utils/browser.js":120,"global/document":136}],110:[function(require,module,exports){
  22890. 'use strict';
  22891. exports.__esModule = true;
  22892. var _component = require('../component');
  22893. var _component2 = _interopRequireDefault(_component);
  22894. var _fn = require('../utils/fn.js');
  22895. var Fn = _interopRequireWildcard(_fn);
  22896. var _window = require('global/window');
  22897. var _window2 = _interopRequireDefault(_window);
  22898. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  22899. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  22900. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  22901. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  22902. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  22903. * @file text-track-display.js
  22904. */
  22905. var darkGray = '#222';
  22906. var lightGray = '#ccc';
  22907. var fontMap = {
  22908. monospace: 'monospace',
  22909. sansSerif: 'sans-serif',
  22910. serif: 'serif',
  22911. monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
  22912. monospaceSerif: '"Courier New", monospace',
  22913. proportionalSansSerif: 'sans-serif',
  22914. proportionalSerif: 'serif',
  22915. casual: '"Comic Sans MS", Impact, fantasy',
  22916. script: '"Monotype Corsiva", cursive',
  22917. smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
  22918. };
  22919. /**
  22920. * Construct an rgba color from a given hex color code.
  22921. *
  22922. * @param {number} color
  22923. * Hex number for color, like #f0e.
  22924. *
  22925. * @param {number} opacity
  22926. * Value for opacity, 0.0 - 1.0.
  22927. *
  22928. * @return {string}
  22929. * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
  22930. *
  22931. * @private
  22932. */
  22933. function constructColor(color, opacity) {
  22934. return 'rgba(' +
  22935. // color looks like "#f0e"
  22936. parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')';
  22937. }
  22938. /**
  22939. * Try to update the style of a DOM element. Some style changes will throw an error,
  22940. * particularly in IE8. Those should be noops.
  22941. *
  22942. * @param {Element} el
  22943. * The DOM element to be styled.
  22944. *
  22945. * @param {string} style
  22946. * The CSS property on the element that should be styled.
  22947. *
  22948. * @param {string} rule
  22949. * The style rule that should be applied to the property.
  22950. */
  22951. function tryUpdateStyle(el, style, rule) {
  22952. try {
  22953. el.style[style] = rule;
  22954. } catch (e) {
  22955. // Satisfies linter.
  22956. return;
  22957. }
  22958. }
  22959. /**
  22960. * The component for displaying text track cues.
  22961. *
  22962. * @extends Component
  22963. */
  22964. var TextTrackDisplay = function (_Component) {
  22965. _inherits(TextTrackDisplay, _Component);
  22966. /**
  22967. * Creates an instance of this class.
  22968. *
  22969. * @param {Player} player
  22970. * The `Player` that this class should be attached to.
  22971. *
  22972. * @param {Object} [options]
  22973. * The key/value store of player options.
  22974. *
  22975. * @param {Component~ReadyCallback} [ready]
  22976. * The function to call when `TextTrackDisplay` is ready.
  22977. */
  22978. function TextTrackDisplay(player, options, ready) {
  22979. _classCallCheck(this, TextTrackDisplay);
  22980. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options, ready));
  22981. player.on('loadstart', Fn.bind(_this, _this.toggleDisplay));
  22982. player.on('texttrackchange', Fn.bind(_this, _this.updateDisplay));
  22983. // This used to be called during player init, but was causing an error
  22984. // if a track should show by default and the display hadn't loaded yet.
  22985. // Should probably be moved to an external track loader when we support
  22986. // tracks that don't need a display.
  22987. player.ready(Fn.bind(_this, function () {
  22988. if (player.tech_ && player.tech_.featuresNativeTextTracks) {
  22989. this.hide();
  22990. return;
  22991. }
  22992. player.on('fullscreenchange', Fn.bind(this, this.updateDisplay));
  22993. var tracks = this.options_.playerOptions.tracks || [];
  22994. for (var i = 0; i < tracks.length; i++) {
  22995. this.player_.addRemoteTextTrack(tracks[i], true);
  22996. }
  22997. var modes = { captions: 1, subtitles: 1 };
  22998. var trackList = this.player_.textTracks();
  22999. var firstDesc = void 0;
  23000. var firstCaptions = void 0;
  23001. if (trackList) {
  23002. for (var _i = 0; _i < trackList.length; _i++) {
  23003. var track = trackList[_i];
  23004. if (track['default']) {
  23005. if (track.kind === 'descriptions' && !firstDesc) {
  23006. firstDesc = track;
  23007. } else if (track.kind in modes && !firstCaptions) {
  23008. firstCaptions = track;
  23009. }
  23010. }
  23011. }
  23012. // We want to show the first default track but captions and subtitles
  23013. // take precedence over descriptions.
  23014. // So, display the first default captions or subtitles track
  23015. // and otherwise the first default descriptions track.
  23016. if (firstCaptions) {
  23017. firstCaptions.mode = 'showing';
  23018. } else if (firstDesc) {
  23019. firstDesc.mode = 'showing';
  23020. }
  23021. }
  23022. }));
  23023. return _this;
  23024. }
  23025. /**
  23026. * Turn display of {@link TextTrack}'s from the current state into the other state.
  23027. * There are only two states:
  23028. * - 'shown'
  23029. * - 'hidden'
  23030. *
  23031. * @listens Player#loadstart
  23032. */
  23033. TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
  23034. if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
  23035. this.hide();
  23036. } else {
  23037. this.show();
  23038. }
  23039. };
  23040. /**
  23041. * Create the {@link Component}'s DOM element.
  23042. *
  23043. * @return {Element}
  23044. * The element that was created.
  23045. */
  23046. TextTrackDisplay.prototype.createEl = function createEl() {
  23047. return _Component.prototype.createEl.call(this, 'div', {
  23048. className: 'vjs-text-track-display'
  23049. }, {
  23050. 'aria-live': 'off',
  23051. 'aria-atomic': 'true'
  23052. });
  23053. };
  23054. /**
  23055. * Clear all displayed {@link TextTrack}s.
  23056. */
  23057. TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
  23058. if (typeof _window2['default'].WebVTT === 'function') {
  23059. _window2['default'].WebVTT.processCues(_window2['default'], [], this.el_);
  23060. }
  23061. };
  23062. /**
  23063. * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
  23064. * a {@link Player#fullscreenchange} is fired.
  23065. *
  23066. * @listens Player#texttrackchange
  23067. * @listens Player#fullscreenchange
  23068. */
  23069. TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
  23070. var tracks = this.player_.textTracks();
  23071. this.clearDisplay();
  23072. if (!tracks) {
  23073. return;
  23074. }
  23075. // Track display prioritization model: if multiple tracks are 'showing',
  23076. // display the first 'subtitles' or 'captions' track which is 'showing',
  23077. // otherwise display the first 'descriptions' track which is 'showing'
  23078. var descriptionsTrack = null;
  23079. var captionsSubtitlesTrack = null;
  23080. var i = tracks.length;
  23081. while (i--) {
  23082. var track = tracks[i];
  23083. if (track.mode === 'showing') {
  23084. if (track.kind === 'descriptions') {
  23085. descriptionsTrack = track;
  23086. } else {
  23087. captionsSubtitlesTrack = track;
  23088. }
  23089. }
  23090. }
  23091. if (captionsSubtitlesTrack) {
  23092. if (this.getAttribute('aria-live') !== 'off') {
  23093. this.setAttribute('aria-live', 'off');
  23094. }
  23095. this.updateForTrack(captionsSubtitlesTrack);
  23096. } else if (descriptionsTrack) {
  23097. if (this.getAttribute('aria-live') !== 'assertive') {
  23098. this.setAttribute('aria-live', 'assertive');
  23099. }
  23100. this.updateForTrack(descriptionsTrack);
  23101. }
  23102. };
  23103. /**
  23104. * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
  23105. *
  23106. * @param {TextTrack} track
  23107. * Text track object to be added to the list.
  23108. */
  23109. TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
  23110. if (typeof _window2['default'].WebVTT !== 'function' || !track.activeCues) {
  23111. return;
  23112. }
  23113. var overrides = this.player_.textTrackSettings.getValues();
  23114. var cues = [];
  23115. for (var _i2 = 0; _i2 < track.activeCues.length; _i2++) {
  23116. cues.push(track.activeCues[_i2]);
  23117. }
  23118. _window2['default'].WebVTT.processCues(_window2['default'], cues, this.el_);
  23119. var i = cues.length;
  23120. while (i--) {
  23121. var cue = cues[i];
  23122. if (!cue) {
  23123. continue;
  23124. }
  23125. var cueDiv = cue.displayState;
  23126. if (overrides.color) {
  23127. cueDiv.firstChild.style.color = overrides.color;
  23128. }
  23129. if (overrides.textOpacity) {
  23130. tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
  23131. }
  23132. if (overrides.backgroundColor) {
  23133. cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
  23134. }
  23135. if (overrides.backgroundOpacity) {
  23136. tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
  23137. }
  23138. if (overrides.windowColor) {
  23139. if (overrides.windowOpacity) {
  23140. tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
  23141. } else {
  23142. cueDiv.style.backgroundColor = overrides.windowColor;
  23143. }
  23144. }
  23145. if (overrides.edgeStyle) {
  23146. if (overrides.edgeStyle === 'dropshadow') {
  23147. cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
  23148. } else if (overrides.edgeStyle === 'raised') {
  23149. cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
  23150. } else if (overrides.edgeStyle === 'depressed') {
  23151. cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
  23152. } else if (overrides.edgeStyle === 'uniform') {
  23153. cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
  23154. }
  23155. }
  23156. if (overrides.fontPercent && overrides.fontPercent !== 1) {
  23157. var fontSize = _window2['default'].parseFloat(cueDiv.style.fontSize);
  23158. cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
  23159. cueDiv.style.height = 'auto';
  23160. cueDiv.style.top = 'auto';
  23161. cueDiv.style.bottom = '2px';
  23162. }
  23163. if (overrides.fontFamily && overrides.fontFamily !== 'default') {
  23164. if (overrides.fontFamily === 'small-caps') {
  23165. cueDiv.firstChild.style.fontVariant = 'small-caps';
  23166. } else {
  23167. cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
  23168. }
  23169. }
  23170. }
  23171. };
  23172. return TextTrackDisplay;
  23173. }(_component2['default']);
  23174. _component2['default'].registerComponent('TextTrackDisplay', TextTrackDisplay);
  23175. exports['default'] = TextTrackDisplay;
  23176. },{"../component":47,"../utils/fn.js":125,"global/window":137}],111:[function(require,module,exports){
  23177. 'use strict';
  23178. exports.__esModule = true;
  23179. /**
  23180. * @file text-track-list-converter.js Utilities for capturing text track state and
  23181. * re-creating tracks based on a capture.
  23182. *
  23183. * @module text-track-list-converter
  23184. */
  23185. /**
  23186. * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
  23187. * represents the {@link TextTrack}'s state.
  23188. *
  23189. * @param {TextTrack} track
  23190. * The text track to query.
  23191. *
  23192. * @return {Object}
  23193. * A serializable javascript representation of the TextTrack.
  23194. * @private
  23195. */
  23196. var trackToJson_ = function trackToJson_(track) {
  23197. var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
  23198. if (track[prop]) {
  23199. acc[prop] = track[prop];
  23200. }
  23201. return acc;
  23202. }, {
  23203. cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
  23204. return {
  23205. startTime: cue.startTime,
  23206. endTime: cue.endTime,
  23207. text: cue.text,
  23208. id: cue.id
  23209. };
  23210. })
  23211. });
  23212. return ret;
  23213. };
  23214. /**
  23215. * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
  23216. * state of all {@link TextTrack}s currently configured. The return array is compatible with
  23217. * {@link text-track-list-converter:jsonToTextTracks}.
  23218. *
  23219. * @param {Tech} tech
  23220. * The tech object to query
  23221. *
  23222. * @return {Array}
  23223. * A serializable javascript representation of the {@link Tech}s
  23224. * {@link TextTrackList}.
  23225. */
  23226. var textTracksToJson = function textTracksToJson(tech) {
  23227. var trackEls = tech.$$('track');
  23228. var trackObjs = Array.prototype.map.call(trackEls, function (t) {
  23229. return t.track;
  23230. });
  23231. var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
  23232. var json = trackToJson_(trackEl.track);
  23233. if (trackEl.src) {
  23234. json.src = trackEl.src;
  23235. }
  23236. return json;
  23237. });
  23238. return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
  23239. return trackObjs.indexOf(track) === -1;
  23240. }).map(trackToJson_));
  23241. };
  23242. /**
  23243. * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
  23244. * object {@link TextTrack} representations.
  23245. *
  23246. * @param {Array} json
  23247. * An array of `TextTrack` representation objects, like those that would be
  23248. * produced by `textTracksToJson`.
  23249. *
  23250. * @param {Tech} tech
  23251. * The `Tech` to create the `TextTrack`s on.
  23252. */
  23253. var jsonToTextTracks = function jsonToTextTracks(json, tech) {
  23254. json.forEach(function (track) {
  23255. var addedTrack = tech.addRemoteTextTrack(track).track;
  23256. if (!track.src && track.cues) {
  23257. track.cues.forEach(function (cue) {
  23258. return addedTrack.addCue(cue);
  23259. });
  23260. }
  23261. });
  23262. return tech.textTracks();
  23263. };
  23264. exports['default'] = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
  23265. },{}],112:[function(require,module,exports){
  23266. 'use strict';
  23267. exports.__esModule = true;
  23268. var _trackList = require('./track-list');
  23269. var _trackList2 = _interopRequireDefault(_trackList);
  23270. var _fn = require('../utils/fn.js');
  23271. var Fn = _interopRequireWildcard(_fn);
  23272. var _browser = require('../utils/browser.js');
  23273. var browser = _interopRequireWildcard(_browser);
  23274. var _document = require('global/document');
  23275. var _document2 = _interopRequireDefault(_document);
  23276. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  23277. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  23278. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  23279. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  23280. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  23281. * @file text-track-list.js
  23282. */
  23283. /**
  23284. * The current list of {@link TextTrack} for a media file.
  23285. *
  23286. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
  23287. * @extends TrackList
  23288. */
  23289. var TextTrackList = function (_TrackList) {
  23290. _inherits(TextTrackList, _TrackList);
  23291. /**
  23292. * Create an instance of this class.
  23293. *
  23294. * @param {TextTrack[]} [tracks=[]]
  23295. * A list of `TextTrack` to instantiate the list with.
  23296. */
  23297. function TextTrackList() {
  23298. var _this, _ret;
  23299. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  23300. _classCallCheck(this, TextTrackList);
  23301. var list = void 0;
  23302. // IE8 forces us to implement inheritance ourselves
  23303. // as it does not support Object.defineProperty properly
  23304. if (browser.IS_IE8) {
  23305. list = _document2['default'].createElement('custom');
  23306. for (var prop in _trackList2['default'].prototype) {
  23307. if (prop !== 'constructor') {
  23308. list[prop] = _trackList2['default'].prototype[prop];
  23309. }
  23310. }
  23311. for (var _prop in TextTrackList.prototype) {
  23312. if (_prop !== 'constructor') {
  23313. list[_prop] = TextTrackList.prototype[_prop];
  23314. }
  23315. }
  23316. }
  23317. list = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  23318. return _ret = list, _possibleConstructorReturn(_this, _ret);
  23319. }
  23320. /**
  23321. * Add a {@link TextTrack} to the `TextTrackList`
  23322. *
  23323. * @param {TextTrack} track
  23324. * The text track to add to the list.
  23325. *
  23326. * @fires TrackList#addtrack
  23327. * @private
  23328. */
  23329. TextTrackList.prototype.addTrack_ = function addTrack_(track) {
  23330. _TrackList.prototype.addTrack_.call(this, track);
  23331. /**
  23332. * @listens TextTrack#modechange
  23333. * @fires TrackList#change
  23334. */
  23335. track.addEventListener('modechange', Fn.bind(this, function () {
  23336. this.trigger('change');
  23337. }));
  23338. };
  23339. return TextTrackList;
  23340. }(_trackList2['default']);
  23341. exports['default'] = TextTrackList;
  23342. },{"../utils/browser.js":120,"../utils/fn.js":125,"./track-list":116,"global/document":136}],113:[function(require,module,exports){
  23343. 'use strict';
  23344. exports.__esModule = true;
  23345. var _window = require('global/window');
  23346. var _window2 = _interopRequireDefault(_window);
  23347. var _component = require('../component');
  23348. var _component2 = _interopRequireDefault(_component);
  23349. var _dom = require('../utils/dom');
  23350. var _fn = require('../utils/fn');
  23351. var Fn = _interopRequireWildcard(_fn);
  23352. var _obj = require('../utils/obj');
  23353. var Obj = _interopRequireWildcard(_obj);
  23354. var _log = require('../utils/log');
  23355. var _log2 = _interopRequireDefault(_log);
  23356. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  23357. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  23358. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  23359. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  23360. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  23361. * @file text-track-settings.js
  23362. */
  23363. var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
  23364. var COLOR_BLACK = ['#000', 'Black'];
  23365. var COLOR_BLUE = ['#00F', 'Blue'];
  23366. var COLOR_CYAN = ['#0FF', 'Cyan'];
  23367. var COLOR_GREEN = ['#0F0', 'Green'];
  23368. var COLOR_MAGENTA = ['#F0F', 'Magenta'];
  23369. var COLOR_RED = ['#F00', 'Red'];
  23370. var COLOR_WHITE = ['#FFF', 'White'];
  23371. var COLOR_YELLOW = ['#FF0', 'Yellow'];
  23372. var OPACITY_OPAQUE = ['1', 'Opaque'];
  23373. var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
  23374. var OPACITY_TRANS = ['0', 'Transparent'];
  23375. // Configuration for the various <select> elements in the DOM of this component.
  23376. //
  23377. // Possible keys include:
  23378. //
  23379. // `default`:
  23380. // The default option index. Only needs to be provided if not zero.
  23381. // `parser`:
  23382. // A function which is used to parse the value from the selected option in
  23383. // a customized way.
  23384. // `selector`:
  23385. // The selector used to find the associated <select> element.
  23386. var selectConfigs = {
  23387. backgroundColor: {
  23388. selector: '.vjs-bg-color > select',
  23389. id: 'captions-background-color-%s',
  23390. label: 'Color',
  23391. options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
  23392. },
  23393. backgroundOpacity: {
  23394. selector: '.vjs-bg-opacity > select',
  23395. id: 'captions-background-opacity-%s',
  23396. label: 'Transparency',
  23397. options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
  23398. },
  23399. color: {
  23400. selector: '.vjs-fg-color > select',
  23401. id: 'captions-foreground-color-%s',
  23402. label: 'Color',
  23403. options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
  23404. },
  23405. edgeStyle: {
  23406. selector: '.vjs-edge-style > select',
  23407. id: '%s',
  23408. label: 'Text Edge Style',
  23409. options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
  23410. },
  23411. fontFamily: {
  23412. selector: '.vjs-font-family > select',
  23413. id: 'captions-font-family-%s',
  23414. label: 'Font Family',
  23415. options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
  23416. },
  23417. fontPercent: {
  23418. selector: '.vjs-font-percent > select',
  23419. id: 'captions-font-size-%s',
  23420. label: 'Font Size',
  23421. options: [['0.50', '50%'], ['0.75', '75%'], ['1.00', '100%'], ['1.25', '125%'], ['1.50', '150%'], ['1.75', '175%'], ['2.00', '200%'], ['3.00', '300%'], ['4.00', '400%']],
  23422. 'default': 2,
  23423. parser: function parser(v) {
  23424. return v === '1.00' ? null : Number(v);
  23425. }
  23426. },
  23427. textOpacity: {
  23428. selector: '.vjs-text-opacity > select',
  23429. id: 'captions-foreground-opacity-%s',
  23430. label: 'Transparency',
  23431. options: [OPACITY_OPAQUE, OPACITY_SEMI]
  23432. },
  23433. // Options for this object are defined below.
  23434. windowColor: {
  23435. selector: '.vjs-window-color > select',
  23436. id: 'captions-window-color-%s',
  23437. label: 'Color'
  23438. },
  23439. // Options for this object are defined below.
  23440. windowOpacity: {
  23441. selector: '.vjs-window-opacity > select',
  23442. id: 'captions-window-opacity-%s',
  23443. label: 'Transparency',
  23444. options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
  23445. }
  23446. };
  23447. selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
  23448. /**
  23449. * Get the actual value of an option.
  23450. *
  23451. * @param {string} value
  23452. * The value to get
  23453. *
  23454. * @param {Function} [parser]
  23455. * Optional function to adjust the value.
  23456. *
  23457. * @return {Mixed}
  23458. * - Will be `undefined` if no value exists
  23459. * - Will be `undefined` if the given value is "none".
  23460. * - Will be the actual value otherwise.
  23461. *
  23462. * @private
  23463. */
  23464. function parseOptionValue(value, parser) {
  23465. if (parser) {
  23466. value = parser(value);
  23467. }
  23468. if (value && value !== 'none') {
  23469. return value;
  23470. }
  23471. }
  23472. /**
  23473. * Gets the value of the selected <option> element within a <select> element.
  23474. *
  23475. * @param {Element} el
  23476. * the element to look in
  23477. *
  23478. * @param {Function} [parser]
  23479. * Optional function to adjust the value.
  23480. *
  23481. * @return {Mixed}
  23482. * - Will be `undefined` if no value exists
  23483. * - Will be `undefined` if the given value is "none".
  23484. * - Will be the actual value otherwise.
  23485. *
  23486. * @private
  23487. */
  23488. function getSelectedOptionValue(el, parser) {
  23489. var value = el.options[el.options.selectedIndex].value;
  23490. return parseOptionValue(value, parser);
  23491. }
  23492. /**
  23493. * Sets the selected <option> element within a <select> element based on a
  23494. * given value.
  23495. *
  23496. * @param {Element} el
  23497. * The element to look in.
  23498. *
  23499. * @param {string} value
  23500. * the property to look on.
  23501. *
  23502. * @param {Function} [parser]
  23503. * Optional function to adjust the value before comparing.
  23504. *
  23505. * @private
  23506. */
  23507. function setSelectedOption(el, value, parser) {
  23508. if (!value) {
  23509. return;
  23510. }
  23511. for (var i = 0; i < el.options.length; i++) {
  23512. if (parseOptionValue(el.options[i].value, parser) === value) {
  23513. el.selectedIndex = i;
  23514. break;
  23515. }
  23516. }
  23517. }
  23518. /**
  23519. * Manipulate Text Tracks settings.
  23520. *
  23521. * @extends Component
  23522. */
  23523. var TextTrackSettings = function (_Component) {
  23524. _inherits(TextTrackSettings, _Component);
  23525. /**
  23526. * Creates an instance of this class.
  23527. *
  23528. * @param {Player} player
  23529. * The `Player` that this class should be attached to.
  23530. *
  23531. * @param {Object} [options]
  23532. * The key/value store of player options.
  23533. */
  23534. function TextTrackSettings(player, options) {
  23535. _classCallCheck(this, TextTrackSettings);
  23536. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options));
  23537. _this.setDefaults();
  23538. _this.hide();
  23539. _this.updateDisplay = Fn.bind(_this, _this.updateDisplay);
  23540. // Grab `persistTextTrackSettings` from the player options if not passed in child options
  23541. if (options.persistTextTrackSettings === undefined) {
  23542. _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
  23543. }
  23544. _this.on(_this.$('.vjs-done-button'), 'click', function () {
  23545. _this.saveSettings();
  23546. _this.hide();
  23547. });
  23548. _this.on(_this.$('.vjs-default-button'), 'click', function () {
  23549. _this.setDefaults();
  23550. _this.updateDisplay();
  23551. });
  23552. Obj.each(selectConfigs, function (config) {
  23553. _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
  23554. });
  23555. if (_this.options_.persistTextTrackSettings) {
  23556. _this.restoreSettings();
  23557. }
  23558. return _this;
  23559. }
  23560. /**
  23561. * Create a <select> element with configured options.
  23562. *
  23563. * @param {string} key
  23564. * Configuration key to use during creation.
  23565. *
  23566. * @return {Element}
  23567. * The DOM element that gets created.
  23568. * @private
  23569. */
  23570. TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
  23571. var _this2 = this;
  23572. var config = selectConfigs[key];
  23573. var id = config.id.replace('%s', this.id_);
  23574. return [(0, _dom.createEl)('label', {
  23575. className: 'vjs-label',
  23576. textContent: config.label
  23577. }, {
  23578. 'for': id
  23579. }), (0, _dom.createEl)('select', { id: id }, undefined, config.options.map(function (o) {
  23580. return (0, _dom.createEl)('option', {
  23581. textContent: _this2.localize(o[1]),
  23582. value: o[0]
  23583. });
  23584. }))];
  23585. };
  23586. /**
  23587. * Create foreground color element for the component
  23588. *
  23589. * @return {Element}
  23590. * The element that was created.
  23591. *
  23592. * @private
  23593. */
  23594. TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
  23595. var legend = (0, _dom.createEl)('legend', {
  23596. textContent: this.localize('Text')
  23597. });
  23598. var select = this.createElSelect_('color');
  23599. var opacity = (0, _dom.createEl)('span', {
  23600. className: 'vjs-text-opacity vjs-opacity'
  23601. }, undefined, this.createElSelect_('textOpacity'));
  23602. return (0, _dom.createEl)('fieldset', {
  23603. className: 'vjs-fg-color vjs-tracksetting'
  23604. }, undefined, [legend].concat(select, opacity));
  23605. };
  23606. /**
  23607. * Create background color element for the component
  23608. *
  23609. * @return {Element}
  23610. * The element that was created
  23611. *
  23612. * @private
  23613. */
  23614. TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
  23615. var legend = (0, _dom.createEl)('legend', {
  23616. textContent: this.localize('Background')
  23617. });
  23618. var select = this.createElSelect_('backgroundColor');
  23619. var opacity = (0, _dom.createEl)('span', {
  23620. className: 'vjs-bg-opacity vjs-opacity'
  23621. }, undefined, this.createElSelect_('backgroundOpacity'));
  23622. return (0, _dom.createEl)('fieldset', {
  23623. className: 'vjs-bg-color vjs-tracksetting'
  23624. }, undefined, [legend].concat(select, opacity));
  23625. };
  23626. /**
  23627. * Create window color element for the component
  23628. *
  23629. * @return {Element}
  23630. * The element that was created
  23631. *
  23632. * @private
  23633. */
  23634. TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
  23635. var legend = (0, _dom.createEl)('legend', {
  23636. textContent: this.localize('Window')
  23637. });
  23638. var select = this.createElSelect_('windowColor');
  23639. var opacity = (0, _dom.createEl)('span', {
  23640. className: 'vjs-window-opacity vjs-opacity'
  23641. }, undefined, this.createElSelect_('windowOpacity'));
  23642. return (0, _dom.createEl)('fieldset', {
  23643. className: 'vjs-window-color vjs-tracksetting'
  23644. }, undefined, [legend].concat(select, opacity));
  23645. };
  23646. /**
  23647. * Create color elements for the component
  23648. *
  23649. * @return {Element}
  23650. * The element that was created
  23651. *
  23652. * @private
  23653. */
  23654. TextTrackSettings.prototype.createElColors_ = function createElColors_() {
  23655. return (0, _dom.createEl)('div', {
  23656. className: 'vjs-tracksettings-colors'
  23657. }, undefined, [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()]);
  23658. };
  23659. /**
  23660. * Create font elements for the component
  23661. *
  23662. * @return {Element}
  23663. * The element that was created.
  23664. *
  23665. * @private
  23666. */
  23667. TextTrackSettings.prototype.createElFont_ = function createElFont_() {
  23668. var fontPercent = (0, _dom.createEl)('div', {
  23669. className: 'vjs-font-percent vjs-tracksetting'
  23670. }, undefined, this.createElSelect_('fontPercent'));
  23671. var edgeStyle = (0, _dom.createEl)('div', {
  23672. className: 'vjs-edge-style vjs-tracksetting'
  23673. }, undefined, this.createElSelect_('edgeStyle'));
  23674. var fontFamily = (0, _dom.createEl)('div', {
  23675. className: 'vjs-font-family vjs-tracksetting'
  23676. }, undefined, this.createElSelect_('fontFamily'));
  23677. return (0, _dom.createEl)('div', {
  23678. className: 'vjs-tracksettings-font'
  23679. }, undefined, [fontPercent, edgeStyle, fontFamily]);
  23680. };
  23681. /**
  23682. * Create controls for the component
  23683. *
  23684. * @return {Element}
  23685. * The element that was created.
  23686. *
  23687. * @private
  23688. */
  23689. TextTrackSettings.prototype.createElControls_ = function createElControls_() {
  23690. var defaultsButton = (0, _dom.createEl)('button', {
  23691. className: 'vjs-default-button',
  23692. textContent: this.localize('Defaults')
  23693. });
  23694. var doneButton = (0, _dom.createEl)('button', {
  23695. className: 'vjs-done-button',
  23696. textContent: 'Done'
  23697. });
  23698. return (0, _dom.createEl)('div', {
  23699. className: 'vjs-tracksettings-controls'
  23700. }, undefined, [defaultsButton, doneButton]);
  23701. };
  23702. /**
  23703. * Create the component's DOM element
  23704. *
  23705. * @return {Element}
  23706. * The element that was created.
  23707. */
  23708. TextTrackSettings.prototype.createEl = function createEl() {
  23709. var settings = (0, _dom.createEl)('div', {
  23710. className: 'vjs-tracksettings'
  23711. }, undefined, [this.createElColors_(), this.createElFont_(), this.createElControls_()]);
  23712. var heading = (0, _dom.createEl)('div', {
  23713. className: 'vjs-control-text',
  23714. id: 'TTsettingsDialogLabel-' + this.id_,
  23715. textContent: 'Caption Settings Dialog'
  23716. }, {
  23717. 'aria-level': '1',
  23718. 'role': 'heading'
  23719. });
  23720. var description = (0, _dom.createEl)('div', {
  23721. className: 'vjs-control-text',
  23722. id: 'TTsettingsDialogDescription-' + this.id_,
  23723. textContent: 'Beginning of dialog window. Escape will cancel and close the window.'
  23724. });
  23725. var doc = (0, _dom.createEl)('div', undefined, {
  23726. role: 'document'
  23727. }, [heading, description, settings]);
  23728. return (0, _dom.createEl)('div', {
  23729. className: 'vjs-caption-settings vjs-modal-overlay',
  23730. tabIndex: -1
  23731. }, {
  23732. 'role': 'dialog',
  23733. 'aria-labelledby': heading.id,
  23734. 'aria-describedby': description.id
  23735. }, doc);
  23736. };
  23737. /**
  23738. * Gets an object of text track settings (or null).
  23739. *
  23740. * @return {Object}
  23741. * An object with config values parsed from the DOM or localStorage.
  23742. */
  23743. TextTrackSettings.prototype.getValues = function getValues() {
  23744. var _this3 = this;
  23745. return Obj.reduce(selectConfigs, function (accum, config, key) {
  23746. var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
  23747. if (value !== undefined) {
  23748. accum[key] = value;
  23749. }
  23750. return accum;
  23751. }, {});
  23752. };
  23753. /**
  23754. * Sets text track settings from an object of values.
  23755. *
  23756. * @param {Object} values
  23757. * An object with config values parsed from the DOM or localStorage.
  23758. */
  23759. TextTrackSettings.prototype.setValues = function setValues(values) {
  23760. var _this4 = this;
  23761. Obj.each(selectConfigs, function (config, key) {
  23762. setSelectedOption(_this4.$(config.selector), values[key], config.parser);
  23763. });
  23764. };
  23765. /**
  23766. * Sets all <select> elements to their default values.
  23767. */
  23768. TextTrackSettings.prototype.setDefaults = function setDefaults() {
  23769. var _this5 = this;
  23770. Obj.each(selectConfigs, function (config) {
  23771. var index = config.hasOwnProperty('default') ? config['default'] : 0;
  23772. _this5.$(config.selector).selectedIndex = index;
  23773. });
  23774. };
  23775. /**
  23776. * Restore texttrack settings from localStorage
  23777. */
  23778. TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
  23779. var values = void 0;
  23780. try {
  23781. values = JSON.parse(_window2['default'].localStorage.getItem(LOCAL_STORAGE_KEY));
  23782. } catch (err) {
  23783. _log2['default'].warn(err);
  23784. }
  23785. if (values) {
  23786. this.setValues(values);
  23787. }
  23788. };
  23789. /**
  23790. * Save text track settings to localStorage
  23791. */
  23792. TextTrackSettings.prototype.saveSettings = function saveSettings() {
  23793. if (!this.options_.persistTextTrackSettings) {
  23794. return;
  23795. }
  23796. var values = this.getValues();
  23797. try {
  23798. if (Object.keys(values).length) {
  23799. _window2['default'].localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
  23800. } else {
  23801. _window2['default'].localStorage.removeItem(LOCAL_STORAGE_KEY);
  23802. }
  23803. } catch (err) {
  23804. _log2['default'].warn(err);
  23805. }
  23806. };
  23807. /**
  23808. * Update display of text track settings
  23809. */
  23810. TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
  23811. var ttDisplay = this.player_.getChild('textTrackDisplay');
  23812. if (ttDisplay) {
  23813. ttDisplay.updateDisplay();
  23814. }
  23815. };
  23816. return TextTrackSettings;
  23817. }(_component2['default']);
  23818. _component2['default'].registerComponent('TextTrackSettings', TextTrackSettings);
  23819. exports['default'] = TextTrackSettings;
  23820. },{"../component":47,"../utils/dom":123,"../utils/fn":125,"../utils/log":128,"../utils/obj":130,"global/window":137}],114:[function(require,module,exports){
  23821. 'use strict';
  23822. exports.__esModule = true;
  23823. var _textTrackCueList = require('./text-track-cue-list');
  23824. var _textTrackCueList2 = _interopRequireDefault(_textTrackCueList);
  23825. var _fn = require('../utils/fn.js');
  23826. var Fn = _interopRequireWildcard(_fn);
  23827. var _trackEnums = require('./track-enums');
  23828. var _log = require('../utils/log.js');
  23829. var _log2 = _interopRequireDefault(_log);
  23830. var _window = require('global/window');
  23831. var _window2 = _interopRequireDefault(_window);
  23832. var _track = require('./track.js');
  23833. var _track2 = _interopRequireDefault(_track);
  23834. var _url = require('../utils/url.js');
  23835. var _xhr = require('xhr');
  23836. var _xhr2 = _interopRequireDefault(_xhr);
  23837. var _mergeOptions = require('../utils/merge-options');
  23838. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  23839. var _browser = require('../utils/browser.js');
  23840. var browser = _interopRequireWildcard(_browser);
  23841. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  23842. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  23843. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  23844. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  23845. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  23846. * @file text-track.js
  23847. */
  23848. /**
  23849. * Takes a webvtt file contents and parses it into cues
  23850. *
  23851. * @param {string} srcContent
  23852. * webVTT file contents
  23853. *
  23854. * @param {TextTrack} track
  23855. * TextTrack to add cues to. Cues come from the srcContent.
  23856. *
  23857. * @private
  23858. */
  23859. var parseCues = function parseCues(srcContent, track) {
  23860. var parser = new _window2['default'].WebVTT.Parser(_window2['default'], _window2['default'].vttjs, _window2['default'].WebVTT.StringDecoder());
  23861. var errors = [];
  23862. parser.oncue = function (cue) {
  23863. track.addCue(cue);
  23864. };
  23865. parser.onparsingerror = function (error) {
  23866. errors.push(error);
  23867. };
  23868. parser.onflush = function () {
  23869. track.trigger({
  23870. type: 'loadeddata',
  23871. target: track
  23872. });
  23873. };
  23874. parser.parse(srcContent);
  23875. if (errors.length > 0) {
  23876. if (_window2['default'].console && _window2['default'].console.groupCollapsed) {
  23877. _window2['default'].console.groupCollapsed('Text Track parsing errors for ' + track.src);
  23878. }
  23879. errors.forEach(function (error) {
  23880. return _log2['default'].error(error);
  23881. });
  23882. if (_window2['default'].console && _window2['default'].console.groupEnd) {
  23883. _window2['default'].console.groupEnd();
  23884. }
  23885. }
  23886. parser.flush();
  23887. };
  23888. /**
  23889. * Load a `TextTrack` from a specifed url.
  23890. *
  23891. * @param {string} src
  23892. * Url to load track from.
  23893. *
  23894. * @param {TextTrack} track
  23895. * Track to add cues to. Comes from the content at the end of `url`.
  23896. *
  23897. * @private
  23898. */
  23899. var loadTrack = function loadTrack(src, track) {
  23900. var opts = {
  23901. uri: src
  23902. };
  23903. var crossOrigin = (0, _url.isCrossOrigin)(src);
  23904. if (crossOrigin) {
  23905. opts.cors = crossOrigin;
  23906. }
  23907. (0, _xhr2['default'])(opts, Fn.bind(this, function (err, response, responseBody) {
  23908. if (err) {
  23909. return _log2['default'].error(err, response);
  23910. }
  23911. track.loaded_ = true;
  23912. // Make sure that vttjs has loaded, otherwise, wait till it finished loading
  23913. // NOTE: this is only used for the alt/video.novtt.js build
  23914. if (typeof _window2['default'].WebVTT !== 'function') {
  23915. if (track.tech_) {
  23916. var loadHandler = function loadHandler() {
  23917. return parseCues(responseBody, track);
  23918. };
  23919. track.tech_.on('vttjsloaded', loadHandler);
  23920. track.tech_.on('vttjserror', function () {
  23921. _log2['default'].error('vttjs failed to load, stopping trying to process ' + track.src);
  23922. track.tech_.off('vttjsloaded', loadHandler);
  23923. });
  23924. }
  23925. } else {
  23926. parseCues(responseBody, track);
  23927. }
  23928. }));
  23929. };
  23930. /**
  23931. * A representation of a single `TextTrack`.
  23932. *
  23933. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
  23934. * @extends Track
  23935. */
  23936. var TextTrack = function (_Track) {
  23937. _inherits(TextTrack, _Track);
  23938. /**
  23939. * Create an instance of this class.
  23940. *
  23941. * @param {Object} options={}
  23942. * Object of option names and values
  23943. *
  23944. * @param {Tech} options.tech
  23945. * A reference to the tech that owns this TextTrack.
  23946. *
  23947. * @param {TextTrack~Kind} [options.kind='subtitles']
  23948. * A valid text track kind.
  23949. *
  23950. * @param {TextTrack~Mode} [options.mode='disabled']
  23951. * A valid text track mode.
  23952. *
  23953. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  23954. * A unique id for this TextTrack.
  23955. *
  23956. * @param {string} [options.label='']
  23957. * The menu label for this track.
  23958. *
  23959. * @param {string} [options.language='']
  23960. * A valid two character language code.
  23961. *
  23962. * @param {string} [options.srclang='']
  23963. * A valid two character language code. An alternative, but deprioritized
  23964. * vesion of `options.language`
  23965. *
  23966. * @param {string} [options.src]
  23967. * A url to TextTrack cues.
  23968. *
  23969. * @param {boolean} [options.default]
  23970. * If this track should default to on or off.
  23971. */
  23972. function TextTrack() {
  23973. var _this, _ret;
  23974. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  23975. _classCallCheck(this, TextTrack);
  23976. if (!options.tech) {
  23977. throw new Error('A tech was not provided.');
  23978. }
  23979. var settings = (0, _mergeOptions2['default'])(options, {
  23980. kind: _trackEnums.TextTrackKind[options.kind] || 'subtitles',
  23981. language: options.language || options.srclang || ''
  23982. });
  23983. var mode = _trackEnums.TextTrackMode[settings.mode] || 'disabled';
  23984. var default_ = settings['default'];
  23985. if (settings.kind === 'metadata' || settings.kind === 'chapters') {
  23986. mode = 'hidden';
  23987. }
  23988. // on IE8 this will be a document element
  23989. // for every other browser this will be a normal object
  23990. var tt = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  23991. tt.tech_ = settings.tech;
  23992. if (browser.IS_IE8) {
  23993. for (var prop in TextTrack.prototype) {
  23994. if (prop !== 'constructor') {
  23995. tt[prop] = TextTrack.prototype[prop];
  23996. }
  23997. }
  23998. }
  23999. tt.cues_ = [];
  24000. tt.activeCues_ = [];
  24001. var cues = new _textTrackCueList2['default'](tt.cues_);
  24002. var activeCues = new _textTrackCueList2['default'](tt.activeCues_);
  24003. var changed = false;
  24004. var timeupdateHandler = Fn.bind(tt, function () {
  24005. // Accessing this.activeCues for the side-effects of updating itself
  24006. // due to it's nature as a getter function. Do not remove or cues will
  24007. // stop updating!
  24008. /* eslint-disable no-unused-expressions */
  24009. this.activeCues;
  24010. /* eslint-enable no-unused-expressions */
  24011. if (changed) {
  24012. this.trigger('cuechange');
  24013. changed = false;
  24014. }
  24015. });
  24016. if (mode !== 'disabled') {
  24017. tt.tech_.ready(function () {
  24018. tt.tech_.on('timeupdate', timeupdateHandler);
  24019. }, true);
  24020. }
  24021. /**
  24022. * @member {boolean} default
  24023. * If this track was set to be on or off by default. Cannot be changed after
  24024. * creation.
  24025. *
  24026. * @readonly
  24027. */
  24028. Object.defineProperty(tt, 'default', {
  24029. get: function get() {
  24030. return default_;
  24031. },
  24032. set: function set() {}
  24033. });
  24034. /**
  24035. * @member {string} mode
  24036. * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
  24037. * not be set if setting to an invalid mode.
  24038. *
  24039. * @fires TextTrack#modechange
  24040. */
  24041. Object.defineProperty(tt, 'mode', {
  24042. get: function get() {
  24043. return mode;
  24044. },
  24045. set: function set(newMode) {
  24046. var _this2 = this;
  24047. if (!_trackEnums.TextTrackMode[newMode]) {
  24048. return;
  24049. }
  24050. mode = newMode;
  24051. if (mode === 'showing') {
  24052. this.tech_.ready(function () {
  24053. _this2.tech_.on('timeupdate', timeupdateHandler);
  24054. }, true);
  24055. }
  24056. /**
  24057. * An event that fires when mode changes on this track. This allows
  24058. * the TextTrackList that holds this track to act accordingly.
  24059. *
  24060. * > Note: This is not part of the spec!
  24061. *
  24062. * @event TextTrack#modechange
  24063. * @type {EventTarget~Event}
  24064. */
  24065. this.trigger('modechange');
  24066. }
  24067. });
  24068. /**
  24069. * @member {TextTrackCueList} cues
  24070. * The text track cue list for this TextTrack.
  24071. */
  24072. Object.defineProperty(tt, 'cues', {
  24073. get: function get() {
  24074. if (!this.loaded_) {
  24075. return null;
  24076. }
  24077. return cues;
  24078. },
  24079. set: function set() {}
  24080. });
  24081. /**
  24082. * @member {TextTrackCueList} activeCues
  24083. * The list text track cues that are currently active for this TextTrack.
  24084. */
  24085. Object.defineProperty(tt, 'activeCues', {
  24086. get: function get() {
  24087. if (!this.loaded_) {
  24088. return null;
  24089. }
  24090. // nothing to do
  24091. if (this.cues.length === 0) {
  24092. return activeCues;
  24093. }
  24094. var ct = this.tech_.currentTime();
  24095. var active = [];
  24096. for (var i = 0, l = this.cues.length; i < l; i++) {
  24097. var cue = this.cues[i];
  24098. if (cue.startTime <= ct && cue.endTime >= ct) {
  24099. active.push(cue);
  24100. } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
  24101. active.push(cue);
  24102. }
  24103. }
  24104. changed = false;
  24105. if (active.length !== this.activeCues_.length) {
  24106. changed = true;
  24107. } else {
  24108. for (var _i = 0; _i < active.length; _i++) {
  24109. if (this.activeCues_.indexOf(active[_i]) === -1) {
  24110. changed = true;
  24111. }
  24112. }
  24113. }
  24114. this.activeCues_ = active;
  24115. activeCues.setCues_(this.activeCues_);
  24116. return activeCues;
  24117. },
  24118. set: function set() {}
  24119. });
  24120. if (settings.src) {
  24121. tt.src = settings.src;
  24122. loadTrack(settings.src, tt);
  24123. } else {
  24124. tt.loaded_ = true;
  24125. }
  24126. return _ret = tt, _possibleConstructorReturn(_this, _ret);
  24127. }
  24128. /**
  24129. * Add a cue to the internal list of cues.
  24130. *
  24131. * @param {TextTrack~Cue} cue
  24132. * The cue to add to our internal list
  24133. */
  24134. TextTrack.prototype.addCue = function addCue(originalCue) {
  24135. var cue = originalCue;
  24136. if (_window2['default'].vttjs && !(originalCue instanceof _window2['default'].vttjs.VTTCue)) {
  24137. cue = new _window2['default'].vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
  24138. for (var prop in originalCue) {
  24139. if (!(prop in cue)) {
  24140. cue[prop] = originalCue[prop];
  24141. }
  24142. }
  24143. // make sure that `id` is copied over
  24144. cue.id = originalCue.id;
  24145. cue.originalCue_ = originalCue;
  24146. }
  24147. var tracks = this.tech_.textTracks();
  24148. if (tracks) {
  24149. for (var i = 0; i < tracks.length; i++) {
  24150. if (tracks[i] !== this) {
  24151. tracks[i].removeCue(cue);
  24152. }
  24153. }
  24154. }
  24155. this.cues_.push(cue);
  24156. this.cues.setCues_(this.cues_);
  24157. };
  24158. /**
  24159. * Remove a cue from our internal list
  24160. *
  24161. * @param {TextTrack~Cue} removeCue
  24162. * The cue to remove from our internal list
  24163. */
  24164. TextTrack.prototype.removeCue = function removeCue(_removeCue) {
  24165. var i = this.cues_.length;
  24166. while (i--) {
  24167. var cue = this.cues_[i];
  24168. if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
  24169. this.cues_.splice(i, 1);
  24170. this.cues.setCues_(this.cues_);
  24171. break;
  24172. }
  24173. }
  24174. };
  24175. return TextTrack;
  24176. }(_track2['default']);
  24177. /**
  24178. * cuechange - One or more cues in the track have become active or stopped being active.
  24179. */
  24180. TextTrack.prototype.allowedEvents_ = {
  24181. cuechange: 'cuechange'
  24182. };
  24183. exports['default'] = TextTrack;
  24184. },{"../utils/browser.js":120,"../utils/fn.js":125,"../utils/log.js":128,"../utils/merge-options":129,"../utils/url.js":134,"./text-track-cue-list":109,"./track-enums":115,"./track.js":117,"global/window":137,"xhr":143}],115:[function(require,module,exports){
  24185. 'use strict';
  24186. exports.__esModule = true;
  24187. /**
  24188. * @file track-kinds.js
  24189. */
  24190. /**
  24191. * All possible `VideoTrackKind`s
  24192. *
  24193. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
  24194. * @typedef VideoTrack~Kind
  24195. * @enum
  24196. */
  24197. var VideoTrackKind = exports.VideoTrackKind = {
  24198. alternative: 'alternative',
  24199. captions: 'captions',
  24200. main: 'main',
  24201. sign: 'sign',
  24202. subtitles: 'subtitles',
  24203. commentary: 'commentary'
  24204. };
  24205. /**
  24206. * All possible `AudioTrackKind`s
  24207. *
  24208. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
  24209. * @typedef AudioTrack~Kind
  24210. * @enum
  24211. */
  24212. var AudioTrackKind = exports.AudioTrackKind = {
  24213. 'alternative': 'alternative',
  24214. 'descriptions': 'descriptions',
  24215. 'main': 'main',
  24216. 'main-desc': 'main-desc',
  24217. 'translation': 'translation',
  24218. 'commentary': 'commentary'
  24219. };
  24220. /**
  24221. * All possible `TextTrackKind`s
  24222. *
  24223. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
  24224. * @typedef TextTrack~Kind
  24225. * @enum
  24226. */
  24227. var TextTrackKind = exports.TextTrackKind = {
  24228. subtitles: 'subtitles',
  24229. captions: 'captions',
  24230. descriptions: 'descriptions',
  24231. chapters: 'chapters',
  24232. metadata: 'metadata'
  24233. };
  24234. /**
  24235. * All possible `TextTrackMode`s
  24236. *
  24237. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
  24238. * @typedef TextTrack~Mode
  24239. * @enum
  24240. */
  24241. var TextTrackMode = exports.TextTrackMode = {
  24242. disabled: 'disabled',
  24243. hidden: 'hidden',
  24244. showing: 'showing'
  24245. };
  24246. },{}],116:[function(require,module,exports){
  24247. 'use strict';
  24248. exports.__esModule = true;
  24249. var _eventTarget = require('../event-target');
  24250. var _eventTarget2 = _interopRequireDefault(_eventTarget);
  24251. var _browser = require('../utils/browser.js');
  24252. var browser = _interopRequireWildcard(_browser);
  24253. var _document = require('global/document');
  24254. var _document2 = _interopRequireDefault(_document);
  24255. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  24256. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  24257. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  24258. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  24259. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  24260. * @file track-list.js
  24261. */
  24262. /**
  24263. * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
  24264. * {@link VideoTrackList}
  24265. *
  24266. * @extends EventTarget
  24267. */
  24268. var TrackList = function (_EventTarget) {
  24269. _inherits(TrackList, _EventTarget);
  24270. /**
  24271. * Create an instance of this class
  24272. *
  24273. * @param {Track[]} tracks
  24274. * A list of tracks to initialize the list with.
  24275. *
  24276. * @param {Object} [list]
  24277. * The child object with inheritance done manually for ie8.
  24278. *
  24279. * @abstract
  24280. */
  24281. function TrackList() {
  24282. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  24283. var _ret;
  24284. var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  24285. _classCallCheck(this, TrackList);
  24286. var _this = _possibleConstructorReturn(this, _EventTarget.call(this));
  24287. if (!list) {
  24288. list = _this; // eslint-disable-line
  24289. if (browser.IS_IE8) {
  24290. list = _document2['default'].createElement('custom');
  24291. for (var prop in TrackList.prototype) {
  24292. if (prop !== 'constructor') {
  24293. list[prop] = TrackList.prototype[prop];
  24294. }
  24295. }
  24296. }
  24297. }
  24298. list.tracks_ = [];
  24299. /**
  24300. * @member {number} length
  24301. * The current number of `Track`s in the this Trackist.
  24302. */
  24303. Object.defineProperty(list, 'length', {
  24304. get: function get() {
  24305. return this.tracks_.length;
  24306. }
  24307. });
  24308. for (var i = 0; i < tracks.length; i++) {
  24309. list.addTrack_(tracks[i]);
  24310. }
  24311. // must return the object, as for ie8 it will not be this
  24312. // but a reference to a document object
  24313. return _ret = list, _possibleConstructorReturn(_this, _ret);
  24314. }
  24315. /**
  24316. * Add a {@link Track} to the `TrackList`
  24317. *
  24318. * @param {Track} track
  24319. * The audio, video, or text track to add to the list.
  24320. *
  24321. * @fires TrackList#addtrack
  24322. * @private
  24323. */
  24324. TrackList.prototype.addTrack_ = function addTrack_(track) {
  24325. var index = this.tracks_.length;
  24326. if (!('' + index in this)) {
  24327. Object.defineProperty(this, index, {
  24328. get: function get() {
  24329. return this.tracks_[index];
  24330. }
  24331. });
  24332. }
  24333. // Do not add duplicate tracks
  24334. if (this.tracks_.indexOf(track) === -1) {
  24335. this.tracks_.push(track);
  24336. /**
  24337. * Triggered when a track is added to a track list.
  24338. *
  24339. * @event TrackList#addtrack
  24340. * @type {EventTarget~Event}
  24341. * @property {Track} track
  24342. * A reference to track that was added.
  24343. */
  24344. this.trigger({
  24345. track: track,
  24346. type: 'addtrack'
  24347. });
  24348. }
  24349. };
  24350. /**
  24351. * Remove a {@link Track} from the `TrackList`
  24352. *
  24353. * @param {Track} track
  24354. * The audio, video, or text track to remove from the list.
  24355. *
  24356. * @fires TrackList#removetrack
  24357. * @private
  24358. */
  24359. TrackList.prototype.removeTrack_ = function removeTrack_(rtrack) {
  24360. var track = void 0;
  24361. for (var i = 0, l = this.length; i < l; i++) {
  24362. if (this[i] === rtrack) {
  24363. track = this[i];
  24364. if (track.off) {
  24365. track.off();
  24366. }
  24367. this.tracks_.splice(i, 1);
  24368. break;
  24369. }
  24370. }
  24371. if (!track) {
  24372. return;
  24373. }
  24374. /**
  24375. * Triggered when a track is removed from track list.
  24376. *
  24377. * @event TrackList#removetrack
  24378. * @type {EventTarget~Event}
  24379. * @property {Track} track
  24380. * A reference to track that was removed.
  24381. */
  24382. this.trigger({
  24383. track: track,
  24384. type: 'removetrack'
  24385. });
  24386. };
  24387. /**
  24388. * Get a Track from the TrackList by a tracks id
  24389. *
  24390. * @param {String} id - the id of the track to get
  24391. * @method getTrackById
  24392. * @return {Track}
  24393. * @private
  24394. */
  24395. TrackList.prototype.getTrackById = function getTrackById(id) {
  24396. var result = null;
  24397. for (var i = 0, l = this.length; i < l; i++) {
  24398. var track = this[i];
  24399. if (track.id === id) {
  24400. result = track;
  24401. break;
  24402. }
  24403. }
  24404. return result;
  24405. };
  24406. return TrackList;
  24407. }(_eventTarget2['default']);
  24408. /**
  24409. * Triggered when a different track is selected/enabled.
  24410. *
  24411. * @event TrackList#change
  24412. * @type {EventTarget~Event}
  24413. */
  24414. /**
  24415. * Events that can be called with on + eventName. See {@link EventHandler}.
  24416. *
  24417. * @property {Object} TrackList#allowedEvents_
  24418. * @private
  24419. */
  24420. TrackList.prototype.allowedEvents_ = {
  24421. change: 'change',
  24422. addtrack: 'addtrack',
  24423. removetrack: 'removetrack'
  24424. };
  24425. // emulate attribute EventHandler support to allow for feature detection
  24426. for (var event in TrackList.prototype.allowedEvents_) {
  24427. TrackList.prototype['on' + event] = null;
  24428. }
  24429. exports['default'] = TrackList;
  24430. },{"../event-target":84,"../utils/browser.js":120,"global/document":136}],117:[function(require,module,exports){
  24431. 'use strict';
  24432. exports.__esModule = true;
  24433. var _browser = require('../utils/browser.js');
  24434. var browser = _interopRequireWildcard(_browser);
  24435. var _document = require('global/document');
  24436. var _document2 = _interopRequireDefault(_document);
  24437. var _guid = require('../utils/guid.js');
  24438. var Guid = _interopRequireWildcard(_guid);
  24439. var _eventTarget = require('../event-target');
  24440. var _eventTarget2 = _interopRequireDefault(_eventTarget);
  24441. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  24442. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  24443. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  24444. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  24445. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  24446. * @file track.js
  24447. */
  24448. /**
  24449. * A Track class that contains all of the common functionality for {@link AudioTrack},
  24450. * {@link VideoTrack}, and {@link TextTrack}.
  24451. *
  24452. * > Note: This class should not be used directly
  24453. *
  24454. * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
  24455. * @extends EventTarget
  24456. * @abstract
  24457. */
  24458. var Track = function (_EventTarget) {
  24459. _inherits(Track, _EventTarget);
  24460. /**
  24461. * Create an instance of this class.
  24462. *
  24463. * @param {Object} [options={}]
  24464. * Object of option names and values
  24465. *
  24466. * @param {string} [options.kind='']
  24467. * A valid kind for the track type you are creating.
  24468. *
  24469. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  24470. * A unique id for this AudioTrack.
  24471. *
  24472. * @param {string} [options.label='']
  24473. * The menu label for this track.
  24474. *
  24475. * @param {string} [options.language='']
  24476. * A valid two character language code.
  24477. *
  24478. * @abstract
  24479. */
  24480. function Track() {
  24481. var _ret;
  24482. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  24483. _classCallCheck(this, Track);
  24484. var _this = _possibleConstructorReturn(this, _EventTarget.call(this));
  24485. var track = _this; // eslint-disable-line
  24486. if (browser.IS_IE8) {
  24487. track = _document2['default'].createElement('custom');
  24488. for (var prop in Track.prototype) {
  24489. if (prop !== 'constructor') {
  24490. track[prop] = Track.prototype[prop];
  24491. }
  24492. }
  24493. }
  24494. var trackProps = {
  24495. id: options.id || 'vjs_track_' + Guid.newGUID(),
  24496. kind: options.kind || '',
  24497. label: options.label || '',
  24498. language: options.language || ''
  24499. };
  24500. /**
  24501. * @member {string} id
  24502. * The id of this track. Cannot be changed after creation.
  24503. *
  24504. * @readonly
  24505. */
  24506. /**
  24507. * @member {string} kind
  24508. * The kind of track that this is. Cannot be changed after creation.
  24509. *
  24510. * @readonly
  24511. */
  24512. /**
  24513. * @member {string} label
  24514. * The label of this track. Cannot be changed after creation.
  24515. *
  24516. * @readonly
  24517. */
  24518. /**
  24519. * @member {string} language
  24520. * The two letter language code for this track. Cannot be changed after
  24521. * creation.
  24522. *
  24523. * @readonly
  24524. */
  24525. var _loop = function _loop(key) {
  24526. Object.defineProperty(track, key, {
  24527. get: function get() {
  24528. return trackProps[key];
  24529. },
  24530. set: function set() {}
  24531. });
  24532. };
  24533. for (var key in trackProps) {
  24534. _loop(key);
  24535. }
  24536. return _ret = track, _possibleConstructorReturn(_this, _ret);
  24537. }
  24538. return Track;
  24539. }(_eventTarget2['default']);
  24540. exports['default'] = Track;
  24541. },{"../event-target":84,"../utils/browser.js":120,"../utils/guid.js":127,"global/document":136}],118:[function(require,module,exports){
  24542. 'use strict';
  24543. exports.__esModule = true;
  24544. var _trackList = require('./track-list');
  24545. var _trackList2 = _interopRequireDefault(_trackList);
  24546. var _browser = require('../utils/browser.js');
  24547. var browser = _interopRequireWildcard(_browser);
  24548. var _document = require('global/document');
  24549. var _document2 = _interopRequireDefault(_document);
  24550. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  24551. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  24552. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  24553. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  24554. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
  24555. * @file video-track-list.js
  24556. */
  24557. /**
  24558. * Un-select all other {@link VideoTrack}s that are selected.
  24559. *
  24560. * @param {VideoTrackList} list
  24561. * list to work on
  24562. *
  24563. * @param {VideoTrack} track
  24564. * The track to skip
  24565. *
  24566. * @private
  24567. */
  24568. var disableOthers = function disableOthers(list, track) {
  24569. for (var i = 0; i < list.length; i++) {
  24570. if (track.id === list[i].id) {
  24571. continue;
  24572. }
  24573. // another video track is enabled, disable it
  24574. list[i].selected = false;
  24575. }
  24576. };
  24577. /**
  24578. * The current list of {@link VideoTrack} for a video.
  24579. *
  24580. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
  24581. * @extends TrackList
  24582. */
  24583. var VideoTrackList = function (_TrackList) {
  24584. _inherits(VideoTrackList, _TrackList);
  24585. /**
  24586. * Create an instance of this class.
  24587. *
  24588. * @param {VideoTrack[]} [tracks=[]]
  24589. * A list of `VideoTrack` to instantiate the list with.
  24590. */
  24591. function VideoTrackList() {
  24592. var _this, _ret;
  24593. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  24594. _classCallCheck(this, VideoTrackList);
  24595. var list = void 0;
  24596. // make sure only 1 track is enabled
  24597. // sorted from last index to first index
  24598. for (var i = tracks.length - 1; i >= 0; i--) {
  24599. if (tracks[i].selected) {
  24600. disableOthers(tracks, tracks[i]);
  24601. break;
  24602. }
  24603. }
  24604. // IE8 forces us to implement inheritance ourselves
  24605. // as it does not support Object.defineProperty properly
  24606. if (browser.IS_IE8) {
  24607. list = _document2['default'].createElement('custom');
  24608. for (var prop in _trackList2['default'].prototype) {
  24609. if (prop !== 'constructor') {
  24610. list[prop] = _trackList2['default'].prototype[prop];
  24611. }
  24612. }
  24613. for (var _prop in VideoTrackList.prototype) {
  24614. if (_prop !== 'constructor') {
  24615. list[_prop] = VideoTrackList.prototype[_prop];
  24616. }
  24617. }
  24618. }
  24619. list = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  24620. list.changing_ = false;
  24621. /**
  24622. * @member {number} VideoTrackList#selectedIndex
  24623. * The current index of the selected {@link VideoTrack`}.
  24624. */
  24625. Object.defineProperty(list, 'selectedIndex', {
  24626. get: function get() {
  24627. for (var _i = 0; _i < this.length; _i++) {
  24628. if (this[_i].selected) {
  24629. return _i;
  24630. }
  24631. }
  24632. return -1;
  24633. },
  24634. set: function set() {}
  24635. });
  24636. return _ret = list, _possibleConstructorReturn(_this, _ret);
  24637. }
  24638. /**
  24639. * Add a {@link VideoTrack} to the `VideoTrackList`.
  24640. *
  24641. * @param {VideoTrack} track
  24642. * The VideoTrack to add to the list
  24643. *
  24644. * @fires TrackList#addtrack
  24645. * @private
  24646. */
  24647. VideoTrackList.prototype.addTrack_ = function addTrack_(track) {
  24648. var _this2 = this;
  24649. if (track.selected) {
  24650. disableOthers(this, track);
  24651. }
  24652. _TrackList.prototype.addTrack_.call(this, track);
  24653. // native tracks don't have this
  24654. if (!track.addEventListener) {
  24655. return;
  24656. }
  24657. /**
  24658. * @listens VideoTrack#selectedchange
  24659. * @fires TrackList#change
  24660. */
  24661. track.addEventListener('selectedchange', function () {
  24662. if (_this2.changing_) {
  24663. return;
  24664. }
  24665. _this2.changing_ = true;
  24666. disableOthers(_this2, track);
  24667. _this2.changing_ = false;
  24668. _this2.trigger('change');
  24669. });
  24670. };
  24671. /**
  24672. * Add a {@link VideoTrack} to the `VideoTrackList`.
  24673. *
  24674. * @param {VideoTrack} track
  24675. * The VideoTrack to add to the list
  24676. *
  24677. * @fires TrackList#addtrack
  24678. */
  24679. VideoTrackList.prototype.addTrack = function addTrack(track) {
  24680. this.addTrack_(track);
  24681. };
  24682. /**
  24683. * Remove a {@link VideoTrack} to the `VideoTrackList`.
  24684. *
  24685. * @param {VideoTrack} track
  24686. * The VideoTrack to remove from the list.
  24687. *
  24688. * @fires TrackList#removetrack
  24689. */
  24690. VideoTrackList.prototype.removeTrack = function removeTrack(track) {
  24691. _TrackList.prototype.removeTrack_.call(this, track);
  24692. };
  24693. return VideoTrackList;
  24694. }(_trackList2['default']);
  24695. exports['default'] = VideoTrackList;
  24696. },{"../utils/browser.js":120,"./track-list":116,"global/document":136}],119:[function(require,module,exports){
  24697. 'use strict';
  24698. exports.__esModule = true;
  24699. var _trackEnums = require('./track-enums');
  24700. var _track = require('./track');
  24701. var _track2 = _interopRequireDefault(_track);
  24702. var _mergeOptions = require('../utils/merge-options');
  24703. var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
  24704. var _browser = require('../utils/browser.js');
  24705. var browser = _interopRequireWildcard(_browser);
  24706. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  24707. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  24708. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  24709. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  24710. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  24711. /**
  24712. * A representation of a single `VideoTrack`.
  24713. *
  24714. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
  24715. * @extends Track
  24716. */
  24717. var VideoTrack = function (_Track) {
  24718. _inherits(VideoTrack, _Track);
  24719. /**
  24720. * Create an instance of this class.
  24721. *
  24722. * @param {Object} [options={}]
  24723. * Object of option names and values
  24724. *
  24725. * @param {string} [options.kind='']
  24726. * A valid {@link VideoTrack~Kind}
  24727. *
  24728. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  24729. * A unique id for this AudioTrack.
  24730. *
  24731. * @param {string} [options.label='']
  24732. * The menu label for this track.
  24733. *
  24734. * @param {string} [options.language='']
  24735. * A valid two character language code.
  24736. *
  24737. * @param {boolean} [options.selected]
  24738. * If this track is the one that is currently playing.
  24739. */
  24740. function VideoTrack() {
  24741. var _this, _ret;
  24742. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  24743. _classCallCheck(this, VideoTrack);
  24744. var settings = (0, _mergeOptions2['default'])(options, {
  24745. kind: _trackEnums.VideoTrackKind[options.kind] || ''
  24746. });
  24747. // on IE8 this will be a document element
  24748. // for every other browser this will be a normal object
  24749. var track = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  24750. var selected = false;
  24751. if (browser.IS_IE8) {
  24752. for (var prop in VideoTrack.prototype) {
  24753. if (prop !== 'constructor') {
  24754. track[prop] = VideoTrack.prototype[prop];
  24755. }
  24756. }
  24757. }
  24758. /**
  24759. * @member {boolean} selected
  24760. * If this `VideoTrack` is selected or not. When setting this will
  24761. * fire {@link VideoTrack#selectedchange} if the state of selected changed.
  24762. *
  24763. * @fires VideoTrack#selectedchange
  24764. */
  24765. Object.defineProperty(track, 'selected', {
  24766. get: function get() {
  24767. return selected;
  24768. },
  24769. set: function set(newSelected) {
  24770. // an invalid or unchanged value
  24771. if (typeof newSelected !== 'boolean' || newSelected === selected) {
  24772. return;
  24773. }
  24774. selected = newSelected;
  24775. /**
  24776. * An event that fires when selected changes on this track. This allows
  24777. * the VideoTrackList that holds this track to act accordingly.
  24778. *
  24779. * > Note: This is not part of the spec! Native tracks will do
  24780. * this internally without an event.
  24781. *
  24782. * @event VideoTrack#selectedchange
  24783. * @type {EventTarget~Event}
  24784. */
  24785. this.trigger('selectedchange');
  24786. }
  24787. });
  24788. // if the user sets this track to selected then
  24789. // set selected to that true value otherwise
  24790. // we keep it false
  24791. if (settings.selected) {
  24792. track.selected = settings.selected;
  24793. }
  24794. return _ret = track, _possibleConstructorReturn(_this, _ret);
  24795. }
  24796. return VideoTrack;
  24797. }(_track2['default']);
  24798. exports['default'] = VideoTrack;
  24799. },{"../utils/browser.js":120,"../utils/merge-options":129,"./track":117,"./track-enums":115}],120:[function(require,module,exports){
  24800. 'use strict';
  24801. exports.__esModule = true;
  24802. exports.BACKGROUND_SIZE_SUPPORTED = exports.TOUCH_ENABLED = exports.IS_ANY_SAFARI = exports.IS_SAFARI = exports.IE_VERSION = exports.IS_IE8 = exports.CHROME_VERSION = exports.IS_CHROME = exports.IS_EDGE = exports.IS_FIREFOX = exports.IS_NATIVE_ANDROID = exports.IS_OLD_ANDROID = exports.ANDROID_VERSION = exports.IS_ANDROID = exports.IOS_VERSION = exports.IS_IOS = exports.IS_IPOD = exports.IS_IPHONE = exports.IS_IPAD = undefined;
  24803. var _dom = require('./dom');
  24804. var Dom = _interopRequireWildcard(_dom);
  24805. var _window = require('global/window');
  24806. var _window2 = _interopRequireDefault(_window);
  24807. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  24808. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  24809. /**
  24810. * @file browser.js
  24811. * @module browser
  24812. */
  24813. var USER_AGENT = _window2['default'].navigator && _window2['default'].navigator.userAgent || '';
  24814. var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
  24815. var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
  24816. /*
  24817. * Device is an iPhone
  24818. *
  24819. * @type {Boolean}
  24820. * @constant
  24821. * @private
  24822. */
  24823. var IS_IPAD = exports.IS_IPAD = /iPad/i.test(USER_AGENT);
  24824. // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
  24825. // to identify iPhones, we need to exclude iPads.
  24826. // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
  24827. var IS_IPHONE = exports.IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
  24828. var IS_IPOD = exports.IS_IPOD = /iPod/i.test(USER_AGENT);
  24829. var IS_IOS = exports.IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
  24830. var IOS_VERSION = exports.IOS_VERSION = function () {
  24831. var match = USER_AGENT.match(/OS (\d+)_/i);
  24832. if (match && match[1]) {
  24833. return match[1];
  24834. }
  24835. return null;
  24836. }();
  24837. var IS_ANDROID = exports.IS_ANDROID = /Android/i.test(USER_AGENT);
  24838. var ANDROID_VERSION = exports.ANDROID_VERSION = function () {
  24839. // This matches Android Major.Minor.Patch versions
  24840. // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
  24841. var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
  24842. if (!match) {
  24843. return null;
  24844. }
  24845. var major = match[1] && parseFloat(match[1]);
  24846. var minor = match[2] && parseFloat(match[2]);
  24847. if (major && minor) {
  24848. return parseFloat(match[1] + '.' + match[2]);
  24849. } else if (major) {
  24850. return major;
  24851. }
  24852. return null;
  24853. }();
  24854. // Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
  24855. var IS_OLD_ANDROID = exports.IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
  24856. var IS_NATIVE_ANDROID = exports.IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
  24857. var IS_FIREFOX = exports.IS_FIREFOX = /Firefox/i.test(USER_AGENT);
  24858. var IS_EDGE = exports.IS_EDGE = /Edge/i.test(USER_AGENT);
  24859. var IS_CHROME = exports.IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT);
  24860. var CHROME_VERSION = exports.CHROME_VERSION = function () {
  24861. var match = USER_AGENT.match(/Chrome\/(\d+)/);
  24862. if (match && match[1]) {
  24863. return parseFloat(match[1]);
  24864. }
  24865. return null;
  24866. }();
  24867. var IS_IE8 = exports.IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);
  24868. var IE_VERSION = exports.IE_VERSION = function () {
  24869. var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
  24870. var version = result && parseFloat(result[1]);
  24871. if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
  24872. // IE 11 has a different user agent string than other IE versions
  24873. version = 11.0;
  24874. }
  24875. return version;
  24876. }();
  24877. var IS_SAFARI = exports.IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
  24878. var IS_ANY_SAFARI = exports.IS_ANY_SAFARI = IS_SAFARI || IS_IOS;
  24879. var TOUCH_ENABLED = exports.TOUCH_ENABLED = Dom.isReal() && ('ontouchstart' in _window2['default'] || _window2['default'].DocumentTouch && _window2['default'].document instanceof _window2['default'].DocumentTouch);
  24880. var BACKGROUND_SIZE_SUPPORTED = exports.BACKGROUND_SIZE_SUPPORTED = Dom.isReal() && 'backgroundSize' in _window2['default'].document.createElement('video').style;
  24881. },{"./dom":123,"global/window":137}],121:[function(require,module,exports){
  24882. 'use strict';
  24883. exports.__esModule = true;
  24884. exports.bufferedPercent = bufferedPercent;
  24885. var _timeRanges = require('./time-ranges.js');
  24886. /**
  24887. * Compute the percentage of the media that has been buffered.
  24888. *
  24889. * @param {TimeRange} buffered
  24890. * The current `TimeRange` object representing buffered time ranges
  24891. *
  24892. * @param {number} duration
  24893. * Total duration of the media
  24894. *
  24895. * @return {number}
  24896. * Percent buffered of the total duration in decimal form.
  24897. */
  24898. function bufferedPercent(buffered, duration) {
  24899. var bufferedDuration = 0;
  24900. var start = void 0;
  24901. var end = void 0;
  24902. if (!duration) {
  24903. return 0;
  24904. }
  24905. if (!buffered || !buffered.length) {
  24906. buffered = (0, _timeRanges.createTimeRange)(0, 0);
  24907. }
  24908. for (var i = 0; i < buffered.length; i++) {
  24909. start = buffered.start(i);
  24910. end = buffered.end(i);
  24911. // buffered end can be bigger than duration by a very small fraction
  24912. if (end > duration) {
  24913. end = duration;
  24914. }
  24915. bufferedDuration += end - start;
  24916. }
  24917. return bufferedDuration / duration;
  24918. } /**
  24919. * @file buffer.js
  24920. * @module buffer
  24921. */
  24922. },{"./time-ranges.js":132}],122:[function(require,module,exports){
  24923. 'use strict';
  24924. exports.__esModule = true;
  24925. exports['default'] = computedStyle;
  24926. var _window = require('global/window');
  24927. var _window2 = _interopRequireDefault(_window);
  24928. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  24929. /**
  24930. * A safe getComputedStyle with an IE8 fallback.
  24931. *
  24932. * This is needed because in Firefox, if the player is loaded in an iframe with
  24933. * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to
  24934. * make sure that the player doesn't break in these cases.
  24935. *
  24936. * @param {Element} el
  24937. * The element you want the computed style of
  24938. *
  24939. * @param {string} prop
  24940. * The property name you want
  24941. *
  24942. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
  24943. */
  24944. function computedStyle(el, prop) {
  24945. if (!el || !prop) {
  24946. return '';
  24947. }
  24948. if (typeof _window2['default'].getComputedStyle === 'function') {
  24949. var cs = _window2['default'].getComputedStyle(el);
  24950. return cs ? cs[prop] : '';
  24951. }
  24952. return el.currentStyle[prop] || '';
  24953. } /**
  24954. * @file computed-style.js
  24955. * @module computed-style
  24956. */
  24957. },{"global/window":137}],123:[function(require,module,exports){
  24958. 'use strict';
  24959. exports.__esModule = true;
  24960. exports.$$ = exports.$ = undefined;
  24961. var _templateObject = _taggedTemplateLiteralLoose(['Setting attributes in the second argument of createEl()\n has been deprecated. Use the third argument instead.\n createEl(type, properties, attributes). Attempting to set ', ' to ', '.'], ['Setting attributes in the second argument of createEl()\n has been deprecated. Use the third argument instead.\n createEl(type, properties, attributes). Attempting to set ', ' to ', '.']);
  24962. exports.isReal = isReal;
  24963. exports.isEl = isEl;
  24964. exports.getEl = getEl;
  24965. exports.createEl = createEl;
  24966. exports.textContent = textContent;
  24967. exports.insertElFirst = insertElFirst;
  24968. exports.getElData = getElData;
  24969. exports.hasElData = hasElData;
  24970. exports.removeElData = removeElData;
  24971. exports.hasElClass = hasElClass;
  24972. exports.addElClass = addElClass;
  24973. exports.removeElClass = removeElClass;
  24974. exports.toggleElClass = toggleElClass;
  24975. exports.setElAttributes = setElAttributes;
  24976. exports.getElAttributes = getElAttributes;
  24977. exports.getAttribute = getAttribute;
  24978. exports.setAttribute = setAttribute;
  24979. exports.removeAttribute = removeAttribute;
  24980. exports.blockTextSelection = blockTextSelection;
  24981. exports.unblockTextSelection = unblockTextSelection;
  24982. exports.findElPosition = findElPosition;
  24983. exports.getPointerPosition = getPointerPosition;
  24984. exports.isTextNode = isTextNode;
  24985. exports.emptyEl = emptyEl;
  24986. exports.normalizeContent = normalizeContent;
  24987. exports.appendContent = appendContent;
  24988. exports.insertContent = insertContent;
  24989. var _document = require('global/document');
  24990. var _document2 = _interopRequireDefault(_document);
  24991. var _window = require('global/window');
  24992. var _window2 = _interopRequireDefault(_window);
  24993. var _guid = require('./guid.js');
  24994. var Guid = _interopRequireWildcard(_guid);
  24995. var _log = require('./log.js');
  24996. var _log2 = _interopRequireDefault(_log);
  24997. var _tsml = require('tsml');
  24998. var _tsml2 = _interopRequireDefault(_tsml);
  24999. var _obj = require('./obj');
  25000. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  25001. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  25002. function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; } /**
  25003. * @file dom.js
  25004. * @module dom
  25005. */
  25006. /**
  25007. * Detect if a value is a string with any non-whitespace characters.
  25008. *
  25009. * @param {string} str
  25010. * The string to check
  25011. *
  25012. * @return {boolean}
  25013. * - True if the string is non-blank
  25014. * - False otherwise
  25015. *
  25016. */
  25017. function isNonBlankString(str) {
  25018. return typeof str === 'string' && /\S/.test(str);
  25019. }
  25020. /**
  25021. * Throws an error if the passed string has whitespace. This is used by
  25022. * class methods to be relatively consistent with the classList API.
  25023. *
  25024. * @param {string} str
  25025. * The string to check for whitespace.
  25026. *
  25027. * @throws {Error}
  25028. * Throws an error if there is whitespace in the string.
  25029. *
  25030. */
  25031. function throwIfWhitespace(str) {
  25032. if (/\s/.test(str)) {
  25033. throw new Error('class has illegal whitespace characters');
  25034. }
  25035. }
  25036. /**
  25037. * Produce a regular expression for matching a className within an elements className.
  25038. *
  25039. * @param {string} className
  25040. * The className to generate the RegExp for.
  25041. *
  25042. * @return {RegExp}
  25043. * The RegExp that will check for a specific `className` in an elements
  25044. * className.
  25045. */
  25046. function classRegExp(className) {
  25047. return new RegExp('(^|\\s)' + className + '($|\\s)');
  25048. }
  25049. /**
  25050. * Whether the current DOM interface appears to be real.
  25051. *
  25052. * @return {Boolean}
  25053. */
  25054. function isReal() {
  25055. return (
  25056. // Both document and window will never be undefined thanks to `global`.
  25057. _document2['default'] === _window2['default'].document &&
  25058. // In IE < 9, DOM methods return "object" as their type, so all we can
  25059. // confidently check is that it exists.
  25060. typeof _document2['default'].createElement !== 'undefined'
  25061. );
  25062. }
  25063. /**
  25064. * Determines, via duck typing, whether or not a value is a DOM element.
  25065. *
  25066. * @param {Mixed} value
  25067. * The thing to check
  25068. *
  25069. * @return {boolean}
  25070. * - True if it is a DOM element
  25071. * - False otherwise
  25072. */
  25073. function isEl(value) {
  25074. return (0, _obj.isObject)(value) && value.nodeType === 1;
  25075. }
  25076. /**
  25077. * Creates functions to query the DOM using a given method.
  25078. *
  25079. * @param {string} method
  25080. * The method to create the query with.
  25081. *
  25082. * @return {Function}
  25083. * The query method
  25084. */
  25085. function createQuerier(method) {
  25086. return function (selector, context) {
  25087. if (!isNonBlankString(selector)) {
  25088. return _document2['default'][method](null);
  25089. }
  25090. if (isNonBlankString(context)) {
  25091. context = _document2['default'].querySelector(context);
  25092. }
  25093. var ctx = isEl(context) ? context : _document2['default'];
  25094. return ctx[method] && ctx[method](selector);
  25095. };
  25096. }
  25097. /**
  25098. * Shorthand for document.getElementById()
  25099. * Also allows for CSS (jQuery) ID syntax. But nothing other than IDs.
  25100. *
  25101. * @param {string} id
  25102. * The id of the element to get
  25103. *
  25104. * @return {Element|null}
  25105. * Element with supplied ID or null if there wasn't one.
  25106. */
  25107. function getEl(id) {
  25108. if (id.indexOf('#') === 0) {
  25109. id = id.slice(1);
  25110. }
  25111. return _document2['default'].getElementById(id);
  25112. }
  25113. /**
  25114. * Creates an element and applies properties.
  25115. *
  25116. * @param {string} [tagName='div']
  25117. * Name of tag to be created.
  25118. *
  25119. * @param {Object} [properties={}]
  25120. * Element properties to be applied.
  25121. *
  25122. * @param {Object} [attributes={}]
  25123. * Element attributes to be applied.
  25124. *
  25125. * @param {String|Element|TextNode|Array|Function} [content]
  25126. * Contents for the element (see: {@link dom:normalizeContent})
  25127. *
  25128. * @return {Element}
  25129. * The element that was created.
  25130. */
  25131. function createEl() {
  25132. var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  25133. var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  25134. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  25135. var content = arguments[3];
  25136. var el = _document2['default'].createElement(tagName);
  25137. Object.getOwnPropertyNames(properties).forEach(function (propName) {
  25138. var val = properties[propName];
  25139. // See #2176
  25140. // We originally were accepting both properties and attributes in the
  25141. // same object, but that doesn't work so well.
  25142. if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
  25143. _log2['default'].warn((0, _tsml2['default'])(_templateObject, propName, val));
  25144. el.setAttribute(propName, val);
  25145. // Handle textContent since it's not supported everywhere and we have a
  25146. // method for it.
  25147. } else if (propName === 'textContent') {
  25148. textContent(el, val);
  25149. } else {
  25150. el[propName] = val;
  25151. }
  25152. });
  25153. Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
  25154. el.setAttribute(attrName, attributes[attrName]);
  25155. });
  25156. if (content) {
  25157. appendContent(el, content);
  25158. }
  25159. return el;
  25160. }
  25161. /**
  25162. * Injects text into an element, replacing any existing contents entirely.
  25163. *
  25164. * @param {Element} el
  25165. * The element to add text content into
  25166. *
  25167. * @param {string} text
  25168. * The text content to add.
  25169. *
  25170. * @return {Element}
  25171. * The element with added text content.
  25172. */
  25173. function textContent(el, text) {
  25174. if (typeof el.textContent === 'undefined') {
  25175. el.innerText = text;
  25176. } else {
  25177. el.textContent = text;
  25178. }
  25179. return el;
  25180. }
  25181. /**
  25182. * Insert an element as the first child node of another
  25183. *
  25184. * @param {Element} child
  25185. * Element to insert
  25186. *
  25187. * @param {Element} parent
  25188. * Element to insert child into
  25189. *
  25190. */
  25191. function insertElFirst(child, parent) {
  25192. if (parent.firstChild) {
  25193. parent.insertBefore(child, parent.firstChild);
  25194. } else {
  25195. parent.appendChild(child);
  25196. }
  25197. }
  25198. /**
  25199. * Element Data Store. Allows for binding data to an element without putting it directly on the element.
  25200. * Ex. Event listeners are stored here.
  25201. * (also from jsninja.com, slightly modified and updated for closure compiler)
  25202. *
  25203. * @type {Object}
  25204. * @private
  25205. */
  25206. var elData = {};
  25207. /*
  25208. * Unique attribute name to store an element's guid in
  25209. *
  25210. * @type {string}
  25211. * @constant
  25212. * @private
  25213. */
  25214. var elIdAttr = 'vdata' + new Date().getTime();
  25215. /**
  25216. * Returns the cache object where data for an element is stored
  25217. *
  25218. * @param {Element} el
  25219. * Element to store data for.
  25220. *
  25221. * @return {Object}
  25222. * The cache object for that el that was passed in.
  25223. */
  25224. function getElData(el) {
  25225. var id = el[elIdAttr];
  25226. if (!id) {
  25227. id = el[elIdAttr] = Guid.newGUID();
  25228. }
  25229. if (!elData[id]) {
  25230. elData[id] = {};
  25231. }
  25232. return elData[id];
  25233. }
  25234. /**
  25235. * Returns whether or not an element has cached data
  25236. *
  25237. * @param {Element} el
  25238. * Check if this element has cached data.
  25239. *
  25240. * @return {boolean}
  25241. * - True if the DOM element has cached data.
  25242. * - False otherwise.
  25243. */
  25244. function hasElData(el) {
  25245. var id = el[elIdAttr];
  25246. if (!id) {
  25247. return false;
  25248. }
  25249. return !!Object.getOwnPropertyNames(elData[id]).length;
  25250. }
  25251. /**
  25252. * Delete data for the element from the cache and the guid attr from getElementById
  25253. *
  25254. * @param {Element} el
  25255. * Remove cached data for this element.
  25256. */
  25257. function removeElData(el) {
  25258. var id = el[elIdAttr];
  25259. if (!id) {
  25260. return;
  25261. }
  25262. // Remove all stored data
  25263. delete elData[id];
  25264. // Remove the elIdAttr property from the DOM node
  25265. try {
  25266. delete el[elIdAttr];
  25267. } catch (e) {
  25268. if (el.removeAttribute) {
  25269. el.removeAttribute(elIdAttr);
  25270. } else {
  25271. // IE doesn't appear to support removeAttribute on the document element
  25272. el[elIdAttr] = null;
  25273. }
  25274. }
  25275. }
  25276. /**
  25277. * Check if an element has a CSS class
  25278. *
  25279. * @param {Element} element
  25280. * Element to check
  25281. *
  25282. * @param {string} classToCheck
  25283. * Class name to check for
  25284. *
  25285. * @return {boolean}
  25286. * - True if the element had the class
  25287. * - False otherwise.
  25288. *
  25289. * @throws {Error}
  25290. * Throws an error if `classToCheck` has white space.
  25291. */
  25292. function hasElClass(element, classToCheck) {
  25293. throwIfWhitespace(classToCheck);
  25294. if (element.classList) {
  25295. return element.classList.contains(classToCheck);
  25296. }
  25297. return classRegExp(classToCheck).test(element.className);
  25298. }
  25299. /**
  25300. * Add a CSS class name to an element
  25301. *
  25302. * @param {Element} element
  25303. * Element to add class name to.
  25304. *
  25305. * @param {string} classToAdd
  25306. * Class name to add.
  25307. *
  25308. * @return {Element}
  25309. * The dom element with the added class name.
  25310. */
  25311. function addElClass(element, classToAdd) {
  25312. if (element.classList) {
  25313. element.classList.add(classToAdd);
  25314. // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
  25315. // in the case of classList not being supported.
  25316. } else if (!hasElClass(element, classToAdd)) {
  25317. element.className = (element.className + ' ' + classToAdd).trim();
  25318. }
  25319. return element;
  25320. }
  25321. /**
  25322. * Remove a CSS class name from an element
  25323. *
  25324. * @param {Element} element
  25325. * Element to remove a class name from.
  25326. *
  25327. * @param {string} classToRemove
  25328. * Class name to remove
  25329. *
  25330. * @return {Element}
  25331. * The dom element with class name removed.
  25332. */
  25333. function removeElClass(element, classToRemove) {
  25334. if (element.classList) {
  25335. element.classList.remove(classToRemove);
  25336. } else {
  25337. throwIfWhitespace(classToRemove);
  25338. element.className = element.className.split(/\s+/).filter(function (c) {
  25339. return c !== classToRemove;
  25340. }).join(' ');
  25341. }
  25342. return element;
  25343. }
  25344. /**
  25345. * The callback definition for toggleElClass.
  25346. *
  25347. * @callback Dom~PredicateCallback
  25348. * @param {Element} element
  25349. * The DOM element of the Component.
  25350. *
  25351. * @param {string} classToToggle
  25352. * The `className` that wants to be toggled
  25353. *
  25354. * @return {boolean|undefined}
  25355. * - If true the `classToToggle` will get added to `element`.
  25356. * - If false the `classToToggle` will get removed from `element`.
  25357. * - If undefined this callback will be ignored
  25358. */
  25359. /**
  25360. * Adds or removes a CSS class name on an element depending on an optional
  25361. * condition or the presence/absence of the class name.
  25362. *
  25363. * @param {Element} element
  25364. * The element to toggle a class name on.
  25365. *
  25366. * @param {string} classToToggle
  25367. * The class that should be toggled
  25368. *
  25369. * @param {boolean|PredicateCallback} [predicate]
  25370. * See the return value for {@link Dom~PredicateCallback}
  25371. *
  25372. * @return {Element}
  25373. * The element with a class that has been toggled.
  25374. */
  25375. function toggleElClass(element, classToToggle, predicate) {
  25376. // This CANNOT use `classList` internally because IE does not support the
  25377. // second parameter to the `classList.toggle()` method! Which is fine because
  25378. // `classList` will be used by the add/remove functions.
  25379. var has = hasElClass(element, classToToggle);
  25380. if (typeof predicate === 'function') {
  25381. predicate = predicate(element, classToToggle);
  25382. }
  25383. if (typeof predicate !== 'boolean') {
  25384. predicate = !has;
  25385. }
  25386. // If the necessary class operation matches the current state of the
  25387. // element, no action is required.
  25388. if (predicate === has) {
  25389. return;
  25390. }
  25391. if (predicate) {
  25392. addElClass(element, classToToggle);
  25393. } else {
  25394. removeElClass(element, classToToggle);
  25395. }
  25396. return element;
  25397. }
  25398. /**
  25399. * Apply attributes to an HTML element.
  25400. *
  25401. * @param {Element} el
  25402. * Element to add attributes to.
  25403. *
  25404. * @param {Object} [attributes]
  25405. * Attributes to be applied.
  25406. */
  25407. function setElAttributes(el, attributes) {
  25408. Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
  25409. var attrValue = attributes[attrName];
  25410. if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
  25411. el.removeAttribute(attrName);
  25412. } else {
  25413. el.setAttribute(attrName, attrValue === true ? '' : attrValue);
  25414. }
  25415. });
  25416. }
  25417. /**
  25418. * Get an element's attribute values, as defined on the HTML tag
  25419. * Attributes are not the same as properties. They're defined on the tag
  25420. * or with setAttribute (which shouldn't be used with HTML)
  25421. * This will return true or false for boolean attributes.
  25422. *
  25423. * @param {Element} tag
  25424. * Element from which to get tag attributes.
  25425. *
  25426. * @return {Object}
  25427. * All attributes of the element.
  25428. */
  25429. function getElAttributes(tag) {
  25430. var obj = {};
  25431. // known boolean attributes
  25432. // we can check for matching boolean properties, but older browsers
  25433. // won't know about HTML5 boolean attributes that we still read from
  25434. var knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ',';
  25435. if (tag && tag.attributes && tag.attributes.length > 0) {
  25436. var attrs = tag.attributes;
  25437. for (var i = attrs.length - 1; i >= 0; i--) {
  25438. var attrName = attrs[i].name;
  25439. var attrVal = attrs[i].value;
  25440. // check for known booleans
  25441. // the matching element property will return a value for typeof
  25442. if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
  25443. // the value of an included boolean attribute is typically an empty
  25444. // string ('') which would equal false if we just check for a false value.
  25445. // we also don't want support bad code like autoplay='false'
  25446. attrVal = attrVal !== null ? true : false;
  25447. }
  25448. obj[attrName] = attrVal;
  25449. }
  25450. }
  25451. return obj;
  25452. }
  25453. /**
  25454. * Get the value of an element's attribute
  25455. *
  25456. * @param {Element} el
  25457. * A DOM element
  25458. *
  25459. * @param {string} attribute
  25460. * Attribute to get the value of
  25461. *
  25462. * @return {string}
  25463. * value of the attribute
  25464. */
  25465. function getAttribute(el, attribute) {
  25466. return el.getAttribute(attribute);
  25467. }
  25468. /**
  25469. * Set the value of an element's attribute
  25470. *
  25471. * @param {Element} el
  25472. * A DOM element
  25473. *
  25474. * @param {string} attribute
  25475. * Attribute to set
  25476. *
  25477. * @param {string} value
  25478. * Value to set the attribute to
  25479. */
  25480. function setAttribute(el, attribute, value) {
  25481. el.setAttribute(attribute, value);
  25482. }
  25483. /**
  25484. * Remove an element's attribute
  25485. *
  25486. * @param {Element} el
  25487. * A DOM element
  25488. *
  25489. * @param {string} attribute
  25490. * Attribute to remove
  25491. */
  25492. function removeAttribute(el, attribute) {
  25493. el.removeAttribute(attribute);
  25494. }
  25495. /**
  25496. * Attempt to block the ability to select text while dragging controls
  25497. */
  25498. function blockTextSelection() {
  25499. _document2['default'].body.focus();
  25500. _document2['default'].onselectstart = function () {
  25501. return false;
  25502. };
  25503. }
  25504. /**
  25505. * Turn off text selection blocking
  25506. */
  25507. function unblockTextSelection() {
  25508. _document2['default'].onselectstart = function () {
  25509. return true;
  25510. };
  25511. }
  25512. /**
  25513. * The postion of a DOM element on the page.
  25514. *
  25515. * @typedef {Object} Dom~Position
  25516. *
  25517. * @property {number} left
  25518. * Pixels to the left
  25519. *
  25520. * @property {number} top
  25521. * Pixels on top
  25522. */
  25523. /**
  25524. * Offset Left.
  25525. * getBoundingClientRect technique from
  25526. * John Resig
  25527. *
  25528. * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
  25529. *
  25530. * @param {Element} el
  25531. * Element from which to get offset
  25532. *
  25533. * @return {Dom~Position}
  25534. * The position of the element that was passed in.
  25535. */
  25536. function findElPosition(el) {
  25537. var box = void 0;
  25538. if (el.getBoundingClientRect && el.parentNode) {
  25539. box = el.getBoundingClientRect();
  25540. }
  25541. if (!box) {
  25542. return {
  25543. left: 0,
  25544. top: 0
  25545. };
  25546. }
  25547. var docEl = _document2['default'].documentElement;
  25548. var body = _document2['default'].body;
  25549. var clientLeft = docEl.clientLeft || body.clientLeft || 0;
  25550. var scrollLeft = _window2['default'].pageXOffset || body.scrollLeft;
  25551. var left = box.left + scrollLeft - clientLeft;
  25552. var clientTop = docEl.clientTop || body.clientTop || 0;
  25553. var scrollTop = _window2['default'].pageYOffset || body.scrollTop;
  25554. var top = box.top + scrollTop - clientTop;
  25555. // Android sometimes returns slightly off decimal values, so need to round
  25556. return {
  25557. left: Math.round(left),
  25558. top: Math.round(top)
  25559. };
  25560. }
  25561. /**
  25562. * x and y coordinates for a dom element or mouse pointer
  25563. *
  25564. * @typedef {Object} Dom~Coordinates
  25565. *
  25566. * @property {number} x
  25567. * x coordinate in pixels
  25568. *
  25569. * @property {number} y
  25570. * y coordinate in pixels
  25571. */
  25572. /**
  25573. * Get pointer position in element
  25574. * Returns an object with x and y coordinates.
  25575. * The base on the coordinates are the bottom left of the element.
  25576. *
  25577. * @param {Element} el
  25578. * Element on which to get the pointer position on
  25579. *
  25580. * @param {EventTarget~Event} event
  25581. * Event object
  25582. *
  25583. * @return {Dom~Coordinates}
  25584. * A Coordinates object corresponding to the mouse position.
  25585. *
  25586. */
  25587. function getPointerPosition(el, event) {
  25588. var position = {};
  25589. var box = findElPosition(el);
  25590. var boxW = el.offsetWidth;
  25591. var boxH = el.offsetHeight;
  25592. var boxY = box.top;
  25593. var boxX = box.left;
  25594. var pageY = event.pageY;
  25595. var pageX = event.pageX;
  25596. if (event.changedTouches) {
  25597. pageX = event.changedTouches[0].pageX;
  25598. pageY = event.changedTouches[0].pageY;
  25599. }
  25600. position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
  25601. position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
  25602. return position;
  25603. }
  25604. /**
  25605. * Determines, via duck typing, whether or not a value is a text node.
  25606. *
  25607. * @param {Mixed} value
  25608. * Check if this value is a text node.
  25609. *
  25610. * @return {boolean}
  25611. * - True if it is a text node
  25612. * - False otherwise
  25613. */
  25614. function isTextNode(value) {
  25615. return (0, _obj.isObject)(value) && value.nodeType === 3;
  25616. }
  25617. /**
  25618. * Empties the contents of an element.
  25619. *
  25620. * @param {Element} el
  25621. * The element to empty children from
  25622. *
  25623. * @return {Element}
  25624. * The element with no children
  25625. */
  25626. function emptyEl(el) {
  25627. while (el.firstChild) {
  25628. el.removeChild(el.firstChild);
  25629. }
  25630. return el;
  25631. }
  25632. /**
  25633. * Normalizes content for eventual insertion into the DOM.
  25634. *
  25635. * This allows a wide range of content definition methods, but protects
  25636. * from falling into the trap of simply writing to `innerHTML`, which is
  25637. * an XSS concern.
  25638. *
  25639. * The content for an element can be passed in multiple types and
  25640. * combinations, whose behavior is as follows:
  25641. *
  25642. * @param {String|Element|TextNode|Array|Function} content
  25643. * - String: Normalized into a text node.
  25644. * - Element/TextNode: Passed through.
  25645. * - Array: A one-dimensional array of strings, elements, nodes, or functions
  25646. * (which return single strings, elements, or nodes).
  25647. * - Function: If the sole argument, is expected to produce a string, element,
  25648. * node, or array as defined above.
  25649. *
  25650. * @return {Array}
  25651. * All of the content that was passed in normalized.
  25652. */
  25653. function normalizeContent(content) {
  25654. // First, invoke content if it is a function. If it produces an array,
  25655. // that needs to happen before normalization.
  25656. if (typeof content === 'function') {
  25657. content = content();
  25658. }
  25659. // Next up, normalize to an array, so one or many items can be normalized,
  25660. // filtered, and returned.
  25661. return (Array.isArray(content) ? content : [content]).map(function (value) {
  25662. // First, invoke value if it is a function to produce a new value,
  25663. // which will be subsequently normalized to a Node of some kind.
  25664. if (typeof value === 'function') {
  25665. value = value();
  25666. }
  25667. if (isEl(value) || isTextNode(value)) {
  25668. return value;
  25669. }
  25670. if (typeof value === 'string' && /\S/.test(value)) {
  25671. return _document2['default'].createTextNode(value);
  25672. }
  25673. }).filter(function (value) {
  25674. return value;
  25675. });
  25676. }
  25677. /**
  25678. * Normalizes and appends content to an element.
  25679. *
  25680. * @param {Element} el
  25681. * Element to append normalized content to.
  25682. *
  25683. *
  25684. * @param {String|Element|TextNode|Array|Function} content
  25685. * See the `content` argument of {@link dom:normalizeContent}
  25686. *
  25687. * @return {Element}
  25688. * The element with appended normalized content.
  25689. */
  25690. function appendContent(el, content) {
  25691. normalizeContent(content).forEach(function (node) {
  25692. return el.appendChild(node);
  25693. });
  25694. return el;
  25695. }
  25696. /**
  25697. * Normalizes and inserts content into an element; this is identical to
  25698. * `appendContent()`, except it empties the element first.
  25699. *
  25700. * @param {Element} el
  25701. * Element to insert normalized content into.
  25702. *
  25703. * @param {String|Element|TextNode|Array|Function} content
  25704. * See the `content` argument of {@link dom:normalizeContent}
  25705. *
  25706. * @return {Element}
  25707. * The element with inserted normalized content.
  25708. *
  25709. */
  25710. function insertContent(el, content) {
  25711. return appendContent(emptyEl(el), content);
  25712. }
  25713. /**
  25714. * Finds a single DOM element matching `selector` within the optional
  25715. * `context` of another DOM element (defaulting to `document`).
  25716. *
  25717. * @param {string} selector
  25718. * A valid CSS selector, which will be passed to `querySelector`.
  25719. *
  25720. * @param {Element|String} [context=document]
  25721. * A DOM element within which to query. Can also be a selector
  25722. * string in which case the first matching element will be used
  25723. * as context. If missing (or no element matches selector), falls
  25724. * back to `document`.
  25725. *
  25726. * @return {Element|null}
  25727. * The element that was found or null.
  25728. */
  25729. var $ = exports.$ = createQuerier('querySelector');
  25730. /**
  25731. * Finds a all DOM elements matching `selector` within the optional
  25732. * `context` of another DOM element (defaulting to `document`).
  25733. *
  25734. * @param {string} selector
  25735. * A valid CSS selector, which will be passed to `querySelectorAll`.
  25736. *
  25737. * @param {Element|String} [context=document]
  25738. * A DOM element within which to query. Can also be a selector
  25739. * string in which case the first matching element will be used
  25740. * as context. If missing (or no element matches selector), falls
  25741. * back to `document`.
  25742. *
  25743. * @return {NodeList}
  25744. * A element list of elements that were found. Will be empty if none were found.
  25745. *
  25746. */
  25747. var $$ = exports.$$ = createQuerier('querySelectorAll');
  25748. },{"./guid.js":127,"./log.js":128,"./obj":130,"global/document":136,"global/window":137,"tsml":42}],124:[function(require,module,exports){
  25749. 'use strict';
  25750. exports.__esModule = true;
  25751. exports.fixEvent = fixEvent;
  25752. exports.on = on;
  25753. exports.off = off;
  25754. exports.trigger = trigger;
  25755. exports.one = one;
  25756. var _dom = require('./dom.js');
  25757. var Dom = _interopRequireWildcard(_dom);
  25758. var _guid = require('./guid.js');
  25759. var Guid = _interopRequireWildcard(_guid);
  25760. var _log = require('./log.js');
  25761. var _log2 = _interopRequireDefault(_log);
  25762. var _window = require('global/window');
  25763. var _window2 = _interopRequireDefault(_window);
  25764. var _document = require('global/document');
  25765. var _document2 = _interopRequireDefault(_document);
  25766. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  25767. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  25768. /**
  25769. * Clean up the listener cache and dispatchers
  25770. *
  25771. * @param {Element|Object} elem
  25772. * Element to clean up
  25773. *
  25774. * @param {string} type
  25775. * Type of event to clean up
  25776. */
  25777. function _cleanUpEvents(elem, type) {
  25778. var data = Dom.getElData(elem);
  25779. // Remove the events of a particular type if there are none left
  25780. if (data.handlers[type].length === 0) {
  25781. delete data.handlers[type];
  25782. // data.handlers[type] = null;
  25783. // Setting to null was causing an error with data.handlers
  25784. // Remove the meta-handler from the element
  25785. if (elem.removeEventListener) {
  25786. elem.removeEventListener(type, data.dispatcher, false);
  25787. } else if (elem.detachEvent) {
  25788. elem.detachEvent('on' + type, data.dispatcher);
  25789. }
  25790. }
  25791. // Remove the events object if there are no types left
  25792. if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
  25793. delete data.handlers;
  25794. delete data.dispatcher;
  25795. delete data.disabled;
  25796. }
  25797. // Finally remove the element data if there is no data left
  25798. if (Object.getOwnPropertyNames(data).length === 0) {
  25799. Dom.removeElData(elem);
  25800. }
  25801. }
  25802. /**
  25803. * Loops through an array of event types and calls the requested method for each type.
  25804. *
  25805. * @param {Function} fn
  25806. * The event method we want to use.
  25807. *
  25808. * @param {Element|Object} elem
  25809. * Element or object to bind listeners to
  25810. *
  25811. * @param {string} type
  25812. * Type of event to bind to.
  25813. *
  25814. * @param {EventTarget~EventListener} callback
  25815. * Event listener.
  25816. */
  25817. /**
  25818. * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
  25819. * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
  25820. * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
  25821. * robust as jquery's, so there's probably some differences.
  25822. *
  25823. * @module events
  25824. */
  25825. function _handleMultipleEvents(fn, elem, types, callback) {
  25826. types.forEach(function (type) {
  25827. // Call the event method for each one of the types
  25828. fn(elem, type, callback);
  25829. });
  25830. }
  25831. /**
  25832. * Fix a native event to have standard property values
  25833. *
  25834. * @param {Object} event
  25835. * Event object to fix.
  25836. *
  25837. * @return {Object}
  25838. * Fixed event object.
  25839. */
  25840. function fixEvent(event) {
  25841. function returnTrue() {
  25842. return true;
  25843. }
  25844. function returnFalse() {
  25845. return false;
  25846. }
  25847. // Test if fixing up is needed
  25848. // Used to check if !event.stopPropagation instead of isPropagationStopped
  25849. // But native events return true for stopPropagation, but don't have
  25850. // other expected methods like isPropagationStopped. Seems to be a problem
  25851. // with the Javascript Ninja code. So we're just overriding all events now.
  25852. if (!event || !event.isPropagationStopped) {
  25853. var old = event || _window2['default'].event;
  25854. event = {};
  25855. // Clone the old object so that we can modify the values event = {};
  25856. // IE8 Doesn't like when you mess with native event properties
  25857. // Firefox returns false for event.hasOwnProperty('type') and other props
  25858. // which makes copying more difficult.
  25859. // TODO: Probably best to create a whitelist of event props
  25860. for (var key in old) {
  25861. // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
  25862. // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
  25863. // and webkitMovementX/Y
  25864. if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
  25865. // Chrome 32+ warns if you try to copy deprecated returnValue, but
  25866. // we still want to if preventDefault isn't supported (IE8).
  25867. if (!(key === 'returnValue' && old.preventDefault)) {
  25868. event[key] = old[key];
  25869. }
  25870. }
  25871. }
  25872. // The event occurred on this element
  25873. if (!event.target) {
  25874. event.target = event.srcElement || _document2['default'];
  25875. }
  25876. // Handle which other element the event is related to
  25877. if (!event.relatedTarget) {
  25878. event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
  25879. }
  25880. // Stop the default browser action
  25881. event.preventDefault = function () {
  25882. if (old.preventDefault) {
  25883. old.preventDefault();
  25884. }
  25885. event.returnValue = false;
  25886. old.returnValue = false;
  25887. event.defaultPrevented = true;
  25888. };
  25889. event.defaultPrevented = false;
  25890. // Stop the event from bubbling
  25891. event.stopPropagation = function () {
  25892. if (old.stopPropagation) {
  25893. old.stopPropagation();
  25894. }
  25895. event.cancelBubble = true;
  25896. old.cancelBubble = true;
  25897. event.isPropagationStopped = returnTrue;
  25898. };
  25899. event.isPropagationStopped = returnFalse;
  25900. // Stop the event from bubbling and executing other handlers
  25901. event.stopImmediatePropagation = function () {
  25902. if (old.stopImmediatePropagation) {
  25903. old.stopImmediatePropagation();
  25904. }
  25905. event.isImmediatePropagationStopped = returnTrue;
  25906. event.stopPropagation();
  25907. };
  25908. event.isImmediatePropagationStopped = returnFalse;
  25909. // Handle mouse position
  25910. if (event.clientX !== null && event.clientX !== undefined) {
  25911. var doc = _document2['default'].documentElement;
  25912. var body = _document2['default'].body;
  25913. event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
  25914. event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
  25915. }
  25916. // Handle key presses
  25917. event.which = event.charCode || event.keyCode;
  25918. // Fix button for mouse clicks:
  25919. // 0 == left; 1 == middle; 2 == right
  25920. if (event.button !== null && event.button !== undefined) {
  25921. // The following is disabled because it does not pass videojs-standard
  25922. // and... yikes.
  25923. /* eslint-disable */
  25924. event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
  25925. /* eslint-enable */
  25926. }
  25927. }
  25928. // Returns fixed-up instance
  25929. return event;
  25930. }
  25931. /**
  25932. * Whether passive event listeners are supported
  25933. */
  25934. var _supportsPassive = false;
  25935. (function () {
  25936. try {
  25937. var opts = Object.defineProperty({}, 'passive', {
  25938. get: function get() {
  25939. _supportsPassive = true;
  25940. }
  25941. });
  25942. _window2['default'].addEventListener('test', null, opts);
  25943. } catch (e) {
  25944. // disregard
  25945. }
  25946. })();
  25947. /**
  25948. * Touch events Chrome expects to be passive
  25949. */
  25950. var passiveEvents = ['touchstart', 'touchmove'];
  25951. /**
  25952. * Add an event listener to element
  25953. * It stores the handler function in a separate cache object
  25954. * and adds a generic handler to the element's event,
  25955. * along with a unique id (guid) to the element.
  25956. *
  25957. * @param {Element|Object} elem
  25958. * Element or object to bind listeners to
  25959. *
  25960. * @param {string|string[]} type
  25961. * Type of event to bind to.
  25962. *
  25963. * @param {EventTarget~EventListener} fn
  25964. * Event listener.
  25965. */
  25966. function on(elem, type, fn) {
  25967. if (Array.isArray(type)) {
  25968. return _handleMultipleEvents(on, elem, type, fn);
  25969. }
  25970. var data = Dom.getElData(elem);
  25971. // We need a place to store all our handler data
  25972. if (!data.handlers) {
  25973. data.handlers = {};
  25974. }
  25975. if (!data.handlers[type]) {
  25976. data.handlers[type] = [];
  25977. }
  25978. if (!fn.guid) {
  25979. fn.guid = Guid.newGUID();
  25980. }
  25981. data.handlers[type].push(fn);
  25982. if (!data.dispatcher) {
  25983. data.disabled = false;
  25984. data.dispatcher = function (event, hash) {
  25985. if (data.disabled) {
  25986. return;
  25987. }
  25988. event = fixEvent(event);
  25989. var handlers = data.handlers[event.type];
  25990. if (handlers) {
  25991. // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
  25992. var handlersCopy = handlers.slice(0);
  25993. for (var m = 0, n = handlersCopy.length; m < n; m++) {
  25994. if (event.isImmediatePropagationStopped()) {
  25995. break;
  25996. } else {
  25997. try {
  25998. handlersCopy[m].call(elem, event, hash);
  25999. } catch (e) {
  26000. _log2['default'].error(e);
  26001. }
  26002. }
  26003. }
  26004. }
  26005. };
  26006. }
  26007. if (data.handlers[type].length === 1) {
  26008. if (elem.addEventListener) {
  26009. var options = false;
  26010. if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
  26011. options = { passive: true };
  26012. }
  26013. elem.addEventListener(type, data.dispatcher, options);
  26014. } else if (elem.attachEvent) {
  26015. elem.attachEvent('on' + type, data.dispatcher);
  26016. }
  26017. }
  26018. }
  26019. /**
  26020. * Removes event listeners from an element
  26021. *
  26022. * @param {Element|Object} elem
  26023. * Object to remove listeners from.
  26024. *
  26025. * @param {string|string[]} [type]
  26026. * Type of listener to remove. Don't include to remove all events from element.
  26027. *
  26028. * @param {EventTarget~EventListener} [fn]
  26029. * Specific listener to remove. Don't include to remove listeners for an event
  26030. * type.
  26031. */
  26032. function off(elem, type, fn) {
  26033. // Don't want to add a cache object through getElData if not needed
  26034. if (!Dom.hasElData(elem)) {
  26035. return;
  26036. }
  26037. var data = Dom.getElData(elem);
  26038. // If no events exist, nothing to unbind
  26039. if (!data.handlers) {
  26040. return;
  26041. }
  26042. if (Array.isArray(type)) {
  26043. return _handleMultipleEvents(off, elem, type, fn);
  26044. }
  26045. // Utility function
  26046. var removeType = function removeType(t) {
  26047. data.handlers[t] = [];
  26048. _cleanUpEvents(elem, t);
  26049. };
  26050. // Are we removing all bound events?
  26051. if (!type) {
  26052. for (var t in data.handlers) {
  26053. removeType(t);
  26054. }
  26055. return;
  26056. }
  26057. var handlers = data.handlers[type];
  26058. // If no handlers exist, nothing to unbind
  26059. if (!handlers) {
  26060. return;
  26061. }
  26062. // If no listener was provided, remove all listeners for type
  26063. if (!fn) {
  26064. removeType(type);
  26065. return;
  26066. }
  26067. // We're only removing a single handler
  26068. if (fn.guid) {
  26069. for (var n = 0; n < handlers.length; n++) {
  26070. if (handlers[n].guid === fn.guid) {
  26071. handlers.splice(n--, 1);
  26072. }
  26073. }
  26074. }
  26075. _cleanUpEvents(elem, type);
  26076. }
  26077. /**
  26078. * Trigger an event for an element
  26079. *
  26080. * @param {Element|Object} elem
  26081. * Element to trigger an event on
  26082. *
  26083. * @param {EventTarget~Event|string} event
  26084. * A string (the type) or an event object with a type attribute
  26085. *
  26086. * @param {Object} [hash]
  26087. * data hash to pass along with the event
  26088. *
  26089. * @return {boolean|undefined}
  26090. * - Returns the opposite of `defaultPrevented` if default was prevented
  26091. * - Otherwise returns undefined
  26092. */
  26093. function trigger(elem, event, hash) {
  26094. // Fetches element data and a reference to the parent (for bubbling).
  26095. // Don't want to add a data object to cache for every parent,
  26096. // so checking hasElData first.
  26097. var elemData = Dom.hasElData(elem) ? Dom.getElData(elem) : {};
  26098. var parent = elem.parentNode || elem.ownerDocument;
  26099. // type = event.type || event,
  26100. // handler;
  26101. // If an event name was passed as a string, creates an event out of it
  26102. if (typeof event === 'string') {
  26103. event = { type: event, target: elem };
  26104. }
  26105. // Normalizes the event properties.
  26106. event = fixEvent(event);
  26107. // If the passed element has a dispatcher, executes the established handlers.
  26108. if (elemData.dispatcher) {
  26109. elemData.dispatcher.call(elem, event, hash);
  26110. }
  26111. // Unless explicitly stopped or the event does not bubble (e.g. media events)
  26112. // recursively calls this function to bubble the event up the DOM.
  26113. if (parent && !event.isPropagationStopped() && event.bubbles === true) {
  26114. trigger.call(null, parent, event, hash);
  26115. // If at the top of the DOM, triggers the default action unless disabled.
  26116. } else if (!parent && !event.defaultPrevented) {
  26117. var targetData = Dom.getElData(event.target);
  26118. // Checks if the target has a default action for this event.
  26119. if (event.target[event.type]) {
  26120. // Temporarily disables event dispatching on the target as we have already executed the handler.
  26121. targetData.disabled = true;
  26122. // Executes the default action.
  26123. if (typeof event.target[event.type] === 'function') {
  26124. event.target[event.type]();
  26125. }
  26126. // Re-enables event dispatching.
  26127. targetData.disabled = false;
  26128. }
  26129. }
  26130. // Inform the triggerer if the default was prevented by returning false
  26131. return !event.defaultPrevented;
  26132. }
  26133. /**
  26134. * Trigger a listener only once for an event
  26135. *
  26136. * @param {Element|Object} elem
  26137. * Element or object to bind to.
  26138. *
  26139. * @param {string|string[]} type
  26140. * Name/type of event
  26141. *
  26142. * @param {Event~EventListener} fn
  26143. * Event Listener function
  26144. */
  26145. function one(elem, type, fn) {
  26146. if (Array.isArray(type)) {
  26147. return _handleMultipleEvents(one, elem, type, fn);
  26148. }
  26149. var func = function func() {
  26150. off(elem, type, func);
  26151. fn.apply(this, arguments);
  26152. };
  26153. // copy the guid to the new function so it can removed using the original function's ID
  26154. func.guid = fn.guid = fn.guid || Guid.newGUID();
  26155. on(elem, type, func);
  26156. }
  26157. },{"./dom.js":123,"./guid.js":127,"./log.js":128,"global/document":136,"global/window":137}],125:[function(require,module,exports){
  26158. 'use strict';
  26159. exports.__esModule = true;
  26160. exports.throttle = exports.bind = undefined;
  26161. var _guid = require('./guid.js');
  26162. /**
  26163. * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
  26164. * It also stores a unique id on the function so it can be easily removed from events.
  26165. *
  26166. * @param {Mixed} context
  26167. * The object to bind as scope.
  26168. *
  26169. * @param {Function} fn
  26170. * The function to be bound to a scope.
  26171. *
  26172. * @param {number} [uid]
  26173. * An optional unique ID for the function to be set
  26174. *
  26175. * @return {Function}
  26176. * The new function that will be bound into the context given
  26177. */
  26178. var bind = exports.bind = function bind(context, fn, uid) {
  26179. // Make sure the function has a unique ID
  26180. if (!fn.guid) {
  26181. fn.guid = (0, _guid.newGUID)();
  26182. }
  26183. // Create the new function that changes the context
  26184. var bound = function bound() {
  26185. return fn.apply(context, arguments);
  26186. };
  26187. // Allow for the ability to individualize this function
  26188. // Needed in the case where multiple objects might share the same prototype
  26189. // IF both items add an event listener with the same function, then you try to remove just one
  26190. // it will remove both because they both have the same guid.
  26191. // when using this, you need to use the bind method when you remove the listener as well.
  26192. // currently used in text tracks
  26193. bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
  26194. return bound;
  26195. };
  26196. /**
  26197. * Wraps the given function, `fn`, with a new function that only invokes `fn`
  26198. * at most once per every `wait` milliseconds.
  26199. *
  26200. * @param {Function} fn
  26201. * The function to be throttled.
  26202. *
  26203. * @param {Number} wait
  26204. * The number of milliseconds by which to throttle.
  26205. *
  26206. * @return {Function}
  26207. */
  26208. /**
  26209. * @file fn.js
  26210. * @module fn
  26211. */
  26212. var throttle = exports.throttle = function throttle(fn, wait) {
  26213. var last = Date.now();
  26214. var throttled = function throttled() {
  26215. var now = Date.now();
  26216. if (now - last >= wait) {
  26217. fn.apply(undefined, arguments);
  26218. last = now;
  26219. }
  26220. };
  26221. return throttled;
  26222. };
  26223. },{"./guid.js":127}],126:[function(require,module,exports){
  26224. 'use strict';
  26225. exports.__esModule = true;
  26226. /**
  26227. * @file format-time.js
  26228. * @module Format-time
  26229. */
  26230. /**
  26231. * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
  26232. * will force a number of leading zeros to cover the length of the guide.
  26233. *
  26234. * @param {number} seconds
  26235. * Number of seconds to be turned into a string
  26236. *
  26237. * @param {number} guide
  26238. * Number (in seconds) to model the string after
  26239. *
  26240. * @return {string}
  26241. * Time formatted as H:MM:SS or M:SS
  26242. */
  26243. function formatTime(seconds) {
  26244. var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
  26245. seconds = seconds < 0 ? 0 : seconds;
  26246. var s = Math.floor(seconds % 60);
  26247. var m = Math.floor(seconds / 60 % 60);
  26248. var h = Math.floor(seconds / 3600);
  26249. var gm = Math.floor(guide / 60 % 60);
  26250. var gh = Math.floor(guide / 3600);
  26251. // handle invalid times
  26252. if (isNaN(seconds) || seconds === Infinity) {
  26253. // '-' is false for all relational operators (e.g. <, >=) so this setting
  26254. // will add the minimum number of fields specified by the guide
  26255. h = m = s = '-';
  26256. }
  26257. // Check if we need to show hours
  26258. h = h > 0 || gh > 0 ? h + ':' : '';
  26259. // If hours are showing, we may need to add a leading zero.
  26260. // Always show at least one digit of minutes.
  26261. m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
  26262. // Check if leading zero is need for seconds
  26263. s = s < 10 ? '0' + s : s;
  26264. return h + m + s;
  26265. }
  26266. exports['default'] = formatTime;
  26267. },{}],127:[function(require,module,exports){
  26268. "use strict";
  26269. exports.__esModule = true;
  26270. exports.newGUID = newGUID;
  26271. /**
  26272. * @file guid.js
  26273. * @module guid
  26274. */
  26275. /**
  26276. * Unique ID for an element or function
  26277. * @type {Number}
  26278. */
  26279. var _guid = 1;
  26280. /**
  26281. * Get a unique auto-incrementing ID by number that has not been returned before.
  26282. *
  26283. * @return {number}
  26284. * A new unique ID.
  26285. */
  26286. function newGUID() {
  26287. return _guid++;
  26288. }
  26289. },{}],128:[function(require,module,exports){
  26290. 'use strict';
  26291. exports.__esModule = true;
  26292. exports.logByType = undefined;
  26293. var _window = require('global/window');
  26294. var _window2 = _interopRequireDefault(_window);
  26295. var _browser = require('./browser');
  26296. var _obj = require('./obj');
  26297. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  26298. var log = void 0;
  26299. /**
  26300. * Log messages to the console and history based on the type of message
  26301. *
  26302. * @param {string} type
  26303. * The name of the console method to use.
  26304. *
  26305. * @param {Array} args
  26306. * The arguments to be passed to the matching console method.
  26307. *
  26308. * @param {boolean} [stringify]
  26309. * By default, only old IEs should get console argument stringification,
  26310. * but this is exposed as a parameter to facilitate testing.
  26311. */
  26312. /**
  26313. * @file log.js
  26314. * @module log
  26315. */
  26316. var logByType = exports.logByType = function logByType(type, args) {
  26317. var stringify = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : !!_browser.IE_VERSION && _browser.IE_VERSION < 11;
  26318. if (type !== 'log') {
  26319. // add the type to the front of the message when it's not "log"
  26320. args.unshift(type.toUpperCase() + ':');
  26321. }
  26322. // add to history
  26323. log.history.push(args);
  26324. // add console prefix after adding to history
  26325. args.unshift('VIDEOJS:');
  26326. // If there's no console then don't try to output messages, but they will
  26327. // still be stored in `log.history`.
  26328. //
  26329. // Was setting these once outside of this function, but containing them
  26330. // in the function makes it easier to test cases where console doesn't exist
  26331. // when the module is executed.
  26332. var fn = _window2['default'].console && _window2['default'].console[type];
  26333. // Bail out if there's no console.
  26334. if (!fn) {
  26335. return;
  26336. }
  26337. // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify
  26338. // objects and arrays for those less-capable browsers.
  26339. if (stringify) {
  26340. args = args.map(function (a) {
  26341. if ((0, _obj.isObject)(a) || Array.isArray(a)) {
  26342. try {
  26343. return JSON.stringify(a);
  26344. } catch (x) {
  26345. return String(a);
  26346. }
  26347. }
  26348. // Cast to string before joining, so we get null and undefined explicitly
  26349. // included in output (as we would in a modern console).
  26350. return String(a);
  26351. }).join(' ');
  26352. }
  26353. // Old IE versions do not allow .apply() for console methods (they are
  26354. // reported as objects rather than functions).
  26355. if (!fn.apply) {
  26356. fn(args);
  26357. } else {
  26358. fn[Array.isArray(args) ? 'apply' : 'call'](_window2['default'].console, args);
  26359. }
  26360. };
  26361. /**
  26362. * Log plain debug messages
  26363. *
  26364. * @param {Mixed[]} args
  26365. * One or more messages or objects that should be logged.
  26366. */
  26367. log = function log() {
  26368. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  26369. args[_key] = arguments[_key];
  26370. }
  26371. logByType('log', args);
  26372. };
  26373. /**
  26374. * Keep a history of log messages
  26375. *
  26376. * @type {Array}
  26377. */
  26378. log.history = [];
  26379. /**
  26380. * Log error messages
  26381. *
  26382. * @param {Mixed[]} args
  26383. * One or more messages or objects that should be logged as an error
  26384. */
  26385. log.error = function () {
  26386. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  26387. args[_key2] = arguments[_key2];
  26388. }
  26389. return logByType('error', args);
  26390. };
  26391. /**
  26392. * Log warning messages
  26393. *
  26394. * @param {Mixed[]} args
  26395. * One or more messages or objects that should be logged as a warning.
  26396. */
  26397. log.warn = function () {
  26398. for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  26399. args[_key3] = arguments[_key3];
  26400. }
  26401. return logByType('warn', args);
  26402. };
  26403. exports['default'] = log;
  26404. },{"./browser":120,"./obj":130,"global/window":137}],129:[function(require,module,exports){
  26405. 'use strict';
  26406. exports.__esModule = true;
  26407. exports['default'] = mergeOptions;
  26408. var _obj = require('./obj');
  26409. /**
  26410. * Deep-merge one or more options objects, recursively merging **only** plain
  26411. * object properties.
  26412. *
  26413. * @param {Object[]} sources
  26414. * One or more objects to merge into a new object.
  26415. *
  26416. * @returns {Object}
  26417. * A new object that is the merged result of all sources.
  26418. */
  26419. function mergeOptions() {
  26420. var result = {};
  26421. for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
  26422. sources[_key] = arguments[_key];
  26423. }
  26424. sources.forEach(function (source) {
  26425. if (!source) {
  26426. return;
  26427. }
  26428. (0, _obj.each)(source, function (value, key) {
  26429. if (!(0, _obj.isPlain)(value)) {
  26430. result[key] = value;
  26431. return;
  26432. }
  26433. if (!(0, _obj.isPlain)(result[key])) {
  26434. result[key] = {};
  26435. }
  26436. result[key] = mergeOptions(result[key], value);
  26437. });
  26438. });
  26439. return result;
  26440. } /**
  26441. * @file merge-options.js
  26442. * @module merge-options
  26443. */
  26444. },{"./obj":130}],130:[function(require,module,exports){
  26445. 'use strict';
  26446. exports.__esModule = true;
  26447. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  26448. exports.each = each;
  26449. exports.reduce = reduce;
  26450. exports.assign = assign;
  26451. exports.isObject = isObject;
  26452. exports.isPlain = isPlain;
  26453. /**
  26454. * @file obj.js
  26455. * @module obj
  26456. */
  26457. /**
  26458. * @callback obj:EachCallback
  26459. *
  26460. * @param {Mixed} value
  26461. * The current key for the object that is being iterated over.
  26462. *
  26463. * @param {string} key
  26464. * The current key-value for object that is being iterated over
  26465. */
  26466. /**
  26467. * @callback obj:ReduceCallback
  26468. *
  26469. * @param {Mixed} accum
  26470. * The value that is accumulating over the reduce loop.
  26471. *
  26472. * @param {Mixed} value
  26473. * The current key for the object that is being iterated over.
  26474. *
  26475. * @param {string} key
  26476. * The current key-value for object that is being iterated over
  26477. *
  26478. * @return {Mixed}
  26479. * The new accumulated value.
  26480. */
  26481. var toString = Object.prototype.toString;
  26482. /**
  26483. * Get the keys of an Object
  26484. *
  26485. * @param {Object}
  26486. * The Object to get the keys from
  26487. *
  26488. * @return {string[]}
  26489. * An array of the keys from the object. Returns an empty array if the
  26490. * object passed in was invalid or had no keys.
  26491. *
  26492. * @private
  26493. */
  26494. var keys = function keys(object) {
  26495. return isObject(object) ? Object.keys(object) : [];
  26496. };
  26497. /**
  26498. * Array-like iteration for objects.
  26499. *
  26500. * @param {Object} object
  26501. * The object to iterate over
  26502. *
  26503. * @param {obj:EachCallback} fn
  26504. * The callback function which is called for each key in the object.
  26505. */
  26506. function each(object, fn) {
  26507. keys(object).forEach(function (key) {
  26508. return fn(object[key], key);
  26509. });
  26510. }
  26511. /**
  26512. * Array-like reduce for objects.
  26513. *
  26514. * @param {Object} object
  26515. * The Object that you want to reduce.
  26516. *
  26517. * @param {Function} fn
  26518. * A callback function which is called for each key in the object. It
  26519. * receives the accumulated value and the per-iteration value and key
  26520. * as arguments.
  26521. *
  26522. * @param {Mixed} [initial = 0]
  26523. * Starting value
  26524. *
  26525. * @return {Mixed}
  26526. * The final accumulated value.
  26527. */
  26528. function reduce(object, fn) {
  26529. var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  26530. return keys(object).reduce(function (accum, key) {
  26531. return fn(accum, object[key], key);
  26532. }, initial);
  26533. }
  26534. /**
  26535. * Object.assign-style object shallow merge/extend.
  26536. *
  26537. * @param {Object} target
  26538. * @param {Object} ...sources
  26539. * @return {Object}
  26540. */
  26541. function assign(target) {
  26542. for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  26543. sources[_key - 1] = arguments[_key];
  26544. }
  26545. if (Object.assign) {
  26546. return Object.assign.apply(Object, [target].concat(sources));
  26547. }
  26548. sources.forEach(function (source) {
  26549. if (!source) {
  26550. return;
  26551. }
  26552. each(source, function (value, key) {
  26553. target[key] = value;
  26554. });
  26555. });
  26556. return target;
  26557. }
  26558. /**
  26559. * Returns whether a value is an object of any kind - including DOM nodes,
  26560. * arrays, regular expressions, etc. Not functions, though.
  26561. *
  26562. * This avoids the gotcha where using `typeof` on a `null` value
  26563. * results in `'object'`.
  26564. *
  26565. * @param {Object} value
  26566. * @return {Boolean}
  26567. */
  26568. function isObject(value) {
  26569. return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object';
  26570. }
  26571. /**
  26572. * Returns whether an object appears to be a "plain" object - that is, a
  26573. * direct instance of `Object`.
  26574. *
  26575. * @param {Object} value
  26576. * @return {Boolean}
  26577. */
  26578. function isPlain(value) {
  26579. return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
  26580. }
  26581. },{}],131:[function(require,module,exports){
  26582. 'use strict';
  26583. exports.__esModule = true;
  26584. exports.setTextContent = exports.createStyleElement = undefined;
  26585. var _document = require('global/document');
  26586. var _document2 = _interopRequireDefault(_document);
  26587. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  26588. /**
  26589. * Create a DOM syle element given a className for it.
  26590. *
  26591. * @param {string} className
  26592. * The className to add to the created style element.
  26593. *
  26594. * @return {Element}
  26595. * The element that was created.
  26596. */
  26597. var createStyleElement = exports.createStyleElement = function createStyleElement(className) {
  26598. var style = _document2['default'].createElement('style');
  26599. style.className = className;
  26600. return style;
  26601. };
  26602. /**
  26603. * Add text to a DOM element.
  26604. *
  26605. * @param {Element} el
  26606. * The Element to add text content to.
  26607. *
  26608. * @param {string} content
  26609. * The text to add to the element.
  26610. */
  26611. /**
  26612. * @file stylesheet.js
  26613. * @module stylesheet
  26614. */
  26615. var setTextContent = exports.setTextContent = function setTextContent(el, content) {
  26616. if (el.styleSheet) {
  26617. el.styleSheet.cssText = content;
  26618. } else {
  26619. el.textContent = content;
  26620. }
  26621. };
  26622. },{"global/document":136}],132:[function(require,module,exports){
  26623. 'use strict';
  26624. exports.__esModule = true;
  26625. exports.createTimeRange = undefined;
  26626. exports.createTimeRanges = createTimeRanges;
  26627. var _log = require('./log.js');
  26628. var _log2 = _interopRequireDefault(_log);
  26629. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  26630. /**
  26631. * Returns the time for the specified index at the start or end
  26632. * of a TimeRange object.
  26633. *
  26634. * @function time-ranges:indexFunction
  26635. *
  26636. * @param {number} [index=0]
  26637. * The range number to return the time for.
  26638. *
  26639. * @return {number}
  26640. * The time that offset at the specified index.
  26641. *
  26642. * @depricated index must be set to a value, in the future this will throw an error.
  26643. */
  26644. /**
  26645. * An object that contains ranges of time for various reasons.
  26646. *
  26647. * @typedef {Object} TimeRange
  26648. *
  26649. * @property {number} length
  26650. * The number of time ranges represented by this Object
  26651. *
  26652. * @property {time-ranges:indexFunction} start
  26653. * Returns the time offset at which a specified time range begins.
  26654. *
  26655. * @property {time-ranges:indexFunction} end
  26656. * Returns the time offset at which a specified time range begins.
  26657. *
  26658. * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
  26659. */
  26660. /**
  26661. * Check if any of the time ranges are over the maximum index.
  26662. *
  26663. * @param {string} fnName
  26664. * The function name to use for logging
  26665. *
  26666. * @param {number} index
  26667. * The index to check
  26668. *
  26669. * @param {number} maxIndex
  26670. * The maximum possible index
  26671. *
  26672. * @throws {Error} if the timeRanges provided are over the maxIndex
  26673. */
  26674. function rangeCheck(fnName, index, maxIndex) {
  26675. if (index < 0 || index > maxIndex) {
  26676. throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is greater than or equal to the maximum bound (' + maxIndex + ').');
  26677. }
  26678. }
  26679. /**
  26680. * Check if any of the time ranges are over the maximum index.
  26681. *
  26682. * @param {string} fnName
  26683. * The function name to use for logging
  26684. *
  26685. * @param {string} valueIndex
  26686. * The proprety that should be used to get the time. should be 'start' or 'end'
  26687. *
  26688. * @param {Array} ranges
  26689. * An array of time ranges
  26690. *
  26691. * @param {Array} [rangeIndex=0]
  26692. * The index to start the search at
  26693. *
  26694. * @return {number}
  26695. * The time that offset at the specified index.
  26696. *
  26697. *
  26698. * @depricated rangeIndex must be set to a value, in the future this will throw an error.
  26699. * @throws {Error} if rangeIndex is more than the length of ranges
  26700. */
  26701. /**
  26702. * @file time-ranges.js
  26703. * @module time-ranges
  26704. */
  26705. function getRange(fnName, valueIndex, ranges, rangeIndex) {
  26706. if (rangeIndex === undefined) {
  26707. _log2['default'].warn('DEPRECATED: Function \'' + fnName + '\' on \'TimeRanges\' called without an index argument.');
  26708. rangeIndex = 0;
  26709. }
  26710. rangeCheck(fnName, rangeIndex, ranges.length - 1);
  26711. return ranges[rangeIndex][valueIndex];
  26712. }
  26713. /**
  26714. * Create a time range object givent ranges of time.
  26715. *
  26716. * @param {Array} [ranges]
  26717. * An array of time ranges.
  26718. */
  26719. function createTimeRangesObj(ranges) {
  26720. if (ranges === undefined || ranges.length === 0) {
  26721. return {
  26722. length: 0,
  26723. start: function start() {
  26724. throw new Error('This TimeRanges object is empty');
  26725. },
  26726. end: function end() {
  26727. throw new Error('This TimeRanges object is empty');
  26728. }
  26729. };
  26730. }
  26731. return {
  26732. length: ranges.length,
  26733. start: getRange.bind(null, 'start', 0, ranges),
  26734. end: getRange.bind(null, 'end', 1, ranges)
  26735. };
  26736. }
  26737. /**
  26738. * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
  26739. *
  26740. * @param {number|Array} start
  26741. * The start of a single range or an array of ranges
  26742. *
  26743. * @param {number} end
  26744. * The end of a single range.
  26745. *
  26746. * @private
  26747. */
  26748. function createTimeRanges(start, end) {
  26749. if (Array.isArray(start)) {
  26750. return createTimeRangesObj(start);
  26751. } else if (start === undefined || end === undefined) {
  26752. return createTimeRangesObj();
  26753. }
  26754. return createTimeRangesObj([[start, end]]);
  26755. }
  26756. exports.createTimeRange = createTimeRanges;
  26757. },{"./log.js":128}],133:[function(require,module,exports){
  26758. 'use strict';
  26759. exports.__esModule = true;
  26760. /**
  26761. * @file to-title-case.js
  26762. * @module to-title-case
  26763. */
  26764. /**
  26765. * Uppercase the first letter of a string.
  26766. *
  26767. * @param {string} string
  26768. * String to be uppercased
  26769. *
  26770. * @return {string}
  26771. * The string with an uppercased first letter
  26772. */
  26773. function toTitleCase(string) {
  26774. if (typeof string !== 'string') {
  26775. return string;
  26776. }
  26777. return string.charAt(0).toUpperCase() + string.slice(1);
  26778. }
  26779. exports['default'] = toTitleCase;
  26780. },{}],134:[function(require,module,exports){
  26781. 'use strict';
  26782. exports.__esModule = true;
  26783. exports.isCrossOrigin = exports.getFileExtension = exports.getAbsoluteURL = exports.parseUrl = undefined;
  26784. var _document = require('global/document');
  26785. var _document2 = _interopRequireDefault(_document);
  26786. var _window = require('global/window');
  26787. var _window2 = _interopRequireDefault(_window);
  26788. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  26789. /**
  26790. * @typedef {Object} url:URLObject
  26791. *
  26792. * @property {string} protocol
  26793. * The protocol of the url that was parsed.
  26794. *
  26795. * @property {string} hostname
  26796. * The hostname of the url that was parsed.
  26797. *
  26798. * @property {string} port
  26799. * The port of the url that was parsed.
  26800. *
  26801. * @property {string} pathname
  26802. * The pathname of the url that was parsed.
  26803. *
  26804. * @property {string} search
  26805. * The search query of the url that was parsed.
  26806. *
  26807. * @property {string} hash
  26808. * The hash of the url that was parsed.
  26809. *
  26810. * @property {string} host
  26811. * The host of the url that was parsed.
  26812. */
  26813. /**
  26814. * Resolve and parse the elements of a URL.
  26815. *
  26816. * @param {String} url
  26817. * The url to parse
  26818. *
  26819. * @return {url:URLObject}
  26820. * An object of url details
  26821. */
  26822. /**
  26823. * @file url.js
  26824. * @module url
  26825. */
  26826. var parseUrl = exports.parseUrl = function parseUrl(url) {
  26827. var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];
  26828. // add the url to an anchor and let the browser parse the URL
  26829. var a = _document2['default'].createElement('a');
  26830. a.href = url;
  26831. // IE8 (and 9?) Fix
  26832. // ie8 doesn't parse the URL correctly until the anchor is actually
  26833. // added to the body, and an innerHTML is needed to trigger the parsing
  26834. var addToBody = a.host === '' && a.protocol !== 'file:';
  26835. var div = void 0;
  26836. if (addToBody) {
  26837. div = _document2['default'].createElement('div');
  26838. div.innerHTML = '<a href="' + url + '"></a>';
  26839. a = div.firstChild;
  26840. // prevent the div from affecting layout
  26841. div.setAttribute('style', 'display:none; position:absolute;');
  26842. _document2['default'].body.appendChild(div);
  26843. }
  26844. // Copy the specific URL properties to a new object
  26845. // This is also needed for IE8 because the anchor loses its
  26846. // properties when it's removed from the dom
  26847. var details = {};
  26848. for (var i = 0; i < props.length; i++) {
  26849. details[props[i]] = a[props[i]];
  26850. }
  26851. // IE9 adds the port to the host property unlike everyone else. If
  26852. // a port identifier is added for standard ports, strip it.
  26853. if (details.protocol === 'http:') {
  26854. details.host = details.host.replace(/:80$/, '');
  26855. }
  26856. if (details.protocol === 'https:') {
  26857. details.host = details.host.replace(/:443$/, '');
  26858. }
  26859. if (addToBody) {
  26860. _document2['default'].body.removeChild(div);
  26861. }
  26862. return details;
  26863. };
  26864. /**
  26865. * Get absolute version of relative URL. Used to tell flash correct URL.
  26866. *
  26867. *
  26868. * @param {string} url
  26869. * URL to make absolute
  26870. *
  26871. * @return {string}
  26872. * Absolute URL
  26873. *
  26874. * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
  26875. */
  26876. var getAbsoluteURL = exports.getAbsoluteURL = function getAbsoluteURL(url) {
  26877. // Check if absolute URL
  26878. if (!url.match(/^https?:\/\//)) {
  26879. // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
  26880. var div = _document2['default'].createElement('div');
  26881. div.innerHTML = '<a href="' + url + '">x</a>';
  26882. url = div.firstChild.href;
  26883. }
  26884. return url;
  26885. };
  26886. /**
  26887. * Returns the extension of the passed file name. It will return an empty string
  26888. * if passed an invalid path.
  26889. *
  26890. * @param {string} path
  26891. * The fileName path like '/path/to/file.mp4'
  26892. *
  26893. * @returns {string}
  26894. * The extension in lower case or an empty string if no
  26895. * extension could be found.
  26896. */
  26897. var getFileExtension = exports.getFileExtension = function getFileExtension(path) {
  26898. if (typeof path === 'string') {
  26899. var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
  26900. var pathParts = splitPathRe.exec(path);
  26901. if (pathParts) {
  26902. return pathParts.pop().toLowerCase();
  26903. }
  26904. }
  26905. return '';
  26906. };
  26907. /**
  26908. * Returns whether the url passed is a cross domain request or not.
  26909. *
  26910. * @param {string} url
  26911. * The url to check.
  26912. *
  26913. * @return {boolean}
  26914. * Whether it is a cross domain request or not.
  26915. */
  26916. var isCrossOrigin = exports.isCrossOrigin = function isCrossOrigin(url) {
  26917. var winLoc = _window2['default'].location;
  26918. var urlInfo = parseUrl(url);
  26919. // IE8 protocol relative urls will return ':' for protocol
  26920. var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
  26921. // Check if url is for another domain/origin
  26922. // IE8 doesn't know location.origin, so we won't rely on it here
  26923. var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
  26924. return crossOrigin;
  26925. };
  26926. },{"global/document":136,"global/window":137}],135:[function(require,module,exports){
  26927. 'use strict';
  26928. exports.__esModule = true;
  26929. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /**
  26930. * @file video.js
  26931. * @module videojs
  26932. */
  26933. /* global define */
  26934. // Include the built-in techs
  26935. var _window = require('global/window');
  26936. var _window2 = _interopRequireDefault(_window);
  26937. var _document = require('global/document');
  26938. var _document2 = _interopRequireDefault(_document);
  26939. var _browser = require('./utils/browser.js');
  26940. var browser = _interopRequireWildcard(_browser);
  26941. var _dom = require('./utils/dom.js');
  26942. var Dom = _interopRequireWildcard(_dom);
  26943. var _setup = require('./setup');
  26944. var setup = _interopRequireWildcard(_setup);
  26945. var _stylesheet = require('./utils/stylesheet.js');
  26946. var stylesheet = _interopRequireWildcard(_stylesheet);
  26947. var _component = require('./component');
  26948. var _component2 = _interopRequireDefault(_component);
  26949. var _eventTarget = require('./event-target');
  26950. var _eventTarget2 = _interopRequireDefault(_eventTarget);
  26951. var _events = require('./utils/events.js');
  26952. var Events = _interopRequireWildcard(_events);
  26953. var _player = require('./player');
  26954. var _player2 = _interopRequireDefault(_player);
  26955. var _plugins = require('./plugins.js');
  26956. var _plugins2 = _interopRequireDefault(_plugins);
  26957. var _mergeOptions2 = require('./utils/merge-options.js');
  26958. var _mergeOptions3 = _interopRequireDefault(_mergeOptions2);
  26959. var _fn = require('./utils/fn.js');
  26960. var Fn = _interopRequireWildcard(_fn);
  26961. var _textTrack = require('./tracks/text-track.js');
  26962. var _textTrack2 = _interopRequireDefault(_textTrack);
  26963. var _audioTrack = require('./tracks/audio-track.js');
  26964. var _audioTrack2 = _interopRequireDefault(_audioTrack);
  26965. var _videoTrack = require('./tracks/video-track.js');
  26966. var _videoTrack2 = _interopRequireDefault(_videoTrack);
  26967. var _timeRanges = require('./utils/time-ranges.js');
  26968. var _formatTime = require('./utils/format-time.js');
  26969. var _formatTime2 = _interopRequireDefault(_formatTime);
  26970. var _log = require('./utils/log.js');
  26971. var _log2 = _interopRequireDefault(_log);
  26972. var _url = require('./utils/url.js');
  26973. var Url = _interopRequireWildcard(_url);
  26974. var _obj = require('./utils/obj');
  26975. var _computedStyle = require('./utils/computed-style.js');
  26976. var _computedStyle2 = _interopRequireDefault(_computedStyle);
  26977. var _extend = require('./extend.js');
  26978. var _extend2 = _interopRequireDefault(_extend);
  26979. var _xhr = require('xhr');
  26980. var _xhr2 = _interopRequireDefault(_xhr);
  26981. var _tech = require('./tech/tech.js');
  26982. var _tech2 = _interopRequireDefault(_tech);
  26983. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
  26984. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
  26985. // HTML5 Element Shim for IE8
  26986. if (typeof HTMLVideoElement === 'undefined' && Dom.isReal()) {
  26987. _document2['default'].createElement('video');
  26988. _document2['default'].createElement('audio');
  26989. _document2['default'].createElement('track');
  26990. }
  26991. /**
  26992. * Doubles as the main function for users to create a player instance and also
  26993. * the main library object.
  26994. * The `videojs` function can be used to initialize or retrieve a player.
  26995. *
  26996. * @param {string|Element} id
  26997. * Video element or video element ID
  26998. *
  26999. * @param {Object} [options]
  27000. * Optional options object for config/settings
  27001. *
  27002. * @param {Component~ReadyCallback} [ready]
  27003. * Optional ready callback
  27004. *
  27005. * @return {Player}
  27006. * A player instance
  27007. *
  27008. * @mixes videojs
  27009. */
  27010. function videojs(id, options, ready) {
  27011. var tag = void 0;
  27012. // Allow for element or ID to be passed in
  27013. // String ID
  27014. if (typeof id === 'string') {
  27015. // Adjust for jQuery ID syntax
  27016. if (id.indexOf('#') === 0) {
  27017. id = id.slice(1);
  27018. }
  27019. // If a player instance has already been created for this ID return it.
  27020. if (videojs.getPlayers()[id]) {
  27021. // If options or ready funtion are passed, warn
  27022. if (options) {
  27023. _log2['default'].warn('Player "' + id + '" is already initialised. Options will not be applied.');
  27024. }
  27025. if (ready) {
  27026. videojs.getPlayers()[id].ready(ready);
  27027. }
  27028. return videojs.getPlayers()[id];
  27029. }
  27030. // Otherwise get element for ID
  27031. tag = Dom.getEl(id);
  27032. // ID is a media element
  27033. } else {
  27034. tag = id;
  27035. }
  27036. // Check for a useable element
  27037. // re: nodeName, could be a box div also
  27038. if (!tag || !tag.nodeName) {
  27039. throw new TypeError('The element or ID supplied is not valid. (videojs)');
  27040. }
  27041. // Element may have a player attr referring to an already created player instance.
  27042. // If so return that otherwise set up a new player below
  27043. if (tag.player || _player2['default'].players[tag.playerId]) {
  27044. return tag.player || _player2['default'].players[tag.playerId];
  27045. }
  27046. options = options || {};
  27047. videojs.hooks('beforesetup').forEach(function (hookFunction) {
  27048. var opts = hookFunction(tag, (0, _mergeOptions3['default'])(options));
  27049. if (!(0, _obj.isObject)(opts) || Array.isArray(opts)) {
  27050. _log2['default'].error('please return an object in beforesetup hooks');
  27051. return;
  27052. }
  27053. options = (0, _mergeOptions3['default'])(options, opts);
  27054. });
  27055. var PlayerComponent = _component2['default'].getComponent('Player');
  27056. // If not, set up a new player
  27057. var player = new PlayerComponent(tag, options, ready);
  27058. videojs.hooks('setup').forEach(function (hookFunction) {
  27059. return hookFunction(player);
  27060. });
  27061. return player;
  27062. }
  27063. /**
  27064. * An Object that contains lifecycle hooks as keys which point to an array
  27065. * of functions that are run when a lifecycle is triggered
  27066. */
  27067. videojs.hooks_ = {};
  27068. /**
  27069. * Get a list of hooks for a specific lifecycle
  27070. *
  27071. * @param {string} type
  27072. * the lifecyle to get hooks from
  27073. *
  27074. * @param {Function} [fn]
  27075. * Optionally add a hook to the lifecycle that your are getting.
  27076. *
  27077. * @return {Array}
  27078. * an array of hooks, or an empty array if there are none.
  27079. */
  27080. videojs.hooks = function (type, fn) {
  27081. videojs.hooks_[type] = videojs.hooks_[type] || [];
  27082. if (fn) {
  27083. videojs.hooks_[type] = videojs.hooks_[type].concat(fn);
  27084. }
  27085. return videojs.hooks_[type];
  27086. };
  27087. /**
  27088. * Add a function hook to a specific videojs lifecycle.
  27089. *
  27090. * @param {string} type
  27091. * the lifecycle to hook the function to.
  27092. *
  27093. * @param {Function|Function[]}
  27094. * The function or array of functions to attach.
  27095. */
  27096. videojs.hook = function (type, fn) {
  27097. videojs.hooks(type, fn);
  27098. };
  27099. /**
  27100. * Remove a hook from a specific videojs lifecycle.
  27101. *
  27102. * @param {string} type
  27103. * the lifecycle that the function hooked to
  27104. *
  27105. * @param {Function} fn
  27106. * The hooked function to remove
  27107. *
  27108. * @return {boolean}
  27109. * The function that was removed or undef
  27110. */
  27111. videojs.removeHook = function (type, fn) {
  27112. var index = videojs.hooks(type).indexOf(fn);
  27113. if (index <= -1) {
  27114. return false;
  27115. }
  27116. videojs.hooks_[type] = videojs.hooks_[type].slice();
  27117. videojs.hooks_[type].splice(index, 1);
  27118. return true;
  27119. };
  27120. // Add default styles
  27121. if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true && Dom.isReal()) {
  27122. var style = Dom.$('.vjs-styles-defaults');
  27123. if (!style) {
  27124. style = stylesheet.createStyleElement('vjs-styles-defaults');
  27125. var head = Dom.$('head');
  27126. if (head) {
  27127. head.insertBefore(style, head.firstChild);
  27128. }
  27129. stylesheet.setTextContent(style, '\n .video-js {\n width: 300px;\n height: 150px;\n }\n\n .vjs-fluid {\n padding-top: 56.25%\n }\n ');
  27130. }
  27131. }
  27132. // Run Auto-load players
  27133. // You have to wait at least once in case this script is loaded after your
  27134. // video in the DOM (weird behavior only with minified version)
  27135. setup.autoSetupTimeout(1, videojs);
  27136. /**
  27137. * Current software version. Follows semver.
  27138. *
  27139. * @type {string}
  27140. */
  27141. videojs.VERSION = '5.20.3';
  27142. /**
  27143. * The global options object. These are the settings that take effect
  27144. * if no overrides are specified when the player is created.
  27145. *
  27146. * @type {Object}
  27147. */
  27148. videojs.options = _player2['default'].prototype.options_;
  27149. /**
  27150. * Get an object with the currently created players, keyed by player ID
  27151. *
  27152. * @return {Object}
  27153. * The created players
  27154. */
  27155. videojs.getPlayers = function () {
  27156. return _player2['default'].players;
  27157. };
  27158. /**
  27159. * Expose players object.
  27160. *
  27161. * @memberOf videojs
  27162. * @property {Object} players
  27163. */
  27164. videojs.players = _player2['default'].players;
  27165. /**
  27166. * Get a component class object by name
  27167. *
  27168. * @borrows Component.getComponent as videojs.getComponent
  27169. */
  27170. videojs.getComponent = _component2['default'].getComponent;
  27171. /**
  27172. * Register a component so it can referred to by name. Used when adding to other
  27173. * components, either through addChild `component.addChild('myComponent')` or through
  27174. * default children options `{ children: ['myComponent'] }`.
  27175. *
  27176. * > NOTE: You could also just initialize the component before adding.
  27177. * `component.addChild(new MyComponent());`
  27178. *
  27179. * @param {string} name
  27180. * The class name of the component
  27181. *
  27182. * @param {Component} comp
  27183. * The component class
  27184. *
  27185. * @return {Component}
  27186. * The newly registered component
  27187. */
  27188. videojs.registerComponent = function (name, comp) {
  27189. if (_tech2['default'].isTech(comp)) {
  27190. _log2['default'].warn('The ' + name + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
  27191. }
  27192. _component2['default'].registerComponent.call(_component2['default'], name, comp);
  27193. };
  27194. /**
  27195. * Get a Tech class object by name
  27196. *
  27197. * @borrows Tech.getTech as videojs.getTech
  27198. */
  27199. videojs.getTech = _tech2['default'].getTech;
  27200. /**
  27201. * Register a Tech so it can referred to by name.
  27202. * This is used in the tech order for the player.
  27203. *
  27204. * @borrows Tech.registerTech as videojs.registerTech
  27205. */
  27206. videojs.registerTech = _tech2['default'].registerTech;
  27207. /**
  27208. * A suite of browser and device tests from {@link browser}.
  27209. *
  27210. * @type {Object}
  27211. * @private
  27212. */
  27213. videojs.browser = browser;
  27214. /**
  27215. * Whether or not the browser supports touch events. Included for backward
  27216. * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
  27217. * instead going forward.
  27218. *
  27219. * @deprecated since version 5.0
  27220. * @type {boolean}
  27221. */
  27222. videojs.TOUCH_ENABLED = browser.TOUCH_ENABLED;
  27223. /**
  27224. * Subclass an existing class
  27225. * Mimics ES6 subclassing with the `extend` keyword
  27226. *
  27227. * @borrows extend:extendFn as videojs.extend
  27228. */
  27229. videojs.extend = _extend2['default'];
  27230. /**
  27231. * Merge two options objects recursively
  27232. * Performs a deep merge like lodash.merge but **only merges plain objects**
  27233. * (not arrays, elements, anything else)
  27234. * Other values will be copied directly from the second object.
  27235. *
  27236. * @borrows merge-options:mergeOptions as videojs.mergeOptions
  27237. */
  27238. videojs.mergeOptions = _mergeOptions3['default'];
  27239. /**
  27240. * Change the context (this) of a function
  27241. *
  27242. * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
  27243. * `function() {}.bind(newContext);` instead of this.
  27244. *
  27245. * @borrows fn:bind as videojs.bind
  27246. */
  27247. videojs.bind = Fn.bind;
  27248. /**
  27249. * Create a Video.js player plugin.
  27250. * Plugins are only initialized when options for the plugin are included
  27251. * in the player options, or the plugin function on the player instance is
  27252. * called.
  27253. *
  27254. * @borrows plugin:plugin as videojs.plugin
  27255. */
  27256. videojs.plugin = _plugins2['default'];
  27257. /**
  27258. * Adding languages so that they're available to all players.
  27259. * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
  27260. *
  27261. * @param {string} code
  27262. * The language code or dictionary property
  27263. *
  27264. * @param {Object} data
  27265. * The data values to be translated
  27266. *
  27267. * @return {Object}
  27268. * The resulting language dictionary object
  27269. */
  27270. videojs.addLanguage = function (code, data) {
  27271. var _mergeOptions;
  27272. code = ('' + code).toLowerCase();
  27273. videojs.options.languages = (0, _mergeOptions3['default'])(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
  27274. return videojs.options.languages[code];
  27275. };
  27276. /**
  27277. * Log messages
  27278. *
  27279. * @borrows log:log as videojs.log
  27280. */
  27281. videojs.log = _log2['default'];
  27282. /**
  27283. * Creates an emulated TimeRange object.
  27284. *
  27285. * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
  27286. */
  27287. /**
  27288. * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
  27289. */
  27290. videojs.createTimeRange = videojs.createTimeRanges = _timeRanges.createTimeRanges;
  27291. /**
  27292. * Format seconds as a time string, H:MM:SS or M:SS
  27293. * Supplying a guide (in seconds) will force a number of leading zeros
  27294. * to cover the length of the guide
  27295. *
  27296. * @borrows format-time:formatTime as videojs.formatTime
  27297. */
  27298. videojs.formatTime = _formatTime2['default'];
  27299. /**
  27300. * Resolve and parse the elements of a URL
  27301. *
  27302. * @borrows url:parseUrl as videojs.parseUrl
  27303. */
  27304. videojs.parseUrl = Url.parseUrl;
  27305. /**
  27306. * Returns whether the url passed is a cross domain request or not.
  27307. *
  27308. * @borrows url:isCrossOrigin as videojs.isCrossOrigin
  27309. */
  27310. videojs.isCrossOrigin = Url.isCrossOrigin;
  27311. /**
  27312. * Event target class.
  27313. *
  27314. * @borrows EventTarget as videojs.EventTarget
  27315. */
  27316. videojs.EventTarget = _eventTarget2['default'];
  27317. /**
  27318. * Add an event listener to element
  27319. * It stores the handler function in a separate cache object
  27320. * and adds a generic handler to the element's event,
  27321. * along with a unique id (guid) to the element.
  27322. *
  27323. * @borrows events:on as videojs.on
  27324. */
  27325. videojs.on = Events.on;
  27326. /**
  27327. * Trigger a listener only once for an event
  27328. *
  27329. * @borrows events:one as videojs.one
  27330. */
  27331. videojs.one = Events.one;
  27332. /**
  27333. * Removes event listeners from an element
  27334. *
  27335. * @borrows events:off as videojs.off
  27336. */
  27337. videojs.off = Events.off;
  27338. /**
  27339. * Trigger an event for an element
  27340. *
  27341. * @borrows events:trigger as videojs.trigger
  27342. */
  27343. videojs.trigger = Events.trigger;
  27344. /**
  27345. * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
  27346. *
  27347. * @param {Object} options
  27348. * settings for the request.
  27349. *
  27350. * @return {XMLHttpRequest|XDomainRequest}
  27351. * The request object.
  27352. *
  27353. * @see https://github.com/Raynos/xhr
  27354. */
  27355. videojs.xhr = _xhr2['default'];
  27356. /**
  27357. * TextTrack class
  27358. *
  27359. * @borrows TextTrack as videojs.TextTrack
  27360. */
  27361. videojs.TextTrack = _textTrack2['default'];
  27362. /**
  27363. * export the AudioTrack class so that source handlers can create
  27364. * AudioTracks and then add them to the players AudioTrackList
  27365. *
  27366. * @borrows AudioTrack as videojs.AudioTrack
  27367. */
  27368. videojs.AudioTrack = _audioTrack2['default'];
  27369. /**
  27370. * export the VideoTrack class so that source handlers can create
  27371. * VideoTracks and then add them to the players VideoTrackList
  27372. *
  27373. * @borrows VideoTrack as videojs.VideoTrack
  27374. */
  27375. videojs.VideoTrack = _videoTrack2['default'];
  27376. /**
  27377. * Determines, via duck typing, whether or not a value is a DOM element.
  27378. *
  27379. * @borrows dom:isEl as videojs.isEl
  27380. */
  27381. videojs.isEl = Dom.isEl;
  27382. /**
  27383. * Determines, via duck typing, whether or not a value is a text node.
  27384. *
  27385. * @borrows dom:isTextNode as videojs.isTextNode
  27386. */
  27387. videojs.isTextNode = Dom.isTextNode;
  27388. /**
  27389. * Creates an element and applies properties.
  27390. *
  27391. * @borrows dom:createEl as videojs.createEl
  27392. */
  27393. videojs.createEl = Dom.createEl;
  27394. /**
  27395. * Check if an element has a CSS class
  27396. *
  27397. * @borrows dom:hasElClass as videojs.hasClass
  27398. */
  27399. videojs.hasClass = Dom.hasElClass;
  27400. /**
  27401. * Add a CSS class name to an element
  27402. *
  27403. * @borrows dom:addElClass as videojs.addClass
  27404. */
  27405. videojs.addClass = Dom.addElClass;
  27406. /**
  27407. * Remove a CSS class name from an element
  27408. *
  27409. * @borrows dom:removeElClass as videojs.removeClass
  27410. */
  27411. videojs.removeClass = Dom.removeElClass;
  27412. /**
  27413. * Adds or removes a CSS class name on an element depending on an optional
  27414. * condition or the presence/absence of the class name.
  27415. *
  27416. * @borrows dom:toggleElClass as videojs.toggleClass
  27417. */
  27418. videojs.toggleClass = Dom.toggleElClass;
  27419. /**
  27420. * Apply attributes to an HTML element.
  27421. *
  27422. * @borrows dom:setElAttributes as videojs.setAttribute
  27423. */
  27424. videojs.setAttributes = Dom.setElAttributes;
  27425. /**
  27426. * Get an element's attribute values, as defined on the HTML tag
  27427. * Attributes are not the same as properties. They're defined on the tag
  27428. * or with setAttribute (which shouldn't be used with HTML)
  27429. * This will return true or false for boolean attributes.
  27430. *
  27431. * @borrows dom:getElAttributes as videojs.getAttributes
  27432. */
  27433. videojs.getAttributes = Dom.getElAttributes;
  27434. /**
  27435. * Empties the contents of an element.
  27436. *
  27437. * @borrows dom:emptyEl as videojs.emptyEl
  27438. */
  27439. videojs.emptyEl = Dom.emptyEl;
  27440. /**
  27441. * Normalizes and appends content to an element.
  27442. *
  27443. * The content for an element can be passed in multiple types and
  27444. * combinations, whose behavior is as follows:
  27445. *
  27446. * - String
  27447. * Normalized into a text node.
  27448. *
  27449. * - Element, TextNode
  27450. * Passed through.
  27451. *
  27452. * - Array
  27453. * A one-dimensional array of strings, elements, nodes, or functions (which
  27454. * return single strings, elements, or nodes).
  27455. *
  27456. * - Function
  27457. * If the sole argument, is expected to produce a string, element,
  27458. * node, or array.
  27459. *
  27460. * @borrows dom:appendContents as videojs.appendContet
  27461. */
  27462. videojs.appendContent = Dom.appendContent;
  27463. /**
  27464. * Normalizes and inserts content into an element; this is identical to
  27465. * `appendContent()`, except it empties the element first.
  27466. *
  27467. * The content for an element can be passed in multiple types and
  27468. * combinations, whose behavior is as follows:
  27469. *
  27470. * - String
  27471. * Normalized into a text node.
  27472. *
  27473. * - Element, TextNode
  27474. * Passed through.
  27475. *
  27476. * - Array
  27477. * A one-dimensional array of strings, elements, nodes, or functions (which
  27478. * return single strings, elements, or nodes).
  27479. *
  27480. * - Function
  27481. * If the sole argument, is expected to produce a string, element,
  27482. * node, or array.
  27483. *
  27484. * @borrows dom:insertContent as videojs.insertContent
  27485. */
  27486. videojs.insertContent = Dom.insertContent;
  27487. /**
  27488. * A safe getComputedStyle with an IE8 fallback.
  27489. *
  27490. * This is because in Firefox, if the player is loaded in an iframe with `display:none`,
  27491. * then `getComputedStyle` returns `null`, so, we do a null-check to make sure
  27492. * that the player doesn't break in these cases.
  27493. * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
  27494. *
  27495. * @borrows computed-style:computedStyle as videojs.computedStyle
  27496. */
  27497. videojs.computedStyle = _computedStyle2['default'];
  27498. /*
  27499. * Custom Universal Module Definition (UMD)
  27500. *
  27501. * Video.js will never be a non-browser lib so we can simplify UMD a bunch and
  27502. * still support requirejs and browserify. This also needs to be closure
  27503. * compiler compatible, so string keys are used.
  27504. */
  27505. if (typeof define === 'function' && define.amd) {
  27506. define('videojs', [], function () {
  27507. return videojs;
  27508. });
  27509. // checking that module is an object too because of umdjs/umd#35
  27510. } else if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object' && (typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') {
  27511. module.exports = videojs;
  27512. }
  27513. exports['default'] = videojs;
  27514. },{"./component":47,"./event-target":84,"./extend.js":85,"./player":93,"./plugins.js":94,"./setup":98,"./tech/tech.js":104,"./tracks/audio-track.js":106,"./tracks/text-track.js":114,"./tracks/video-track.js":119,"./utils/browser.js":120,"./utils/computed-style.js":122,"./utils/dom.js":123,"./utils/events.js":124,"./utils/fn.js":125,"./utils/format-time.js":126,"./utils/log.js":128,"./utils/merge-options.js":129,"./utils/obj":130,"./utils/stylesheet.js":131,"./utils/time-ranges.js":132,"./utils/url.js":134,"global/document":136,"global/window":137,"xhr":143}],136:[function(require,module,exports){
  27515. (function (global){
  27516. var topLevel = typeof global !== 'undefined' ? global :
  27517. typeof window !== 'undefined' ? window : {}
  27518. var minDoc = require('min-document');
  27519. if (typeof document !== 'undefined') {
  27520. module.exports = document;
  27521. } else {
  27522. var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  27523. if (!doccy) {
  27524. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  27525. }
  27526. module.exports = doccy;
  27527. }
  27528. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  27529. },{"min-document":13}],137:[function(require,module,exports){
  27530. (function (global){
  27531. if (typeof window !== "undefined") {
  27532. module.exports = window;
  27533. } else if (typeof global !== "undefined") {
  27534. module.exports = global;
  27535. } else if (typeof self !== "undefined"){
  27536. module.exports = self;
  27537. } else {
  27538. module.exports = {};
  27539. }
  27540. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  27541. },{}],138:[function(require,module,exports){
  27542. /**
  27543. * Copyright 2013 vtt.js Contributors
  27544. *
  27545. * Licensed under the Apache License, Version 2.0 (the "License");
  27546. * you may not use this file except in compliance with the License.
  27547. * You may obtain a copy of the License at
  27548. *
  27549. * http://www.apache.org/licenses/LICENSE-2.0
  27550. *
  27551. * Unless required by applicable law or agreed to in writing, software
  27552. * distributed under the License is distributed on an "AS IS" BASIS,
  27553. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  27554. * See the License for the specific language governing permissions and
  27555. * limitations under the License.
  27556. */
  27557. // Default exports for Node. Export the extended versions of VTTCue and
  27558. // VTTRegion in Node since we likely want the capability to convert back and
  27559. // forth between JSON. If we don't then it's not that big of a deal since we're
  27560. // off browser.
  27561. var window = require('global/window');
  27562. var vttjs = module.exports = {
  27563. WebVTT: require("./vtt.js"),
  27564. VTTCue: require("./vttcue.js"),
  27565. VTTRegion: require("./vttregion.js")
  27566. };
  27567. window.vttjs = vttjs;
  27568. window.WebVTT = vttjs.WebVTT;
  27569. var cueShim = vttjs.VTTCue;
  27570. var regionShim = vttjs.VTTRegion;
  27571. var nativeVTTCue = window.VTTCue;
  27572. var nativeVTTRegion = window.VTTRegion;
  27573. vttjs.shim = function() {
  27574. window.VTTCue = cueShim;
  27575. window.VTTRegion = regionShim;
  27576. };
  27577. vttjs.restore = function() {
  27578. window.VTTCue = nativeVTTCue;
  27579. window.VTTRegion = nativeVTTRegion;
  27580. };
  27581. if (!window.VTTCue) {
  27582. vttjs.shim();
  27583. }
  27584. },{"./vtt.js":139,"./vttcue.js":140,"./vttregion.js":141,"global/window":16}],139:[function(require,module,exports){
  27585. /**
  27586. * Copyright 2013 vtt.js Contributors
  27587. *
  27588. * Licensed under the Apache License, Version 2.0 (the "License");
  27589. * you may not use this file except in compliance with the License.
  27590. * You may obtain a copy of the License at
  27591. *
  27592. * http://www.apache.org/licenses/LICENSE-2.0
  27593. *
  27594. * Unless required by applicable law or agreed to in writing, software
  27595. * distributed under the License is distributed on an "AS IS" BASIS,
  27596. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  27597. * See the License for the specific language governing permissions and
  27598. * limitations under the License.
  27599. */
  27600. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  27601. /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
  27602. var _objCreate = Object.create || (function() {
  27603. function F() {}
  27604. return function(o) {
  27605. if (arguments.length !== 1) {
  27606. throw new Error('Object.create shim only accepts one parameter.');
  27607. }
  27608. F.prototype = o;
  27609. return new F();
  27610. };
  27611. })();
  27612. // Creates a new ParserError object from an errorData object. The errorData
  27613. // object should have default code and message properties. The default message
  27614. // property can be overriden by passing in a message parameter.
  27615. // See ParsingError.Errors below for acceptable errors.
  27616. function ParsingError(errorData, message) {
  27617. this.name = "ParsingError";
  27618. this.code = errorData.code;
  27619. this.message = message || errorData.message;
  27620. }
  27621. ParsingError.prototype = _objCreate(Error.prototype);
  27622. ParsingError.prototype.constructor = ParsingError;
  27623. // ParsingError metadata for acceptable ParsingErrors.
  27624. ParsingError.Errors = {
  27625. BadSignature: {
  27626. code: 0,
  27627. message: "Malformed WebVTT signature."
  27628. },
  27629. BadTimeStamp: {
  27630. code: 1,
  27631. message: "Malformed time stamp."
  27632. }
  27633. };
  27634. // Try to parse input as a time stamp.
  27635. function parseTimeStamp(input) {
  27636. function computeSeconds(h, m, s, f) {
  27637. return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
  27638. }
  27639. var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
  27640. if (!m) {
  27641. return null;
  27642. }
  27643. if (m[3]) {
  27644. // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
  27645. return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
  27646. } else if (m[1] > 59) {
  27647. // Timestamp takes the form of [hours]:[minutes].[milliseconds]
  27648. // First position is hours as it's over 59.
  27649. return computeSeconds(m[1], m[2], 0, m[4]);
  27650. } else {
  27651. // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
  27652. return computeSeconds(0, m[1], m[2], m[4]);
  27653. }
  27654. }
  27655. // A settings object holds key/value pairs and will ignore anything but the first
  27656. // assignment to a specific key.
  27657. function Settings() {
  27658. this.values = _objCreate(null);
  27659. }
  27660. Settings.prototype = {
  27661. // Only accept the first assignment to any key.
  27662. set: function(k, v) {
  27663. if (!this.get(k) && v !== "") {
  27664. this.values[k] = v;
  27665. }
  27666. },
  27667. // Return the value for a key, or a default value.
  27668. // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
  27669. // a number of possible default values as properties where 'defaultKey' is
  27670. // the key of the property that will be chosen; otherwise it's assumed to be
  27671. // a single value.
  27672. get: function(k, dflt, defaultKey) {
  27673. if (defaultKey) {
  27674. return this.has(k) ? this.values[k] : dflt[defaultKey];
  27675. }
  27676. return this.has(k) ? this.values[k] : dflt;
  27677. },
  27678. // Check whether we have a value for a key.
  27679. has: function(k) {
  27680. return k in this.values;
  27681. },
  27682. // Accept a setting if its one of the given alternatives.
  27683. alt: function(k, v, a) {
  27684. for (var n = 0; n < a.length; ++n) {
  27685. if (v === a[n]) {
  27686. this.set(k, v);
  27687. break;
  27688. }
  27689. }
  27690. },
  27691. // Accept a setting if its a valid (signed) integer.
  27692. integer: function(k, v) {
  27693. if (/^-?\d+$/.test(v)) { // integer
  27694. this.set(k, parseInt(v, 10));
  27695. }
  27696. },
  27697. // Accept a setting if its a valid percentage.
  27698. percent: function(k, v) {
  27699. var m;
  27700. if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
  27701. v = parseFloat(v);
  27702. if (v >= 0 && v <= 100) {
  27703. this.set(k, v);
  27704. return true;
  27705. }
  27706. }
  27707. return false;
  27708. }
  27709. };
  27710. // Helper function to parse input into groups separated by 'groupDelim', and
  27711. // interprete each group as a key/value pair separated by 'keyValueDelim'.
  27712. function parseOptions(input, callback, keyValueDelim, groupDelim) {
  27713. var groups = groupDelim ? input.split(groupDelim) : [input];
  27714. for (var i in groups) {
  27715. if (typeof groups[i] !== "string") {
  27716. continue;
  27717. }
  27718. var kv = groups[i].split(keyValueDelim);
  27719. if (kv.length !== 2) {
  27720. continue;
  27721. }
  27722. var k = kv[0];
  27723. var v = kv[1];
  27724. callback(k, v);
  27725. }
  27726. }
  27727. function parseCue(input, cue, regionList) {
  27728. // Remember the original input if we need to throw an error.
  27729. var oInput = input;
  27730. // 4.1 WebVTT timestamp
  27731. function consumeTimeStamp() {
  27732. var ts = parseTimeStamp(input);
  27733. if (ts === null) {
  27734. throw new ParsingError(ParsingError.Errors.BadTimeStamp,
  27735. "Malformed timestamp: " + oInput);
  27736. }
  27737. // Remove time stamp from input.
  27738. input = input.replace(/^[^\sa-zA-Z-]+/, "");
  27739. return ts;
  27740. }
  27741. // 4.4.2 WebVTT cue settings
  27742. function consumeCueSettings(input, cue) {
  27743. var settings = new Settings();
  27744. parseOptions(input, function (k, v) {
  27745. switch (k) {
  27746. case "region":
  27747. // Find the last region we parsed with the same region id.
  27748. for (var i = regionList.length - 1; i >= 0; i--) {
  27749. if (regionList[i].id === v) {
  27750. settings.set(k, regionList[i].region);
  27751. break;
  27752. }
  27753. }
  27754. break;
  27755. case "vertical":
  27756. settings.alt(k, v, ["rl", "lr"]);
  27757. break;
  27758. case "line":
  27759. var vals = v.split(","),
  27760. vals0 = vals[0];
  27761. settings.integer(k, vals0);
  27762. settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
  27763. settings.alt(k, vals0, ["auto"]);
  27764. if (vals.length === 2) {
  27765. settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
  27766. }
  27767. break;
  27768. case "position":
  27769. vals = v.split(",");
  27770. settings.percent(k, vals[0]);
  27771. if (vals.length === 2) {
  27772. settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
  27773. }
  27774. break;
  27775. case "size":
  27776. settings.percent(k, v);
  27777. break;
  27778. case "align":
  27779. settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
  27780. break;
  27781. }
  27782. }, /:/, /\s/);
  27783. // Apply default values for any missing fields.
  27784. cue.region = settings.get("region", null);
  27785. cue.vertical = settings.get("vertical", "");
  27786. cue.line = settings.get("line", "auto");
  27787. cue.lineAlign = settings.get("lineAlign", "start");
  27788. cue.snapToLines = settings.get("snapToLines", true);
  27789. cue.size = settings.get("size", 100);
  27790. cue.align = settings.get("align", "middle");
  27791. cue.position = settings.get("position", {
  27792. start: 0,
  27793. left: 0,
  27794. middle: 50,
  27795. end: 100,
  27796. right: 100
  27797. }, cue.align);
  27798. cue.positionAlign = settings.get("positionAlign", {
  27799. start: "start",
  27800. left: "start",
  27801. middle: "middle",
  27802. end: "end",
  27803. right: "end"
  27804. }, cue.align);
  27805. }
  27806. function skipWhitespace() {
  27807. input = input.replace(/^\s+/, "");
  27808. }
  27809. // 4.1 WebVTT cue timings.
  27810. skipWhitespace();
  27811. cue.startTime = consumeTimeStamp(); // (1) collect cue start time
  27812. skipWhitespace();
  27813. if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->"
  27814. throw new ParsingError(ParsingError.Errors.BadTimeStamp,
  27815. "Malformed time stamp (time stamps must be separated by '-->'): " +
  27816. oInput);
  27817. }
  27818. input = input.substr(3);
  27819. skipWhitespace();
  27820. cue.endTime = consumeTimeStamp(); // (5) collect cue end time
  27821. // 4.1 WebVTT cue settings list.
  27822. skipWhitespace();
  27823. consumeCueSettings(input, cue);
  27824. }
  27825. var ESCAPE = {
  27826. "&amp;": "&",
  27827. "&lt;": "<",
  27828. "&gt;": ">",
  27829. "&lrm;": "\u200e",
  27830. "&rlm;": "\u200f",
  27831. "&nbsp;": "\u00a0"
  27832. };
  27833. var TAG_NAME = {
  27834. c: "span",
  27835. i: "i",
  27836. b: "b",
  27837. u: "u",
  27838. ruby: "ruby",
  27839. rt: "rt",
  27840. v: "span",
  27841. lang: "span"
  27842. };
  27843. var TAG_ANNOTATION = {
  27844. v: "title",
  27845. lang: "lang"
  27846. };
  27847. var NEEDS_PARENT = {
  27848. rt: "ruby"
  27849. };
  27850. // Parse content into a document fragment.
  27851. function parseContent(window, input) {
  27852. function nextToken() {
  27853. // Check for end-of-string.
  27854. if (!input) {
  27855. return null;
  27856. }
  27857. // Consume 'n' characters from the input.
  27858. function consume(result) {
  27859. input = input.substr(result.length);
  27860. return result;
  27861. }
  27862. var m = input.match(/^([^<]*)(<[^>]+>?)?/);
  27863. // If there is some text before the next tag, return it, otherwise return
  27864. // the tag.
  27865. return consume(m[1] ? m[1] : m[2]);
  27866. }
  27867. // Unescape a string 's'.
  27868. function unescape1(e) {
  27869. return ESCAPE[e];
  27870. }
  27871. function unescape(s) {
  27872. while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
  27873. s = s.replace(m[0], unescape1);
  27874. }
  27875. return s;
  27876. }
  27877. function shouldAdd(current, element) {
  27878. return !NEEDS_PARENT[element.localName] ||
  27879. NEEDS_PARENT[element.localName] === current.localName;
  27880. }
  27881. // Create an element for this tag.
  27882. function createElement(type, annotation) {
  27883. var tagName = TAG_NAME[type];
  27884. if (!tagName) {
  27885. return null;
  27886. }
  27887. var element = window.document.createElement(tagName);
  27888. element.localName = tagName;
  27889. var name = TAG_ANNOTATION[type];
  27890. if (name && annotation) {
  27891. element[name] = annotation.trim();
  27892. }
  27893. return element;
  27894. }
  27895. var rootDiv = window.document.createElement("div"),
  27896. current = rootDiv,
  27897. t,
  27898. tagStack = [];
  27899. while ((t = nextToken()) !== null) {
  27900. if (t[0] === '<') {
  27901. if (t[1] === "/") {
  27902. // If the closing tag matches, move back up to the parent node.
  27903. if (tagStack.length &&
  27904. tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
  27905. tagStack.pop();
  27906. current = current.parentNode;
  27907. }
  27908. // Otherwise just ignore the end tag.
  27909. continue;
  27910. }
  27911. var ts = parseTimeStamp(t.substr(1, t.length - 2));
  27912. var node;
  27913. if (ts) {
  27914. // Timestamps are lead nodes as well.
  27915. node = window.document.createProcessingInstruction("timestamp", ts);
  27916. current.appendChild(node);
  27917. continue;
  27918. }
  27919. var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
  27920. // If we can't parse the tag, skip to the next tag.
  27921. if (!m) {
  27922. continue;
  27923. }
  27924. // Try to construct an element, and ignore the tag if we couldn't.
  27925. node = createElement(m[1], m[3]);
  27926. if (!node) {
  27927. continue;
  27928. }
  27929. // Determine if the tag should be added based on the context of where it
  27930. // is placed in the cuetext.
  27931. if (!shouldAdd(current, node)) {
  27932. continue;
  27933. }
  27934. // Set the class list (as a list of classes, separated by space).
  27935. if (m[2]) {
  27936. node.className = m[2].substr(1).replace('.', ' ');
  27937. }
  27938. // Append the node to the current node, and enter the scope of the new
  27939. // node.
  27940. tagStack.push(m[1]);
  27941. current.appendChild(node);
  27942. current = node;
  27943. continue;
  27944. }
  27945. // Text nodes are leaf nodes.
  27946. current.appendChild(window.document.createTextNode(unescape(t)));
  27947. }
  27948. return rootDiv;
  27949. }
  27950. // This is a list of all the Unicode characters that have a strong
  27951. // right-to-left category. What this means is that these characters are
  27952. // written right-to-left for sure. It was generated by pulling all the strong
  27953. // right-to-left characters out of the Unicode data table. That table can
  27954. // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
  27955. var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6],
  27956. [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d],
  27957. [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6],
  27958. [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5],
  27959. [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815],
  27960. [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858],
  27961. [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f],
  27962. [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c],
  27963. [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1],
  27964. [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc],
  27965. [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808],
  27966. [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855],
  27967. [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f],
  27968. [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13],
  27969. [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58],
  27970. [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72],
  27971. [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f],
  27972. [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32],
  27973. [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42],
  27974. [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f],
  27975. [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59],
  27976. [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62],
  27977. [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77],
  27978. [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b],
  27979. [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
  27980. function isStrongRTLChar(charCode) {
  27981. for (var i = 0; i < strongRTLRanges.length; i++) {
  27982. var currentRange = strongRTLRanges[i];
  27983. if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
  27984. return true;
  27985. }
  27986. }
  27987. return false;
  27988. }
  27989. function determineBidi(cueDiv) {
  27990. var nodeStack = [],
  27991. text = "",
  27992. charCode;
  27993. if (!cueDiv || !cueDiv.childNodes) {
  27994. return "ltr";
  27995. }
  27996. function pushNodes(nodeStack, node) {
  27997. for (var i = node.childNodes.length - 1; i >= 0; i--) {
  27998. nodeStack.push(node.childNodes[i]);
  27999. }
  28000. }
  28001. function nextTextNode(nodeStack) {
  28002. if (!nodeStack || !nodeStack.length) {
  28003. return null;
  28004. }
  28005. var node = nodeStack.pop(),
  28006. text = node.textContent || node.innerText;
  28007. if (text) {
  28008. // TODO: This should match all unicode type B characters (paragraph
  28009. // separator characters). See issue #115.
  28010. var m = text.match(/^.*(\n|\r)/);
  28011. if (m) {
  28012. nodeStack.length = 0;
  28013. return m[0];
  28014. }
  28015. return text;
  28016. }
  28017. if (node.tagName === "ruby") {
  28018. return nextTextNode(nodeStack);
  28019. }
  28020. if (node.childNodes) {
  28021. pushNodes(nodeStack, node);
  28022. return nextTextNode(nodeStack);
  28023. }
  28024. }
  28025. pushNodes(nodeStack, cueDiv);
  28026. while ((text = nextTextNode(nodeStack))) {
  28027. for (var i = 0; i < text.length; i++) {
  28028. charCode = text.charCodeAt(i);
  28029. if (isStrongRTLChar(charCode)) {
  28030. return "rtl";
  28031. }
  28032. }
  28033. }
  28034. return "ltr";
  28035. }
  28036. function computeLinePos(cue) {
  28037. if (typeof cue.line === "number" &&
  28038. (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
  28039. return cue.line;
  28040. }
  28041. if (!cue.track || !cue.track.textTrackList ||
  28042. !cue.track.textTrackList.mediaElement) {
  28043. return -1;
  28044. }
  28045. var track = cue.track,
  28046. trackList = track.textTrackList,
  28047. count = 0;
  28048. for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
  28049. if (trackList[i].mode === "showing") {
  28050. count++;
  28051. }
  28052. }
  28053. return ++count * -1;
  28054. }
  28055. function StyleBox() {
  28056. }
  28057. // Apply styles to a div. If there is no div passed then it defaults to the
  28058. // div on 'this'.
  28059. StyleBox.prototype.applyStyles = function(styles, div) {
  28060. div = div || this.div;
  28061. for (var prop in styles) {
  28062. if (styles.hasOwnProperty(prop)) {
  28063. div.style[prop] = styles[prop];
  28064. }
  28065. }
  28066. };
  28067. StyleBox.prototype.formatStyle = function(val, unit) {
  28068. return val === 0 ? 0 : val + unit;
  28069. };
  28070. // Constructs the computed display state of the cue (a div). Places the div
  28071. // into the overlay which should be a block level element (usually a div).
  28072. function CueStyleBox(window, cue, styleOptions) {
  28073. var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
  28074. var color = "rgba(255, 255, 255, 1)";
  28075. var backgroundColor = "rgba(0, 0, 0, 0.8)";
  28076. if (isIE8) {
  28077. color = "rgb(255, 255, 255)";
  28078. backgroundColor = "rgb(0, 0, 0)";
  28079. }
  28080. StyleBox.call(this);
  28081. this.cue = cue;
  28082. // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
  28083. // have inline positioning and will function as the cue background box.
  28084. this.cueDiv = parseContent(window, cue.text);
  28085. var styles = {
  28086. color: color,
  28087. backgroundColor: backgroundColor,
  28088. position: "relative",
  28089. left: 0,
  28090. right: 0,
  28091. top: 0,
  28092. bottom: 0,
  28093. display: "inline"
  28094. };
  28095. if (!isIE8) {
  28096. styles.writingMode = cue.vertical === "" ? "horizontal-tb"
  28097. : cue.vertical === "lr" ? "vertical-lr"
  28098. : "vertical-rl";
  28099. styles.unicodeBidi = "plaintext";
  28100. }
  28101. this.applyStyles(styles, this.cueDiv);
  28102. // Create an absolutely positioned div that will be used to position the cue
  28103. // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
  28104. // mirrors of them except "middle" which is "center" in CSS.
  28105. this.div = window.document.createElement("div");
  28106. styles = {
  28107. textAlign: cue.align === "middle" ? "center" : cue.align,
  28108. font: styleOptions.font,
  28109. whiteSpace: "pre-line",
  28110. position: "absolute"
  28111. };
  28112. if (!isIE8) {
  28113. styles.direction = determineBidi(this.cueDiv);
  28114. styles.writingMode = cue.vertical === "" ? "horizontal-tb"
  28115. : cue.vertical === "lr" ? "vertical-lr"
  28116. : "vertical-rl".
  28117. stylesunicodeBidi = "plaintext";
  28118. }
  28119. this.applyStyles(styles);
  28120. this.div.appendChild(this.cueDiv);
  28121. // Calculate the distance from the reference edge of the viewport to the text
  28122. // position of the cue box. The reference edge will be resolved later when
  28123. // the box orientation styles are applied.
  28124. var textPos = 0;
  28125. switch (cue.positionAlign) {
  28126. case "start":
  28127. textPos = cue.position;
  28128. break;
  28129. case "middle":
  28130. textPos = cue.position - (cue.size / 2);
  28131. break;
  28132. case "end":
  28133. textPos = cue.position - cue.size;
  28134. break;
  28135. }
  28136. // Horizontal box orientation; textPos is the distance from the left edge of the
  28137. // area to the left edge of the box and cue.size is the distance extending to
  28138. // the right from there.
  28139. if (cue.vertical === "") {
  28140. this.applyStyles({
  28141. left: this.formatStyle(textPos, "%"),
  28142. width: this.formatStyle(cue.size, "%")
  28143. });
  28144. // Vertical box orientation; textPos is the distance from the top edge of the
  28145. // area to the top edge of the box and cue.size is the height extending
  28146. // downwards from there.
  28147. } else {
  28148. this.applyStyles({
  28149. top: this.formatStyle(textPos, "%"),
  28150. height: this.formatStyle(cue.size, "%")
  28151. });
  28152. }
  28153. this.move = function(box) {
  28154. this.applyStyles({
  28155. top: this.formatStyle(box.top, "px"),
  28156. bottom: this.formatStyle(box.bottom, "px"),
  28157. left: this.formatStyle(box.left, "px"),
  28158. right: this.formatStyle(box.right, "px"),
  28159. height: this.formatStyle(box.height, "px"),
  28160. width: this.formatStyle(box.width, "px")
  28161. });
  28162. };
  28163. }
  28164. CueStyleBox.prototype = _objCreate(StyleBox.prototype);
  28165. CueStyleBox.prototype.constructor = CueStyleBox;
  28166. // Represents the co-ordinates of an Element in a way that we can easily
  28167. // compute things with such as if it overlaps or intersects with another Element.
  28168. // Can initialize it with either a StyleBox or another BoxPosition.
  28169. function BoxPosition(obj) {
  28170. var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
  28171. // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
  28172. // was passed in and we need to copy the results of 'getBoundingClientRect'
  28173. // as the object returned is readonly. All co-ordinate values are in reference
  28174. // to the viewport origin (top left).
  28175. var lh, height, width, top;
  28176. if (obj.div) {
  28177. height = obj.div.offsetHeight;
  28178. width = obj.div.offsetWidth;
  28179. top = obj.div.offsetTop;
  28180. var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
  28181. rects.getClientRects && rects.getClientRects();
  28182. obj = obj.div.getBoundingClientRect();
  28183. // In certain cases the outter div will be slightly larger then the sum of
  28184. // the inner div's lines. This could be due to bold text, etc, on some platforms.
  28185. // In this case we should get the average line height and use that. This will
  28186. // result in the desired behaviour.
  28187. lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
  28188. : 0;
  28189. }
  28190. this.left = obj.left;
  28191. this.right = obj.right;
  28192. this.top = obj.top || top;
  28193. this.height = obj.height || height;
  28194. this.bottom = obj.bottom || (top + (obj.height || height));
  28195. this.width = obj.width || width;
  28196. this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
  28197. if (isIE8 && !this.lineHeight) {
  28198. this.lineHeight = 13;
  28199. }
  28200. }
  28201. // Move the box along a particular axis. Optionally pass in an amount to move
  28202. // the box. If no amount is passed then the default is the line height of the
  28203. // box.
  28204. BoxPosition.prototype.move = function(axis, toMove) {
  28205. toMove = toMove !== undefined ? toMove : this.lineHeight;
  28206. switch (axis) {
  28207. case "+x":
  28208. this.left += toMove;
  28209. this.right += toMove;
  28210. break;
  28211. case "-x":
  28212. this.left -= toMove;
  28213. this.right -= toMove;
  28214. break;
  28215. case "+y":
  28216. this.top += toMove;
  28217. this.bottom += toMove;
  28218. break;
  28219. case "-y":
  28220. this.top -= toMove;
  28221. this.bottom -= toMove;
  28222. break;
  28223. }
  28224. };
  28225. // Check if this box overlaps another box, b2.
  28226. BoxPosition.prototype.overlaps = function(b2) {
  28227. return this.left < b2.right &&
  28228. this.right > b2.left &&
  28229. this.top < b2.bottom &&
  28230. this.bottom > b2.top;
  28231. };
  28232. // Check if this box overlaps any other boxes in boxes.
  28233. BoxPosition.prototype.overlapsAny = function(boxes) {
  28234. for (var i = 0; i < boxes.length; i++) {
  28235. if (this.overlaps(boxes[i])) {
  28236. return true;
  28237. }
  28238. }
  28239. return false;
  28240. };
  28241. // Check if this box is within another box.
  28242. BoxPosition.prototype.within = function(container) {
  28243. return this.top >= container.top &&
  28244. this.bottom <= container.bottom &&
  28245. this.left >= container.left &&
  28246. this.right <= container.right;
  28247. };
  28248. // Check if this box is entirely within the container or it is overlapping
  28249. // on the edge opposite of the axis direction passed. For example, if "+x" is
  28250. // passed and the box is overlapping on the left edge of the container, then
  28251. // return true.
  28252. BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
  28253. switch (axis) {
  28254. case "+x":
  28255. return this.left < container.left;
  28256. case "-x":
  28257. return this.right > container.right;
  28258. case "+y":
  28259. return this.top < container.top;
  28260. case "-y":
  28261. return this.bottom > container.bottom;
  28262. }
  28263. };
  28264. // Find the percentage of the area that this box is overlapping with another
  28265. // box.
  28266. BoxPosition.prototype.intersectPercentage = function(b2) {
  28267. var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
  28268. y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
  28269. intersectArea = x * y;
  28270. return intersectArea / (this.height * this.width);
  28271. };
  28272. // Convert the positions from this box to CSS compatible positions using
  28273. // the reference container's positions. This has to be done because this
  28274. // box's positions are in reference to the viewport origin, whereas, CSS
  28275. // values are in referecne to their respective edges.
  28276. BoxPosition.prototype.toCSSCompatValues = function(reference) {
  28277. return {
  28278. top: this.top - reference.top,
  28279. bottom: reference.bottom - this.bottom,
  28280. left: this.left - reference.left,
  28281. right: reference.right - this.right,
  28282. height: this.height,
  28283. width: this.width
  28284. };
  28285. };
  28286. // Get an object that represents the box's position without anything extra.
  28287. // Can pass a StyleBox, HTMLElement, or another BoxPositon.
  28288. BoxPosition.getSimpleBoxPosition = function(obj) {
  28289. var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
  28290. var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
  28291. var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
  28292. obj = obj.div ? obj.div.getBoundingClientRect() :
  28293. obj.tagName ? obj.getBoundingClientRect() : obj;
  28294. var ret = {
  28295. left: obj.left,
  28296. right: obj.right,
  28297. top: obj.top || top,
  28298. height: obj.height || height,
  28299. bottom: obj.bottom || (top + (obj.height || height)),
  28300. width: obj.width || width
  28301. };
  28302. return ret;
  28303. };
  28304. // Move a StyleBox to its specified, or next best, position. The containerBox
  28305. // is the box that contains the StyleBox, such as a div. boxPositions are
  28306. // a list of other boxes that the styleBox can't overlap with.
  28307. function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
  28308. // Find the best position for a cue box, b, on the video. The axis parameter
  28309. // is a list of axis, the order of which, it will move the box along. For example:
  28310. // Passing ["+x", "-x"] will move the box first along the x axis in the positive
  28311. // direction. If it doesn't find a good position for it there it will then move
  28312. // it along the x axis in the negative direction.
  28313. function findBestPosition(b, axis) {
  28314. var bestPosition,
  28315. specifiedPosition = new BoxPosition(b),
  28316. percentage = 1; // Highest possible so the first thing we get is better.
  28317. for (var i = 0; i < axis.length; i++) {
  28318. while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
  28319. (b.within(containerBox) && b.overlapsAny(boxPositions))) {
  28320. b.move(axis[i]);
  28321. }
  28322. // We found a spot where we aren't overlapping anything. This is our
  28323. // best position.
  28324. if (b.within(containerBox)) {
  28325. return b;
  28326. }
  28327. var p = b.intersectPercentage(containerBox);
  28328. // If we're outside the container box less then we were on our last try
  28329. // then remember this position as the best position.
  28330. if (percentage > p) {
  28331. bestPosition = new BoxPosition(b);
  28332. percentage = p;
  28333. }
  28334. // Reset the box position to the specified position.
  28335. b = new BoxPosition(specifiedPosition);
  28336. }
  28337. return bestPosition || specifiedPosition;
  28338. }
  28339. var boxPosition = new BoxPosition(styleBox),
  28340. cue = styleBox.cue,
  28341. linePos = computeLinePos(cue),
  28342. axis = [];
  28343. // If we have a line number to align the cue to.
  28344. if (cue.snapToLines) {
  28345. var size;
  28346. switch (cue.vertical) {
  28347. case "":
  28348. axis = [ "+y", "-y" ];
  28349. size = "height";
  28350. break;
  28351. case "rl":
  28352. axis = [ "+x", "-x" ];
  28353. size = "width";
  28354. break;
  28355. case "lr":
  28356. axis = [ "-x", "+x" ];
  28357. size = "width";
  28358. break;
  28359. }
  28360. var step = boxPosition.lineHeight,
  28361. position = step * Math.round(linePos),
  28362. maxPosition = containerBox[size] + step,
  28363. initialAxis = axis[0];
  28364. // If the specified intial position is greater then the max position then
  28365. // clamp the box to the amount of steps it would take for the box to
  28366. // reach the max position.
  28367. if (Math.abs(position) > maxPosition) {
  28368. position = position < 0 ? -1 : 1;
  28369. position *= Math.ceil(maxPosition / step) * step;
  28370. }
  28371. // If computed line position returns negative then line numbers are
  28372. // relative to the bottom of the video instead of the top. Therefore, we
  28373. // need to increase our initial position by the length or width of the
  28374. // video, depending on the writing direction, and reverse our axis directions.
  28375. if (linePos < 0) {
  28376. position += cue.vertical === "" ? containerBox.height : containerBox.width;
  28377. axis = axis.reverse();
  28378. }
  28379. // Move the box to the specified position. This may not be its best
  28380. // position.
  28381. boxPosition.move(initialAxis, position);
  28382. } else {
  28383. // If we have a percentage line value for the cue.
  28384. var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
  28385. switch (cue.lineAlign) {
  28386. case "middle":
  28387. linePos -= (calculatedPercentage / 2);
  28388. break;
  28389. case "end":
  28390. linePos -= calculatedPercentage;
  28391. break;
  28392. }
  28393. // Apply initial line position to the cue box.
  28394. switch (cue.vertical) {
  28395. case "":
  28396. styleBox.applyStyles({
  28397. top: styleBox.formatStyle(linePos, "%")
  28398. });
  28399. break;
  28400. case "rl":
  28401. styleBox.applyStyles({
  28402. left: styleBox.formatStyle(linePos, "%")
  28403. });
  28404. break;
  28405. case "lr":
  28406. styleBox.applyStyles({
  28407. right: styleBox.formatStyle(linePos, "%")
  28408. });
  28409. break;
  28410. }
  28411. axis = [ "+y", "-x", "+x", "-y" ];
  28412. // Get the box position again after we've applied the specified positioning
  28413. // to it.
  28414. boxPosition = new BoxPosition(styleBox);
  28415. }
  28416. var bestPosition = findBestPosition(boxPosition, axis);
  28417. styleBox.move(bestPosition.toCSSCompatValues(containerBox));
  28418. }
  28419. function WebVTT() {
  28420. // Nothing
  28421. }
  28422. // Helper to allow strings to be decoded instead of the default binary utf8 data.
  28423. WebVTT.StringDecoder = function() {
  28424. return {
  28425. decode: function(data) {
  28426. if (!data) {
  28427. return "";
  28428. }
  28429. if (typeof data !== "string") {
  28430. throw new Error("Error - expected string data.");
  28431. }
  28432. return decodeURIComponent(encodeURIComponent(data));
  28433. }
  28434. };
  28435. };
  28436. WebVTT.convertCueToDOMTree = function(window, cuetext) {
  28437. if (!window || !cuetext) {
  28438. return null;
  28439. }
  28440. return parseContent(window, cuetext);
  28441. };
  28442. var FONT_SIZE_PERCENT = 0.05;
  28443. var FONT_STYLE = "sans-serif";
  28444. var CUE_BACKGROUND_PADDING = "1.5%";
  28445. // Runs the processing model over the cues and regions passed to it.
  28446. // @param overlay A block level element (usually a div) that the computed cues
  28447. // and regions will be placed into.
  28448. WebVTT.processCues = function(window, cues, overlay) {
  28449. if (!window || !cues || !overlay) {
  28450. return null;
  28451. }
  28452. // Remove all previous children.
  28453. while (overlay.firstChild) {
  28454. overlay.removeChild(overlay.firstChild);
  28455. }
  28456. var paddedOverlay = window.document.createElement("div");
  28457. paddedOverlay.style.position = "absolute";
  28458. paddedOverlay.style.left = "0";
  28459. paddedOverlay.style.right = "0";
  28460. paddedOverlay.style.top = "0";
  28461. paddedOverlay.style.bottom = "0";
  28462. paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
  28463. overlay.appendChild(paddedOverlay);
  28464. // Determine if we need to compute the display states of the cues. This could
  28465. // be the case if a cue's state has been changed since the last computation or
  28466. // if it has not been computed yet.
  28467. function shouldCompute(cues) {
  28468. for (var i = 0; i < cues.length; i++) {
  28469. if (cues[i].hasBeenReset || !cues[i].displayState) {
  28470. return true;
  28471. }
  28472. }
  28473. return false;
  28474. }
  28475. // We don't need to recompute the cues' display states. Just reuse them.
  28476. if (!shouldCompute(cues)) {
  28477. for (var i = 0; i < cues.length; i++) {
  28478. paddedOverlay.appendChild(cues[i].displayState);
  28479. }
  28480. return;
  28481. }
  28482. var boxPositions = [],
  28483. containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
  28484. fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
  28485. var styleOptions = {
  28486. font: fontSize + "px " + FONT_STYLE
  28487. };
  28488. (function() {
  28489. var styleBox, cue;
  28490. for (var i = 0; i < cues.length; i++) {
  28491. cue = cues[i];
  28492. // Compute the intial position and styles of the cue div.
  28493. styleBox = new CueStyleBox(window, cue, styleOptions);
  28494. paddedOverlay.appendChild(styleBox.div);
  28495. // Move the cue div to it's correct line position.
  28496. moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
  28497. // Remember the computed div so that we don't have to recompute it later
  28498. // if we don't have too.
  28499. cue.displayState = styleBox.div;
  28500. boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
  28501. }
  28502. })();
  28503. };
  28504. WebVTT.Parser = function(window, vttjs, decoder) {
  28505. if (!decoder) {
  28506. decoder = vttjs;
  28507. vttjs = {};
  28508. }
  28509. if (!vttjs) {
  28510. vttjs = {};
  28511. }
  28512. this.window = window;
  28513. this.vttjs = vttjs;
  28514. this.state = "INITIAL";
  28515. this.buffer = "";
  28516. this.decoder = decoder || new TextDecoder("utf8");
  28517. this.regionList = [];
  28518. };
  28519. WebVTT.Parser.prototype = {
  28520. // If the error is a ParsingError then report it to the consumer if
  28521. // possible. If it's not a ParsingError then throw it like normal.
  28522. reportOrThrowError: function(e) {
  28523. if (e instanceof ParsingError) {
  28524. this.onparsingerror && this.onparsingerror(e);
  28525. } else {
  28526. throw e;
  28527. }
  28528. },
  28529. parse: function (data) {
  28530. var self = this;
  28531. // If there is no data then we won't decode it, but will just try to parse
  28532. // whatever is in buffer already. This may occur in circumstances, for
  28533. // example when flush() is called.
  28534. if (data) {
  28535. // Try to decode the data that we received.
  28536. self.buffer += self.decoder.decode(data, {stream: true});
  28537. }
  28538. function collectNextLine() {
  28539. var buffer = self.buffer;
  28540. var pos = 0;
  28541. while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
  28542. ++pos;
  28543. }
  28544. var line = buffer.substr(0, pos);
  28545. // Advance the buffer early in case we fail below.
  28546. if (buffer[pos] === '\r') {
  28547. ++pos;
  28548. }
  28549. if (buffer[pos] === '\n') {
  28550. ++pos;
  28551. }
  28552. self.buffer = buffer.substr(pos);
  28553. return line;
  28554. }
  28555. // 3.4 WebVTT region and WebVTT region settings syntax
  28556. function parseRegion(input) {
  28557. var settings = new Settings();
  28558. parseOptions(input, function (k, v) {
  28559. switch (k) {
  28560. case "id":
  28561. settings.set(k, v);
  28562. break;
  28563. case "width":
  28564. settings.percent(k, v);
  28565. break;
  28566. case "lines":
  28567. settings.integer(k, v);
  28568. break;
  28569. case "regionanchor":
  28570. case "viewportanchor":
  28571. var xy = v.split(',');
  28572. if (xy.length !== 2) {
  28573. break;
  28574. }
  28575. // We have to make sure both x and y parse, so use a temporary
  28576. // settings object here.
  28577. var anchor = new Settings();
  28578. anchor.percent("x", xy[0]);
  28579. anchor.percent("y", xy[1]);
  28580. if (!anchor.has("x") || !anchor.has("y")) {
  28581. break;
  28582. }
  28583. settings.set(k + "X", anchor.get("x"));
  28584. settings.set(k + "Y", anchor.get("y"));
  28585. break;
  28586. case "scroll":
  28587. settings.alt(k, v, ["up"]);
  28588. break;
  28589. }
  28590. }, /=/, /\s/);
  28591. // Create the region, using default values for any values that were not
  28592. // specified.
  28593. if (settings.has("id")) {
  28594. var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
  28595. region.width = settings.get("width", 100);
  28596. region.lines = settings.get("lines", 3);
  28597. region.regionAnchorX = settings.get("regionanchorX", 0);
  28598. region.regionAnchorY = settings.get("regionanchorY", 100);
  28599. region.viewportAnchorX = settings.get("viewportanchorX", 0);
  28600. region.viewportAnchorY = settings.get("viewportanchorY", 100);
  28601. region.scroll = settings.get("scroll", "");
  28602. // Register the region.
  28603. self.onregion && self.onregion(region);
  28604. // Remember the VTTRegion for later in case we parse any VTTCues that
  28605. // reference it.
  28606. self.regionList.push({
  28607. id: settings.get("id"),
  28608. region: region
  28609. });
  28610. }
  28611. }
  28612. // draft-pantos-http-live-streaming-20
  28613. // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
  28614. // 3.5 WebVTT
  28615. function parseTimestampMap(input) {
  28616. var settings = new Settings();
  28617. parseOptions(input, function(k, v) {
  28618. switch(k) {
  28619. case "MPEGT":
  28620. settings.integer(k + 'S', v);
  28621. break;
  28622. case "LOCA":
  28623. settings.set(k + 'L', parseTimeStamp(v));
  28624. break;
  28625. }
  28626. }, /[^\d]:/, /,/);
  28627. self.ontimestampmap && self.ontimestampmap({
  28628. "MPEGTS": settings.get("MPEGTS"),
  28629. "LOCAL": settings.get("LOCAL")
  28630. });
  28631. }
  28632. // 3.2 WebVTT metadata header syntax
  28633. function parseHeader(input) {
  28634. if (input.match(/X-TIMESTAMP-MAP/)) {
  28635. // This line contains HLS X-TIMESTAMP-MAP metadata
  28636. parseOptions(input, function(k, v) {
  28637. switch(k) {
  28638. case "X-TIMESTAMP-MAP":
  28639. parseTimestampMap(v);
  28640. break;
  28641. }
  28642. }, /=/);
  28643. } else {
  28644. parseOptions(input, function (k, v) {
  28645. switch (k) {
  28646. case "Region":
  28647. // 3.3 WebVTT region metadata header syntax
  28648. parseRegion(v);
  28649. break;
  28650. }
  28651. }, /:/);
  28652. }
  28653. }
  28654. // 5.1 WebVTT file parsing.
  28655. try {
  28656. var line;
  28657. if (self.state === "INITIAL") {
  28658. // We can't start parsing until we have the first line.
  28659. if (!/\r\n|\n/.test(self.buffer)) {
  28660. return this;
  28661. }
  28662. line = collectNextLine();
  28663. var m = line.match(/^WEBVTT([ \t].*)?$/);
  28664. if (!m || !m[0]) {
  28665. throw new ParsingError(ParsingError.Errors.BadSignature);
  28666. }
  28667. self.state = "HEADER";
  28668. }
  28669. var alreadyCollectedLine = false;
  28670. while (self.buffer) {
  28671. // We can't parse a line until we have the full line.
  28672. if (!/\r\n|\n/.test(self.buffer)) {
  28673. return this;
  28674. }
  28675. if (!alreadyCollectedLine) {
  28676. line = collectNextLine();
  28677. } else {
  28678. alreadyCollectedLine = false;
  28679. }
  28680. switch (self.state) {
  28681. case "HEADER":
  28682. // 13-18 - Allow a header (metadata) under the WEBVTT line.
  28683. if (/:/.test(line)) {
  28684. parseHeader(line);
  28685. } else if (!line) {
  28686. // An empty line terminates the header and starts the body (cues).
  28687. self.state = "ID";
  28688. }
  28689. continue;
  28690. case "NOTE":
  28691. // Ignore NOTE blocks.
  28692. if (!line) {
  28693. self.state = "ID";
  28694. }
  28695. continue;
  28696. case "ID":
  28697. // Check for the start of NOTE blocks.
  28698. if (/^NOTE($|[ \t])/.test(line)) {
  28699. self.state = "NOTE";
  28700. break;
  28701. }
  28702. // 19-29 - Allow any number of line terminators, then initialize new cue values.
  28703. if (!line) {
  28704. continue;
  28705. }
  28706. self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
  28707. self.state = "CUE";
  28708. // 30-39 - Check if self line contains an optional identifier or timing data.
  28709. if (line.indexOf("-->") === -1) {
  28710. self.cue.id = line;
  28711. continue;
  28712. }
  28713. // Process line as start of a cue.
  28714. /*falls through*/
  28715. case "CUE":
  28716. // 40 - Collect cue timings and settings.
  28717. try {
  28718. parseCue(line, self.cue, self.regionList);
  28719. } catch (e) {
  28720. self.reportOrThrowError(e);
  28721. // In case of an error ignore rest of the cue.
  28722. self.cue = null;
  28723. self.state = "BADCUE";
  28724. continue;
  28725. }
  28726. self.state = "CUETEXT";
  28727. continue;
  28728. case "CUETEXT":
  28729. var hasSubstring = line.indexOf("-->") !== -1;
  28730. // 34 - If we have an empty line then report the cue.
  28731. // 35 - If we have the special substring '-->' then report the cue,
  28732. // but do not collect the line as we need to process the current
  28733. // one as a new cue.
  28734. if (!line || hasSubstring && (alreadyCollectedLine = true)) {
  28735. // We are done parsing self cue.
  28736. self.oncue && self.oncue(self.cue);
  28737. self.cue = null;
  28738. self.state = "ID";
  28739. continue;
  28740. }
  28741. if (self.cue.text) {
  28742. self.cue.text += "\n";
  28743. }
  28744. self.cue.text += line;
  28745. continue;
  28746. case "BADCUE": // BADCUE
  28747. // 54-62 - Collect and discard the remaining cue.
  28748. if (!line) {
  28749. self.state = "ID";
  28750. }
  28751. continue;
  28752. }
  28753. }
  28754. } catch (e) {
  28755. self.reportOrThrowError(e);
  28756. // If we are currently parsing a cue, report what we have.
  28757. if (self.state === "CUETEXT" && self.cue && self.oncue) {
  28758. self.oncue(self.cue);
  28759. }
  28760. self.cue = null;
  28761. // Enter BADWEBVTT state if header was not parsed correctly otherwise
  28762. // another exception occurred so enter BADCUE state.
  28763. self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
  28764. }
  28765. return this;
  28766. },
  28767. flush: function () {
  28768. var self = this;
  28769. try {
  28770. // Finish decoding the stream.
  28771. self.buffer += self.decoder.decode();
  28772. // Synthesize the end of the current cue or region.
  28773. if (self.cue || self.state === "HEADER") {
  28774. self.buffer += "\n\n";
  28775. self.parse();
  28776. }
  28777. // If we've flushed, parsed, and we're still on the INITIAL state then
  28778. // that means we don't have enough of the stream to parse the first
  28779. // line.
  28780. if (self.state === "INITIAL") {
  28781. throw new ParsingError(ParsingError.Errors.BadSignature);
  28782. }
  28783. } catch(e) {
  28784. self.reportOrThrowError(e);
  28785. }
  28786. self.onflush && self.onflush();
  28787. return this;
  28788. }
  28789. };
  28790. module.exports = WebVTT;
  28791. },{}],140:[function(require,module,exports){
  28792. /**
  28793. * Copyright 2013 vtt.js Contributors
  28794. *
  28795. * Licensed under the Apache License, Version 2.0 (the "License");
  28796. * you may not use this file except in compliance with the License.
  28797. * You may obtain a copy of the License at
  28798. *
  28799. * http://www.apache.org/licenses/LICENSE-2.0
  28800. *
  28801. * Unless required by applicable law or agreed to in writing, software
  28802. * distributed under the License is distributed on an "AS IS" BASIS,
  28803. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  28804. * See the License for the specific language governing permissions and
  28805. * limitations under the License.
  28806. */
  28807. var autoKeyword = "auto";
  28808. var directionSetting = {
  28809. "": true,
  28810. "lr": true,
  28811. "rl": true
  28812. };
  28813. var alignSetting = {
  28814. "start": true,
  28815. "middle": true,
  28816. "end": true,
  28817. "left": true,
  28818. "right": true
  28819. };
  28820. function findDirectionSetting(value) {
  28821. if (typeof value !== "string") {
  28822. return false;
  28823. }
  28824. var dir = directionSetting[value.toLowerCase()];
  28825. return dir ? value.toLowerCase() : false;
  28826. }
  28827. function findAlignSetting(value) {
  28828. if (typeof value !== "string") {
  28829. return false;
  28830. }
  28831. var align = alignSetting[value.toLowerCase()];
  28832. return align ? value.toLowerCase() : false;
  28833. }
  28834. function extend(obj) {
  28835. var i = 1;
  28836. for (; i < arguments.length; i++) {
  28837. var cobj = arguments[i];
  28838. for (var p in cobj) {
  28839. obj[p] = cobj[p];
  28840. }
  28841. }
  28842. return obj;
  28843. }
  28844. function VTTCue(startTime, endTime, text) {
  28845. var cue = this;
  28846. var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
  28847. var baseObj = {};
  28848. if (isIE8) {
  28849. cue = document.createElement('custom');
  28850. } else {
  28851. baseObj.enumerable = true;
  28852. }
  28853. /**
  28854. * Shim implementation specific properties. These properties are not in
  28855. * the spec.
  28856. */
  28857. // Lets us know when the VTTCue's data has changed in such a way that we need
  28858. // to recompute its display state. This lets us compute its display state
  28859. // lazily.
  28860. cue.hasBeenReset = false;
  28861. /**
  28862. * VTTCue and TextTrackCue properties
  28863. * http://dev.w3.org/html5/webvtt/#vttcue-interface
  28864. */
  28865. var _id = "";
  28866. var _pauseOnExit = false;
  28867. var _startTime = startTime;
  28868. var _endTime = endTime;
  28869. var _text = text;
  28870. var _region = null;
  28871. var _vertical = "";
  28872. var _snapToLines = true;
  28873. var _line = "auto";
  28874. var _lineAlign = "start";
  28875. var _position = 50;
  28876. var _positionAlign = "middle";
  28877. var _size = 50;
  28878. var _align = "middle";
  28879. Object.defineProperty(cue,
  28880. "id", extend({}, baseObj, {
  28881. get: function() {
  28882. return _id;
  28883. },
  28884. set: function(value) {
  28885. _id = "" + value;
  28886. }
  28887. }));
  28888. Object.defineProperty(cue,
  28889. "pauseOnExit", extend({}, baseObj, {
  28890. get: function() {
  28891. return _pauseOnExit;
  28892. },
  28893. set: function(value) {
  28894. _pauseOnExit = !!value;
  28895. }
  28896. }));
  28897. Object.defineProperty(cue,
  28898. "startTime", extend({}, baseObj, {
  28899. get: function() {
  28900. return _startTime;
  28901. },
  28902. set: function(value) {
  28903. if (typeof value !== "number") {
  28904. throw new TypeError("Start time must be set to a number.");
  28905. }
  28906. _startTime = value;
  28907. this.hasBeenReset = true;
  28908. }
  28909. }));
  28910. Object.defineProperty(cue,
  28911. "endTime", extend({}, baseObj, {
  28912. get: function() {
  28913. return _endTime;
  28914. },
  28915. set: function(value) {
  28916. if (typeof value !== "number") {
  28917. throw new TypeError("End time must be set to a number.");
  28918. }
  28919. _endTime = value;
  28920. this.hasBeenReset = true;
  28921. }
  28922. }));
  28923. Object.defineProperty(cue,
  28924. "text", extend({}, baseObj, {
  28925. get: function() {
  28926. return _text;
  28927. },
  28928. set: function(value) {
  28929. _text = "" + value;
  28930. this.hasBeenReset = true;
  28931. }
  28932. }));
  28933. Object.defineProperty(cue,
  28934. "region", extend({}, baseObj, {
  28935. get: function() {
  28936. return _region;
  28937. },
  28938. set: function(value) {
  28939. _region = value;
  28940. this.hasBeenReset = true;
  28941. }
  28942. }));
  28943. Object.defineProperty(cue,
  28944. "vertical", extend({}, baseObj, {
  28945. get: function() {
  28946. return _vertical;
  28947. },
  28948. set: function(value) {
  28949. var setting = findDirectionSetting(value);
  28950. // Have to check for false because the setting an be an empty string.
  28951. if (setting === false) {
  28952. throw new SyntaxError("An invalid or illegal string was specified.");
  28953. }
  28954. _vertical = setting;
  28955. this.hasBeenReset = true;
  28956. }
  28957. }));
  28958. Object.defineProperty(cue,
  28959. "snapToLines", extend({}, baseObj, {
  28960. get: function() {
  28961. return _snapToLines;
  28962. },
  28963. set: function(value) {
  28964. _snapToLines = !!value;
  28965. this.hasBeenReset = true;
  28966. }
  28967. }));
  28968. Object.defineProperty(cue,
  28969. "line", extend({}, baseObj, {
  28970. get: function() {
  28971. return _line;
  28972. },
  28973. set: function(value) {
  28974. if (typeof value !== "number" && value !== autoKeyword) {
  28975. throw new SyntaxError("An invalid number or illegal string was specified.");
  28976. }
  28977. _line = value;
  28978. this.hasBeenReset = true;
  28979. }
  28980. }));
  28981. Object.defineProperty(cue,
  28982. "lineAlign", extend({}, baseObj, {
  28983. get: function() {
  28984. return _lineAlign;
  28985. },
  28986. set: function(value) {
  28987. var setting = findAlignSetting(value);
  28988. if (!setting) {
  28989. throw new SyntaxError("An invalid or illegal string was specified.");
  28990. }
  28991. _lineAlign = setting;
  28992. this.hasBeenReset = true;
  28993. }
  28994. }));
  28995. Object.defineProperty(cue,
  28996. "position", extend({}, baseObj, {
  28997. get: function() {
  28998. return _position;
  28999. },
  29000. set: function(value) {
  29001. if (value < 0 || value > 100) {
  29002. throw new Error("Position must be between 0 and 100.");
  29003. }
  29004. _position = value;
  29005. this.hasBeenReset = true;
  29006. }
  29007. }));
  29008. Object.defineProperty(cue,
  29009. "positionAlign", extend({}, baseObj, {
  29010. get: function() {
  29011. return _positionAlign;
  29012. },
  29013. set: function(value) {
  29014. var setting = findAlignSetting(value);
  29015. if (!setting) {
  29016. throw new SyntaxError("An invalid or illegal string was specified.");
  29017. }
  29018. _positionAlign = setting;
  29019. this.hasBeenReset = true;
  29020. }
  29021. }));
  29022. Object.defineProperty(cue,
  29023. "size", extend({}, baseObj, {
  29024. get: function() {
  29025. return _size;
  29026. },
  29027. set: function(value) {
  29028. if (value < 0 || value > 100) {
  29029. throw new Error("Size must be between 0 and 100.");
  29030. }
  29031. _size = value;
  29032. this.hasBeenReset = true;
  29033. }
  29034. }));
  29035. Object.defineProperty(cue,
  29036. "align", extend({}, baseObj, {
  29037. get: function() {
  29038. return _align;
  29039. },
  29040. set: function(value) {
  29041. var setting = findAlignSetting(value);
  29042. if (!setting) {
  29043. throw new SyntaxError("An invalid or illegal string was specified.");
  29044. }
  29045. _align = setting;
  29046. this.hasBeenReset = true;
  29047. }
  29048. }));
  29049. /**
  29050. * Other <track> spec defined properties
  29051. */
  29052. // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
  29053. cue.displayState = undefined;
  29054. if (isIE8) {
  29055. return cue;
  29056. }
  29057. }
  29058. /**
  29059. * VTTCue methods
  29060. */
  29061. VTTCue.prototype.getCueAsHTML = function() {
  29062. // Assume WebVTT.convertCueToDOMTree is on the global.
  29063. return WebVTT.convertCueToDOMTree(window, this.text);
  29064. };
  29065. module.exports = VTTCue;
  29066. },{}],141:[function(require,module,exports){
  29067. /**
  29068. * Copyright 2013 vtt.js Contributors
  29069. *
  29070. * Licensed under the Apache License, Version 2.0 (the "License");
  29071. * you may not use this file except in compliance with the License.
  29072. * You may obtain a copy of the License at
  29073. *
  29074. * http://www.apache.org/licenses/LICENSE-2.0
  29075. *
  29076. * Unless required by applicable law or agreed to in writing, software
  29077. * distributed under the License is distributed on an "AS IS" BASIS,
  29078. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  29079. * See the License for the specific language governing permissions and
  29080. * limitations under the License.
  29081. */
  29082. var scrollSetting = {
  29083. "": true,
  29084. "up": true
  29085. };
  29086. function findScrollSetting(value) {
  29087. if (typeof value !== "string") {
  29088. return false;
  29089. }
  29090. var scroll = scrollSetting[value.toLowerCase()];
  29091. return scroll ? value.toLowerCase() : false;
  29092. }
  29093. function isValidPercentValue(value) {
  29094. return typeof value === "number" && (value >= 0 && value <= 100);
  29095. }
  29096. // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
  29097. function VTTRegion() {
  29098. var _width = 100;
  29099. var _lines = 3;
  29100. var _regionAnchorX = 0;
  29101. var _regionAnchorY = 100;
  29102. var _viewportAnchorX = 0;
  29103. var _viewportAnchorY = 100;
  29104. var _scroll = "";
  29105. Object.defineProperties(this, {
  29106. "width": {
  29107. enumerable: true,
  29108. get: function() {
  29109. return _width;
  29110. },
  29111. set: function(value) {
  29112. if (!isValidPercentValue(value)) {
  29113. throw new Error("Width must be between 0 and 100.");
  29114. }
  29115. _width = value;
  29116. }
  29117. },
  29118. "lines": {
  29119. enumerable: true,
  29120. get: function() {
  29121. return _lines;
  29122. },
  29123. set: function(value) {
  29124. if (typeof value !== "number") {
  29125. throw new TypeError("Lines must be set to a number.");
  29126. }
  29127. _lines = value;
  29128. }
  29129. },
  29130. "regionAnchorY": {
  29131. enumerable: true,
  29132. get: function() {
  29133. return _regionAnchorY;
  29134. },
  29135. set: function(value) {
  29136. if (!isValidPercentValue(value)) {
  29137. throw new Error("RegionAnchorX must be between 0 and 100.");
  29138. }
  29139. _regionAnchorY = value;
  29140. }
  29141. },
  29142. "regionAnchorX": {
  29143. enumerable: true,
  29144. get: function() {
  29145. return _regionAnchorX;
  29146. },
  29147. set: function(value) {
  29148. if(!isValidPercentValue(value)) {
  29149. throw new Error("RegionAnchorY must be between 0 and 100.");
  29150. }
  29151. _regionAnchorX = value;
  29152. }
  29153. },
  29154. "viewportAnchorY": {
  29155. enumerable: true,
  29156. get: function() {
  29157. return _viewportAnchorY;
  29158. },
  29159. set: function(value) {
  29160. if (!isValidPercentValue(value)) {
  29161. throw new Error("ViewportAnchorY must be between 0 and 100.");
  29162. }
  29163. _viewportAnchorY = value;
  29164. }
  29165. },
  29166. "viewportAnchorX": {
  29167. enumerable: true,
  29168. get: function() {
  29169. return _viewportAnchorX;
  29170. },
  29171. set: function(value) {
  29172. if (!isValidPercentValue(value)) {
  29173. throw new Error("ViewportAnchorX must be between 0 and 100.");
  29174. }
  29175. _viewportAnchorX = value;
  29176. }
  29177. },
  29178. "scroll": {
  29179. enumerable: true,
  29180. get: function() {
  29181. return _scroll;
  29182. },
  29183. set: function(value) {
  29184. var setting = findScrollSetting(value);
  29185. // Have to check for false as an empty string is a legal value.
  29186. if (setting === false) {
  29187. throw new SyntaxError("An invalid or illegal string was specified.");
  29188. }
  29189. _scroll = setting;
  29190. }
  29191. }
  29192. });
  29193. }
  29194. module.exports = VTTRegion;
  29195. },{}],142:[function(require,module,exports){
  29196. // By default assume browserify was used to bundle app. These arguments are passed to
  29197. // the module by browserify.
  29198. var bundleFn = arguments[3];
  29199. var sources = arguments[4];
  29200. var cache = arguments[5];
  29201. var stringify = JSON.stringify;
  29202. var webpack = false;
  29203. // webpackBootstrap
  29204. var webpackBootstrapFn = function(modules) {
  29205. // The module cache
  29206. var installedModules = {};
  29207. // The require function
  29208. function __webpack_require__(moduleId) {
  29209. // Check if module is in cache
  29210. if(installedModules[moduleId]) {
  29211. return installedModules[moduleId].exports;
  29212. }
  29213. // Create a new module (and put it into the cache)
  29214. var module = installedModules[moduleId] = {
  29215. i: moduleId,
  29216. l: false,
  29217. exports: {}
  29218. };
  29219. // Execute the module function
  29220. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  29221. // Flag the module as loaded
  29222. module.l = true;
  29223. // Return the exports of the module
  29224. return module.exports;
  29225. }
  29226. // expose the modules object (__webpack_modules__)
  29227. __webpack_require__.m = modules;
  29228. // expose the module cache
  29229. __webpack_require__.c = installedModules;
  29230. // define getter function for harmony exports
  29231. __webpack_require__.d = function(exports, name, getter) {
  29232. if(!__webpack_require__.o(exports, name)) {
  29233. Object.defineProperty(exports, name, {
  29234. configurable: false,
  29235. enumerable: true,
  29236. get: getter
  29237. });
  29238. }
  29239. };
  29240. // getDefaultExport function for compatibility with non-harmony modules
  29241. __webpack_require__.n = function(module) {
  29242. var getter = module && module.__esModule ?
  29243. function getDefault() { return module['default']; } :
  29244. function getModuleExports() { return module; };
  29245. __webpack_require__.d(getter, 'a', getter);
  29246. return getter;
  29247. };
  29248. // Object.prototype.hasOwnProperty.call
  29249. __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  29250. // __webpack_public_path__
  29251. __webpack_require__.p = "";
  29252. // Load entry module and return exports
  29253. return __webpack_require__(__webpack_require__.s = entryModule);
  29254. }
  29255. if (typeof bundleFn === 'undefined') {
  29256. // Assume this was bundled with webpack and not browserify
  29257. webpack = true;
  29258. bundleFn = webpackBootstrapFn;
  29259. sources = __webpack_modules__;
  29260. }
  29261. var bundleWithBrowserify = function(fn) {
  29262. // with browserify we must find the module key ourselves
  29263. var cacheKeys = Object.keys(cache);
  29264. var fnModuleKey;
  29265. for (var i = 0; i < cacheKeys.length; i++) {
  29266. var cacheKey = cacheKeys[i];
  29267. var cacheExports = cache[cacheKey].exports;
  29268. // Using babel as a transpiler to use esmodule, the export will always
  29269. // be an object with the default export as a property of it. To ensure
  29270. // the existing api and babel esmodule exports are both supported we
  29271. // check for both
  29272. if (cacheExports === fn || cacheExports && cacheExports.default === fn) {
  29273. fnModuleKey = cacheKey;
  29274. break;
  29275. }
  29276. }
  29277. // if we couldn't find one, lets make one
  29278. if (!fnModuleKey) {
  29279. fnModuleKey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  29280. var fnModuleCache = {};
  29281. for (var i = 0; i < cacheKeys.length; i++) {
  29282. var cacheKey = cacheKeys[i];
  29283. fnModuleCache[cacheKey] = cacheKey;
  29284. }
  29285. sources[fnModuleKey] = [
  29286. 'function(require,module,exports){' + fn + '(self); }',
  29287. fnModuleCache
  29288. ];
  29289. }
  29290. var entryKey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
  29291. var entryCache = {};
  29292. entryCache[fnModuleKey] = fnModuleKey;
  29293. sources[entryKey] = [
  29294. 'function(require,module,exports){' +
  29295. // try to call default if defined to also support babel esmodule exports
  29296. 'var f = require(' + stringify(fnModuleKey) + ');' +
  29297. '(f.default ? f.default : f)(self);' +
  29298. '}',
  29299. entryCache
  29300. ];
  29301. return '(' + bundleFn + ')({'
  29302. + Object.keys(sources).map(function(key) {
  29303. return stringify(key) + ':['
  29304. + sources[key][0] + ','
  29305. + stringify(sources[key][1]) + ']';
  29306. }).join(',')
  29307. + '},{},[' + stringify(entryKey) + '])';
  29308. };
  29309. var bundleWithWebpack = function(fn, fnModuleId) {
  29310. var devMode = typeof fnModuleId === 'string';
  29311. var sourceStrings;
  29312. if (devMode) {
  29313. sourceStrings = {};
  29314. } else {
  29315. sourceStrings = [];
  29316. }
  29317. Object.keys(sources).forEach(function(sKey) {
  29318. if (!sources[sKey]) {
  29319. return;
  29320. }
  29321. sourceStrings[sKey] = sources[sKey].toString();
  29322. });
  29323. var fnModuleExports = __webpack_require__(fnModuleId);
  29324. // Using babel as a transpiler to use esmodule, the export will always
  29325. // be an object with the default export as a property of it. To ensure
  29326. // the existing api and babel esmodule exports are both supported we
  29327. // check for both
  29328. if (!(fnModuleExports && (fnModuleExports === fn || fnModuleExports.default === fn))) {
  29329. var fnSourceString = sourceStrings[fnModuleId];
  29330. sourceStrings[fnModuleId] = fnSourceString.substring(0, fnSourceString.length - 1) +
  29331. '\n' + fn.name + '();\n}';
  29332. }
  29333. var modulesString;
  29334. if (devMode) {
  29335. // must escape quotes to support webpack loader options
  29336. fnModuleId = stringify(fnModuleId);
  29337. // dev mode in webpack4, modules are passed as an object
  29338. var mappedSourceStrings = Object.keys(sourceStrings).map(function(sKey) {
  29339. return stringify(sKey) + ':' + sourceStrings[sKey];
  29340. });
  29341. modulesString = '{' + mappedSourceStrings.join(',') + '}';
  29342. } else {
  29343. modulesString = '[' + sourceStrings.join(',') + ']';
  29344. }
  29345. return 'var fn = (' + bundleFn.toString().replace('entryModule', fnModuleId) + ')('
  29346. + modulesString
  29347. + ');\n'
  29348. // not a function when calling a function from the current scope
  29349. + '(typeof fn === "function") && fn(self);';
  29350. };
  29351. module.exports = function webwackify(fn, fnModuleId) {
  29352. var src;
  29353. if (webpack) {
  29354. src = bundleWithWebpack(fn, fnModuleId);
  29355. } else {
  29356. src = bundleWithBrowserify(fn);
  29357. }
  29358. var blob = new Blob([src], { type: 'text/javascript' });
  29359. var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
  29360. var workerUrl = URL.createObjectURL(blob);
  29361. var worker = new Worker(workerUrl);
  29362. worker.objectURL = workerUrl;
  29363. return worker;
  29364. };
  29365. },{}],143:[function(require,module,exports){
  29366. "use strict";
  29367. var window = require("global/window")
  29368. var isFunction = require("is-function")
  29369. var parseHeaders = require("parse-headers")
  29370. var xtend = require("xtend")
  29371. module.exports = createXHR
  29372. createXHR.XMLHttpRequest = window.XMLHttpRequest || noop
  29373. createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest
  29374. forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
  29375. createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
  29376. options = initParams(uri, options, callback)
  29377. options.method = method.toUpperCase()
  29378. return _createXHR(options)
  29379. }
  29380. })
  29381. function forEachArray(array, iterator) {
  29382. for (var i = 0; i < array.length; i++) {
  29383. iterator(array[i])
  29384. }
  29385. }
  29386. function isEmpty(obj){
  29387. for(var i in obj){
  29388. if(obj.hasOwnProperty(i)) return false
  29389. }
  29390. return true
  29391. }
  29392. function initParams(uri, options, callback) {
  29393. var params = uri
  29394. if (isFunction(options)) {
  29395. callback = options
  29396. if (typeof uri === "string") {
  29397. params = {uri:uri}
  29398. }
  29399. } else {
  29400. params = xtend(options, {uri: uri})
  29401. }
  29402. params.callback = callback
  29403. return params
  29404. }
  29405. function createXHR(uri, options, callback) {
  29406. options = initParams(uri, options, callback)
  29407. return _createXHR(options)
  29408. }
  29409. function _createXHR(options) {
  29410. if(typeof options.callback === "undefined"){
  29411. throw new Error("callback argument missing")
  29412. }
  29413. var called = false
  29414. var callback = function cbOnce(err, response, body){
  29415. if(!called){
  29416. called = true
  29417. options.callback(err, response, body)
  29418. }
  29419. }
  29420. function readystatechange() {
  29421. if (xhr.readyState === 4) {
  29422. loadFunc()
  29423. }
  29424. }
  29425. function getBody() {
  29426. // Chrome with requestType=blob throws errors arround when even testing access to responseText
  29427. var body = undefined
  29428. if (xhr.response) {
  29429. body = xhr.response
  29430. } else {
  29431. body = xhr.responseText || getXml(xhr)
  29432. }
  29433. if (isJson) {
  29434. try {
  29435. body = JSON.parse(body)
  29436. } catch (e) {}
  29437. }
  29438. return body
  29439. }
  29440. var failureResponse = {
  29441. body: undefined,
  29442. headers: {},
  29443. statusCode: 0,
  29444. method: method,
  29445. url: uri,
  29446. rawRequest: xhr
  29447. }
  29448. function errorFunc(evt) {
  29449. clearTimeout(timeoutTimer)
  29450. if(!(evt instanceof Error)){
  29451. evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") )
  29452. }
  29453. evt.statusCode = 0
  29454. return callback(evt, failureResponse)
  29455. }
  29456. // will load the data & process the response in a special response object
  29457. function loadFunc() {
  29458. if (aborted) return
  29459. var status
  29460. clearTimeout(timeoutTimer)
  29461. if(options.useXDR && xhr.status===undefined) {
  29462. //IE8 CORS GET successful response doesn't have a status field, but body is fine
  29463. status = 200
  29464. } else {
  29465. status = (xhr.status === 1223 ? 204 : xhr.status)
  29466. }
  29467. var response = failureResponse
  29468. var err = null
  29469. if (status !== 0){
  29470. response = {
  29471. body: getBody(),
  29472. statusCode: status,
  29473. method: method,
  29474. headers: {},
  29475. url: uri,
  29476. rawRequest: xhr
  29477. }
  29478. if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
  29479. response.headers = parseHeaders(xhr.getAllResponseHeaders())
  29480. }
  29481. } else {
  29482. err = new Error("Internal XMLHttpRequest Error")
  29483. }
  29484. return callback(err, response, response.body)
  29485. }
  29486. var xhr = options.xhr || null
  29487. if (!xhr) {
  29488. if (options.cors || options.useXDR) {
  29489. xhr = new createXHR.XDomainRequest()
  29490. }else{
  29491. xhr = new createXHR.XMLHttpRequest()
  29492. }
  29493. }
  29494. var key
  29495. var aborted
  29496. var uri = xhr.url = options.uri || options.url
  29497. var method = xhr.method = options.method || "GET"
  29498. var body = options.body || options.data || null
  29499. var headers = xhr.headers = options.headers || {}
  29500. var sync = !!options.sync
  29501. var isJson = false
  29502. var timeoutTimer
  29503. if ("json" in options) {
  29504. isJson = true
  29505. headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user
  29506. if (method !== "GET" && method !== "HEAD") {
  29507. headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user
  29508. body = JSON.stringify(options.json)
  29509. }
  29510. }
  29511. xhr.onreadystatechange = readystatechange
  29512. xhr.onload = loadFunc
  29513. xhr.onerror = errorFunc
  29514. // IE9 must have onprogress be set to a unique function.
  29515. xhr.onprogress = function () {
  29516. // IE must die
  29517. }
  29518. xhr.ontimeout = errorFunc
  29519. xhr.open(method, uri, !sync, options.username, options.password)
  29520. //has to be after open
  29521. if(!sync) {
  29522. xhr.withCredentials = !!options.withCredentials
  29523. }
  29524. // Cannot set timeout with sync request
  29525. // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
  29526. // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
  29527. if (!sync && options.timeout > 0 ) {
  29528. timeoutTimer = setTimeout(function(){
  29529. aborted=true//IE9 may still call readystatechange
  29530. xhr.abort("timeout")
  29531. var e = new Error("XMLHttpRequest timeout")
  29532. e.code = "ETIMEDOUT"
  29533. errorFunc(e)
  29534. }, options.timeout )
  29535. }
  29536. if (xhr.setRequestHeader) {
  29537. for(key in headers){
  29538. if(headers.hasOwnProperty(key)){
  29539. xhr.setRequestHeader(key, headers[key])
  29540. }
  29541. }
  29542. } else if (options.headers && !isEmpty(options.headers)) {
  29543. throw new Error("Headers cannot be set on an XDomainRequest object")
  29544. }
  29545. if ("responseType" in options) {
  29546. xhr.responseType = options.responseType
  29547. }
  29548. if ("beforeSend" in options &&
  29549. typeof options.beforeSend === "function"
  29550. ) {
  29551. options.beforeSend(xhr)
  29552. }
  29553. xhr.send(body)
  29554. return xhr
  29555. }
  29556. function getXml(xhr) {
  29557. if (xhr.responseType === "document") {
  29558. return xhr.responseXML
  29559. }
  29560. var firefoxBugTakenEffect = xhr.status === 204 && xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"
  29561. if (xhr.responseType === "" && !firefoxBugTakenEffect) {
  29562. return xhr.responseXML
  29563. }
  29564. return null
  29565. }
  29566. function noop() {}
  29567. },{"global/window":16,"is-function":17,"parse-headers":39,"xtend":144}],144:[function(require,module,exports){
  29568. module.exports = extend
  29569. var hasOwnProperty = Object.prototype.hasOwnProperty;
  29570. function extend() {
  29571. var target = {}
  29572. for (var i = 0; i < arguments.length; i++) {
  29573. var source = arguments[i]
  29574. for (var key in source) {
  29575. if (hasOwnProperty.call(source, key)) {
  29576. target[key] = source[key]
  29577. }
  29578. }
  29579. }
  29580. return target
  29581. }
  29582. },{}],145:[function(require,module,exports){
  29583. /* eslint-disable no-var */
  29584. /* eslint-env qunit */
  29585. var mediaSources = require('../es5/videojs-contrib-media-sources.js');
  29586. var q = window.QUnit;
  29587. q.module('Browserify Require');
  29588. q.test('mediaSources should be requirable and bundled via browserify', function(assert) {
  29589. assert.ok(mediaSources, 'videojs-contrib-media-sources is required properly');
  29590. });
  29591. },{"../es5/videojs-contrib-media-sources.js":11}]},{},[145]);