video.js 748 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653
  1. /**
  2. * @license
  3. * Video.js 6.13.0 <http://videojs.com/>
  4. * Copyright Brightcove, Inc. <https://www.brightcove.com/>
  5. * Available under Apache License Version 2.0
  6. * <https://github.com/videojs/video.js/blob/master/LICENSE>
  7. *
  8. * Includes vtt.js <https://github.com/mozilla/vtt.js>
  9. * Available under Apache License Version 2.0
  10. * <https://github.com/mozilla/vtt.js/blob/master/LICENSE>
  11. */
  12. (function (global, factory) {
  13. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  14. typeof define === 'function' && define.amd ? define(factory) :
  15. (global.videojs = factory());
  16. }(this, (function () {
  17. var version = "6.13.0";
  18. var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  19. function createCommonjsModule(fn, module) {
  20. return module = { exports: {} }, fn(module, module.exports), module.exports;
  21. }
  22. var win;
  23. if (typeof window !== "undefined") {
  24. win = window;
  25. } else if (typeof commonjsGlobal !== "undefined") {
  26. win = commonjsGlobal;
  27. } else if (typeof self !== "undefined"){
  28. win = self;
  29. } else {
  30. win = {};
  31. }
  32. var window_1 = win;
  33. var empty = {};
  34. var empty$1 = (Object.freeze || Object)({
  35. 'default': empty
  36. });
  37. var minDoc = ( empty$1 && empty ) || empty$1;
  38. var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal :
  39. typeof window !== 'undefined' ? window : {};
  40. var doccy;
  41. if (typeof document !== 'undefined') {
  42. doccy = document;
  43. } else {
  44. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  45. if (!doccy) {
  46. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  47. }
  48. }
  49. var document_1 = doccy;
  50. /**
  51. * @file browser.js
  52. * @module browser
  53. */
  54. var USER_AGENT = window_1.navigator && window_1.navigator.userAgent || '';
  55. var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
  56. var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
  57. /*
  58. * Device is an iPhone
  59. *
  60. * @type {Boolean}
  61. * @constant
  62. * @private
  63. */
  64. var IS_IPAD = /iPad/i.test(USER_AGENT);
  65. // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
  66. // to identify iPhones, we need to exclude iPads.
  67. // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
  68. var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
  69. var IS_IPOD = /iPod/i.test(USER_AGENT);
  70. var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
  71. var IOS_VERSION = function () {
  72. var match = USER_AGENT.match(/OS (\d+)_/i);
  73. if (match && match[1]) {
  74. return match[1];
  75. }
  76. return null;
  77. }();
  78. var IS_ANDROID = /Android/i.test(USER_AGENT);
  79. var ANDROID_VERSION = function () {
  80. // This matches Android Major.Minor.Patch versions
  81. // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
  82. var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
  83. if (!match) {
  84. return null;
  85. }
  86. var major = match[1] && parseFloat(match[1]);
  87. var minor = match[2] && parseFloat(match[2]);
  88. if (major && minor) {
  89. return parseFloat(match[1] + '.' + match[2]);
  90. } else if (major) {
  91. return major;
  92. }
  93. return null;
  94. }();
  95. // Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
  96. var IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
  97. var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
  98. var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
  99. var IS_EDGE = /Edge/i.test(USER_AGENT);
  100. var IS_CHROME = !IS_EDGE && (/Chrome/i.test(USER_AGENT) || /CriOS/i.test(USER_AGENT));
  101. var CHROME_VERSION = function () {
  102. var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
  103. if (match && match[2]) {
  104. return parseFloat(match[2]);
  105. }
  106. return null;
  107. }();
  108. var IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);
  109. var IE_VERSION = function () {
  110. var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
  111. var version = result && parseFloat(result[1]);
  112. if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
  113. // IE 11 has a different user agent string than other IE versions
  114. version = 11.0;
  115. }
  116. return version;
  117. }();
  118. var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
  119. var IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;
  120. var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || window_1.navigator.maxTouchPoints || window_1.DocumentTouch && window_1.document instanceof window_1.DocumentTouch);
  121. var BACKGROUND_SIZE_SUPPORTED = isReal() && 'backgroundSize' in window_1.document.createElement('video').style;
  122. var browser = (Object.freeze || Object)({
  123. IS_IPAD: IS_IPAD,
  124. IS_IPHONE: IS_IPHONE,
  125. IS_IPOD: IS_IPOD,
  126. IS_IOS: IS_IOS,
  127. IOS_VERSION: IOS_VERSION,
  128. IS_ANDROID: IS_ANDROID,
  129. ANDROID_VERSION: ANDROID_VERSION,
  130. IS_OLD_ANDROID: IS_OLD_ANDROID,
  131. IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
  132. IS_FIREFOX: IS_FIREFOX,
  133. IS_EDGE: IS_EDGE,
  134. IS_CHROME: IS_CHROME,
  135. CHROME_VERSION: CHROME_VERSION,
  136. IS_IE8: IS_IE8,
  137. IE_VERSION: IE_VERSION,
  138. IS_SAFARI: IS_SAFARI,
  139. IS_ANY_SAFARI: IS_ANY_SAFARI,
  140. TOUCH_ENABLED: TOUCH_ENABLED,
  141. BACKGROUND_SIZE_SUPPORTED: BACKGROUND_SIZE_SUPPORTED
  142. });
  143. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  144. return typeof obj;
  145. } : function (obj) {
  146. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  147. };
  148. var classCallCheck = function (instance, Constructor) {
  149. if (!(instance instanceof Constructor)) {
  150. throw new TypeError("Cannot call a class as a function");
  151. }
  152. };
  153. var inherits = function (subClass, superClass) {
  154. if (typeof superClass !== "function" && superClass !== null) {
  155. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  156. }
  157. subClass.prototype = Object.create(superClass && superClass.prototype, {
  158. constructor: {
  159. value: subClass,
  160. enumerable: false,
  161. writable: true,
  162. configurable: true
  163. }
  164. });
  165. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  166. };
  167. var possibleConstructorReturn = function (self, call) {
  168. if (!self) {
  169. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  170. }
  171. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  172. };
  173. var taggedTemplateLiteralLoose = function (strings, raw) {
  174. strings.raw = raw;
  175. return strings;
  176. };
  177. /**
  178. * @file obj.js
  179. * @module obj
  180. */
  181. /**
  182. * @callback obj:EachCallback
  183. *
  184. * @param {Mixed} value
  185. * The current key for the object that is being iterated over.
  186. *
  187. * @param {string} key
  188. * The current key-value for object that is being iterated over
  189. */
  190. /**
  191. * @callback obj:ReduceCallback
  192. *
  193. * @param {Mixed} accum
  194. * The value that is accumulating over the reduce loop.
  195. *
  196. * @param {Mixed} value
  197. * The current key for the object that is being iterated over.
  198. *
  199. * @param {string} key
  200. * The current key-value for object that is being iterated over
  201. *
  202. * @return {Mixed}
  203. * The new accumulated value.
  204. */
  205. var toString = Object.prototype.toString;
  206. /**
  207. * Get the keys of an Object
  208. *
  209. * @param {Object}
  210. * The Object to get the keys from
  211. *
  212. * @return {string[]}
  213. * An array of the keys from the object. Returns an empty array if the
  214. * object passed in was invalid or had no keys.
  215. *
  216. * @private
  217. */
  218. var keys = function keys(object) {
  219. return isObject(object) ? Object.keys(object) : [];
  220. };
  221. /**
  222. * Array-like iteration for objects.
  223. *
  224. * @param {Object} object
  225. * The object to iterate over
  226. *
  227. * @param {obj:EachCallback} fn
  228. * The callback function which is called for each key in the object.
  229. */
  230. function each(object, fn) {
  231. keys(object).forEach(function (key) {
  232. return fn(object[key], key);
  233. });
  234. }
  235. /**
  236. * Array-like reduce for objects.
  237. *
  238. * @param {Object} object
  239. * The Object that you want to reduce.
  240. *
  241. * @param {Function} fn
  242. * A callback function which is called for each key in the object. It
  243. * receives the accumulated value and the per-iteration value and key
  244. * as arguments.
  245. *
  246. * @param {Mixed} [initial = 0]
  247. * Starting value
  248. *
  249. * @return {Mixed}
  250. * The final accumulated value.
  251. */
  252. function reduce(object, fn) {
  253. var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  254. return keys(object).reduce(function (accum, key) {
  255. return fn(accum, object[key], key);
  256. }, initial);
  257. }
  258. /**
  259. * Object.assign-style object shallow merge/extend.
  260. *
  261. * @param {Object} target
  262. * @param {Object} ...sources
  263. * @return {Object}
  264. */
  265. function assign(target) {
  266. for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  267. sources[_key - 1] = arguments[_key];
  268. }
  269. if (Object.assign) {
  270. return Object.assign.apply(Object, [target].concat(sources));
  271. }
  272. sources.forEach(function (source) {
  273. if (!source) {
  274. return;
  275. }
  276. each(source, function (value, key) {
  277. target[key] = value;
  278. });
  279. });
  280. return target;
  281. }
  282. /**
  283. * Returns whether a value is an object of any kind - including DOM nodes,
  284. * arrays, regular expressions, etc. Not functions, though.
  285. *
  286. * This avoids the gotcha where using `typeof` on a `null` value
  287. * results in `'object'`.
  288. *
  289. * @param {Object} value
  290. * @return {Boolean}
  291. */
  292. function isObject(value) {
  293. return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object';
  294. }
  295. /**
  296. * Returns whether an object appears to be a "plain" object - that is, a
  297. * direct instance of `Object`.
  298. *
  299. * @param {Object} value
  300. * @return {Boolean}
  301. */
  302. function isPlain(value) {
  303. return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
  304. }
  305. /**
  306. * @file create-logger.js
  307. * @module create-logger
  308. */
  309. // This is the private tracking variable for the logging history.
  310. var history = [];
  311. /**
  312. * Log messages to the console and history based on the type of message
  313. *
  314. * @private
  315. * @param {string} type
  316. * The name of the console method to use.
  317. *
  318. * @param {Array} args
  319. * The arguments to be passed to the matching console method.
  320. */
  321. var LogByTypeFactory = function LogByTypeFactory(name, log) {
  322. return function (type, level, args, stringify) {
  323. var lvl = log.levels[level];
  324. var lvlRegExp = new RegExp('^(' + lvl + ')$');
  325. if (type !== 'log') {
  326. // Add the type to the front of the message when it's not "log".
  327. args.unshift(type.toUpperCase() + ':');
  328. }
  329. // Add console prefix after adding to history.
  330. args.unshift(name + ':');
  331. // Add a clone of the args at this point to history.
  332. if (history) {
  333. history.push([].concat(args));
  334. }
  335. // If there's no console then don't try to output messages, but they will
  336. // still be stored in history.
  337. if (!window_1.console) {
  338. return;
  339. }
  340. // Was setting these once outside of this function, but containing them
  341. // in the function makes it easier to test cases where console doesn't exist
  342. // when the module is executed.
  343. var fn = window_1.console[type];
  344. if (!fn && type === 'debug') {
  345. // Certain browsers don't have support for console.debug. For those, we
  346. // should default to the closest comparable log.
  347. fn = window_1.console.info || window_1.console.log;
  348. }
  349. // Bail out if there's no console or if this type is not allowed by the
  350. // current logging level.
  351. if (!fn || !lvl || !lvlRegExp.test(type)) {
  352. return;
  353. }
  354. // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify
  355. // objects and arrays for those less-capable browsers.
  356. if (stringify) {
  357. args = args.map(function (a) {
  358. if (isObject(a) || Array.isArray(a)) {
  359. try {
  360. return JSON.stringify(a);
  361. } catch (x) {
  362. return String(a);
  363. }
  364. }
  365. // Cast to string before joining, so we get null and undefined explicitly
  366. // included in output (as we would in a modern console).
  367. return String(a);
  368. }).join(' ');
  369. }
  370. // Old IE versions do not allow .apply() for console methods (they are
  371. // reported as objects rather than functions).
  372. if (!fn.apply) {
  373. fn(args);
  374. } else {
  375. fn[Array.isArray(args) ? 'apply' : 'call'](window_1.console, args);
  376. }
  377. };
  378. };
  379. function createLogger$1(name) {
  380. // This is the private tracking variable for logging level.
  381. var level = 'info';
  382. // the curried logByType bound to the specific log and history
  383. var logByType = void 0;
  384. /**
  385. * Logs plain debug messages. Similar to `console.log`.
  386. *
  387. * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
  388. * of our JSDoc template, we cannot properly document this as both a function
  389. * and a namespace, so its function signature is documented here.
  390. *
  391. * #### Arguments
  392. * ##### *args
  393. * Mixed[]
  394. *
  395. * Any combination of values that could be passed to `console.log()`.
  396. *
  397. * #### Return Value
  398. *
  399. * `undefined`
  400. *
  401. * @namespace
  402. * @param {Mixed[]} args
  403. * One or more messages or objects that should be logged.
  404. */
  405. var log = function log() {
  406. var stringify = log.stringify || IE_VERSION && IE_VERSION < 11;
  407. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  408. args[_key] = arguments[_key];
  409. }
  410. logByType('log', level, args, stringify);
  411. };
  412. // This is the logByType helper that the logging methods below use
  413. logByType = LogByTypeFactory(name, log);
  414. /**
  415. * Create a new sublogger which chains the old name to the new name.
  416. *
  417. * For example, doing `videojs.log.createLogger('player')` and then using that logger will log the following:
  418. * ```js
  419. * mylogger('foo');
  420. * // > VIDEOJS: player: foo
  421. * ```
  422. *
  423. * @param {string} name
  424. * The name to add call the new logger
  425. * @return {Object}
  426. */
  427. log.createLogger = function (subname) {
  428. return createLogger$1(name + ': ' + subname);
  429. };
  430. /**
  431. * Enumeration of available logging levels, where the keys are the level names
  432. * and the values are `|`-separated strings containing logging methods allowed
  433. * in that logging level. These strings are used to create a regular expression
  434. * matching the function name being called.
  435. *
  436. * Levels provided by Video.js are:
  437. *
  438. * - `off`: Matches no calls. Any value that can be cast to `false` will have
  439. * this effect. The most restrictive.
  440. * - `all`: Matches only Video.js-provided functions (`debug`, `log`,
  441. * `log.warn`, and `log.error`).
  442. * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.
  443. * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.
  444. * - `warn`: Matches `log.warn` and `log.error` calls.
  445. * - `error`: Matches only `log.error` calls.
  446. *
  447. * @type {Object}
  448. */
  449. log.levels = {
  450. all: 'debug|log|warn|error',
  451. off: '',
  452. debug: 'debug|log|warn|error',
  453. info: 'log|warn|error',
  454. warn: 'warn|error',
  455. error: 'error',
  456. DEFAULT: level
  457. };
  458. /**
  459. * Get or set the current logging level.
  460. *
  461. * If a string matching a key from {@link module:log.levels} is provided, acts
  462. * as a setter.
  463. *
  464. * @param {string} [lvl]
  465. * Pass a valid level to set a new logging level.
  466. *
  467. * @return {string}
  468. * The current logging level.
  469. */
  470. log.level = function (lvl) {
  471. if (typeof lvl === 'string') {
  472. if (!log.levels.hasOwnProperty(lvl)) {
  473. throw new Error('"' + lvl + '" in not a valid log level');
  474. }
  475. level = lvl;
  476. }
  477. return level;
  478. };
  479. /**
  480. * Returns an array containing everything that has been logged to the history.
  481. *
  482. * This array is a shallow clone of the internal history record. However, its
  483. * contents are _not_ cloned; so, mutating objects inside this array will
  484. * mutate them in history.
  485. *
  486. * @return {Array}
  487. */
  488. log.history = function () {
  489. return history ? [].concat(history) : [];
  490. };
  491. /**
  492. * Allows you to filter the history by the given logger name
  493. *
  494. * @param {string} fname
  495. * The name to filter by
  496. *
  497. * @return {Array}
  498. * The filtered list to return
  499. */
  500. log.history.filter = function (fname) {
  501. return (history || []).filter(function (historyItem) {
  502. // if the first item in each historyItem includes `fname`, then it's a match
  503. return new RegExp('.*' + fname + '.*').test(historyItem[0]);
  504. });
  505. };
  506. /**
  507. * Clears the internal history tracking, but does not prevent further history
  508. * tracking.
  509. */
  510. log.history.clear = function () {
  511. if (history) {
  512. history.length = 0;
  513. }
  514. };
  515. /**
  516. * Disable history tracking if it is currently enabled.
  517. */
  518. log.history.disable = function () {
  519. if (history !== null) {
  520. history.length = 0;
  521. history = null;
  522. }
  523. };
  524. /**
  525. * Enable history tracking if it is currently disabled.
  526. */
  527. log.history.enable = function () {
  528. if (history === null) {
  529. history = [];
  530. }
  531. };
  532. /**
  533. * Logs error messages. Similar to `console.error`.
  534. *
  535. * @param {Mixed[]} args
  536. * One or more messages or objects that should be logged as an error
  537. */
  538. log.error = function () {
  539. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  540. args[_key2] = arguments[_key2];
  541. }
  542. return logByType('error', level, args);
  543. };
  544. /**
  545. * Logs warning messages. Similar to `console.warn`.
  546. *
  547. * @param {Mixed[]} args
  548. * One or more messages or objects that should be logged as a warning.
  549. */
  550. log.warn = function () {
  551. for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  552. args[_key3] = arguments[_key3];
  553. }
  554. return logByType('warn', level, args);
  555. };
  556. /**
  557. * Logs debug messages. Similar to `console.debug`, but may also act as a comparable
  558. * log if `console.debug` is not available
  559. *
  560. * @param {Mixed[]} args
  561. * One or more messages or objects that should be logged as debug.
  562. */
  563. log.debug = function () {
  564. for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
  565. args[_key4] = arguments[_key4];
  566. }
  567. return logByType('debug', level, args);
  568. };
  569. return log;
  570. }
  571. /**
  572. * @file log.js
  573. * @module log
  574. */
  575. var log = createLogger$1('VIDEOJS');
  576. var createLogger = log.createLogger;
  577. function clean (s) {
  578. return s.replace(/\n\r?\s*/g, '')
  579. }
  580. var tsml = function tsml (sa) {
  581. var s = ''
  582. , i = 0;
  583. for (; i < arguments.length; i++)
  584. s += clean(sa[i]) + (arguments[i + 1] || '');
  585. return s
  586. };
  587. /**
  588. * @file computed-style.js
  589. * @module computed-style
  590. */
  591. /**
  592. * A safe getComputedStyle with an IE8 fallback.
  593. *
  594. * This is needed because in Firefox, if the player is loaded in an iframe with
  595. * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to
  596. * make sure that the player doesn't break in these cases.
  597. *
  598. * @param {Element} el
  599. * The element you want the computed style of
  600. *
  601. * @param {string} prop
  602. * The property name you want
  603. *
  604. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
  605. *
  606. * @static
  607. * @const
  608. */
  609. function computedStyle(el, prop) {
  610. if (!el || !prop) {
  611. return '';
  612. }
  613. if (typeof window_1.getComputedStyle === 'function') {
  614. var cs = window_1.getComputedStyle(el);
  615. return cs ? cs[prop] : '';
  616. }
  617. return el.currentStyle[prop] || '';
  618. }
  619. 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 ', '.']);
  620. /**
  621. * @file dom.js
  622. * @module dom
  623. */
  624. /**
  625. * Detect if a value is a string with any non-whitespace characters.
  626. *
  627. * @param {string} str
  628. * The string to check
  629. *
  630. * @return {boolean}
  631. * - True if the string is non-blank
  632. * - False otherwise
  633. *
  634. */
  635. function isNonBlankString(str) {
  636. return typeof str === 'string' && /\S/.test(str);
  637. }
  638. /**
  639. * Throws an error if the passed string has whitespace. This is used by
  640. * class methods to be relatively consistent with the classList API.
  641. *
  642. * @param {string} str
  643. * The string to check for whitespace.
  644. *
  645. * @throws {Error}
  646. * Throws an error if there is whitespace in the string.
  647. *
  648. */
  649. function throwIfWhitespace(str) {
  650. if (/\s/.test(str)) {
  651. throw new Error('class has illegal whitespace characters');
  652. }
  653. }
  654. /**
  655. * Produce a regular expression for matching a className within an elements className.
  656. *
  657. * @param {string} className
  658. * The className to generate the RegExp for.
  659. *
  660. * @return {RegExp}
  661. * The RegExp that will check for a specific `className` in an elements
  662. * className.
  663. */
  664. function classRegExp(className) {
  665. return new RegExp('(^|\\s)' + className + '($|\\s)');
  666. }
  667. /**
  668. * Whether the current DOM interface appears to be real.
  669. *
  670. * @return {Boolean}
  671. */
  672. function isReal() {
  673. return (
  674. // Both document and window will never be undefined thanks to `global`.
  675. document_1 === window_1.document &&
  676. // In IE < 9, DOM methods return "object" as their type, so all we can
  677. // confidently check is that it exists.
  678. typeof document_1.createElement !== 'undefined'
  679. );
  680. }
  681. /**
  682. * Determines, via duck typing, whether or not a value is a DOM element.
  683. *
  684. * @param {Mixed} value
  685. * The thing to check
  686. *
  687. * @return {boolean}
  688. * - True if it is a DOM element
  689. * - False otherwise
  690. */
  691. function isEl(value) {
  692. return isObject(value) && value.nodeType === 1;
  693. }
  694. /**
  695. * Determines if the current DOM is embedded in an iframe.
  696. *
  697. * @return {boolean}
  698. *
  699. */
  700. function isInFrame() {
  701. // We need a try/catch here because Safari will throw errors when attempting
  702. // to get either `parent` or `self`
  703. try {
  704. return window_1.parent !== window_1.self;
  705. } catch (x) {
  706. return true;
  707. }
  708. }
  709. /**
  710. * Creates functions to query the DOM using a given method.
  711. *
  712. * @param {string} method
  713. * The method to create the query with.
  714. *
  715. * @return {Function}
  716. * The query method
  717. */
  718. function createQuerier(method) {
  719. return function (selector, context) {
  720. if (!isNonBlankString(selector)) {
  721. return document_1[method](null);
  722. }
  723. if (isNonBlankString(context)) {
  724. context = document_1.querySelector(context);
  725. }
  726. var ctx = isEl(context) ? context : document_1;
  727. return ctx[method] && ctx[method](selector);
  728. };
  729. }
  730. /**
  731. * Creates an element and applies properties.
  732. *
  733. * @param {string} [tagName='div']
  734. * Name of tag to be created.
  735. *
  736. * @param {Object} [properties={}]
  737. * Element properties to be applied.
  738. *
  739. * @param {Object} [attributes={}]
  740. * Element attributes to be applied.
  741. *
  742. * @param {String|Element|TextNode|Array|Function} [content]
  743. * Contents for the element (see: {@link dom:normalizeContent})
  744. *
  745. * @return {Element}
  746. * The element that was created.
  747. */
  748. function createEl() {
  749. var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  750. var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  751. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  752. var content = arguments[3];
  753. var el = document_1.createElement(tagName);
  754. Object.getOwnPropertyNames(properties).forEach(function (propName) {
  755. var val = properties[propName];
  756. // See #2176
  757. // We originally were accepting both properties and attributes in the
  758. // same object, but that doesn't work so well.
  759. if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
  760. log.warn(tsml(_templateObject, propName, val));
  761. el.setAttribute(propName, val);
  762. // Handle textContent since it's not supported everywhere and we have a
  763. // method for it.
  764. } else if (propName === 'textContent') {
  765. textContent(el, val);
  766. } else {
  767. el[propName] = val;
  768. }
  769. });
  770. Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
  771. el.setAttribute(attrName, attributes[attrName]);
  772. });
  773. if (content) {
  774. appendContent(el, content);
  775. }
  776. return el;
  777. }
  778. /**
  779. * Injects text into an element, replacing any existing contents entirely.
  780. *
  781. * @param {Element} el
  782. * The element to add text content into
  783. *
  784. * @param {string} text
  785. * The text content to add.
  786. *
  787. * @return {Element}
  788. * The element with added text content.
  789. */
  790. function textContent(el, text) {
  791. if (typeof el.textContent === 'undefined') {
  792. el.innerText = text;
  793. } else {
  794. el.textContent = text;
  795. }
  796. return el;
  797. }
  798. /**
  799. * Insert an element as the first child node of another
  800. *
  801. * @param {Element} child
  802. * Element to insert
  803. *
  804. * @param {Element} parent
  805. * Element to insert child into
  806. */
  807. function prependTo(child, parent) {
  808. if (parent.firstChild) {
  809. parent.insertBefore(child, parent.firstChild);
  810. } else {
  811. parent.appendChild(child);
  812. }
  813. }
  814. /**
  815. * Check if an element has a CSS class
  816. *
  817. * @param {Element} element
  818. * Element to check
  819. *
  820. * @param {string} classToCheck
  821. * Class name to check for
  822. *
  823. * @return {boolean}
  824. * - True if the element had the class
  825. * - False otherwise.
  826. *
  827. * @throws {Error}
  828. * Throws an error if `classToCheck` has white space.
  829. */
  830. function hasClass(element, classToCheck) {
  831. throwIfWhitespace(classToCheck);
  832. if (element.classList) {
  833. return element.classList.contains(classToCheck);
  834. }
  835. return classRegExp(classToCheck).test(element.className);
  836. }
  837. /**
  838. * Add a CSS class name to an element
  839. *
  840. * @param {Element} element
  841. * Element to add class name to.
  842. *
  843. * @param {string} classToAdd
  844. * Class name to add.
  845. *
  846. * @return {Element}
  847. * The dom element with the added class name.
  848. */
  849. function addClass(element, classToAdd) {
  850. if (element.classList) {
  851. element.classList.add(classToAdd);
  852. // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
  853. // in the case of classList not being supported.
  854. } else if (!hasClass(element, classToAdd)) {
  855. element.className = (element.className + ' ' + classToAdd).trim();
  856. }
  857. return element;
  858. }
  859. /**
  860. * Remove a CSS class name from an element
  861. *
  862. * @param {Element} element
  863. * Element to remove a class name from.
  864. *
  865. * @param {string} classToRemove
  866. * Class name to remove
  867. *
  868. * @return {Element}
  869. * The dom element with class name removed.
  870. */
  871. function removeClass(element, classToRemove) {
  872. if (element.classList) {
  873. element.classList.remove(classToRemove);
  874. } else {
  875. throwIfWhitespace(classToRemove);
  876. element.className = element.className.split(/\s+/).filter(function (c) {
  877. return c !== classToRemove;
  878. }).join(' ');
  879. }
  880. return element;
  881. }
  882. /**
  883. * The callback definition for toggleElClass.
  884. *
  885. * @callback Dom~PredicateCallback
  886. * @param {Element} element
  887. * The DOM element of the Component.
  888. *
  889. * @param {string} classToToggle
  890. * The `className` that wants to be toggled
  891. *
  892. * @return {boolean|undefined}
  893. * - If true the `classToToggle` will get added to `element`.
  894. * - If false the `classToToggle` will get removed from `element`.
  895. * - If undefined this callback will be ignored
  896. */
  897. /**
  898. * Adds or removes a CSS class name on an element depending on an optional
  899. * condition or the presence/absence of the class name.
  900. *
  901. * @param {Element} element
  902. * The element to toggle a class name on.
  903. *
  904. * @param {string} classToToggle
  905. * The class that should be toggled
  906. *
  907. * @param {boolean|PredicateCallback} [predicate]
  908. * See the return value for {@link Dom~PredicateCallback}
  909. *
  910. * @return {Element}
  911. * The element with a class that has been toggled.
  912. */
  913. function toggleClass(element, classToToggle, predicate) {
  914. // This CANNOT use `classList` internally because IE does not support the
  915. // second parameter to the `classList.toggle()` method! Which is fine because
  916. // `classList` will be used by the add/remove functions.
  917. var has = hasClass(element, classToToggle);
  918. if (typeof predicate === 'function') {
  919. predicate = predicate(element, classToToggle);
  920. }
  921. if (typeof predicate !== 'boolean') {
  922. predicate = !has;
  923. }
  924. // If the necessary class operation matches the current state of the
  925. // element, no action is required.
  926. if (predicate === has) {
  927. return;
  928. }
  929. if (predicate) {
  930. addClass(element, classToToggle);
  931. } else {
  932. removeClass(element, classToToggle);
  933. }
  934. return element;
  935. }
  936. /**
  937. * Apply attributes to an HTML element.
  938. *
  939. * @param {Element} el
  940. * Element to add attributes to.
  941. *
  942. * @param {Object} [attributes]
  943. * Attributes to be applied.
  944. */
  945. function setAttributes(el, attributes) {
  946. Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
  947. var attrValue = attributes[attrName];
  948. if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
  949. el.removeAttribute(attrName);
  950. } else {
  951. el.setAttribute(attrName, attrValue === true ? '' : attrValue);
  952. }
  953. });
  954. }
  955. /**
  956. * Get an element's attribute values, as defined on the HTML tag
  957. * Attributes are not the same as properties. They're defined on the tag
  958. * or with setAttribute (which shouldn't be used with HTML)
  959. * This will return true or false for boolean attributes.
  960. *
  961. * @param {Element} tag
  962. * Element from which to get tag attributes.
  963. *
  964. * @return {Object}
  965. * All attributes of the element.
  966. */
  967. function getAttributes(tag) {
  968. var obj = {};
  969. // known boolean attributes
  970. // we can check for matching boolean properties, but older browsers
  971. // won't know about HTML5 boolean attributes that we still read from
  972. var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
  973. if (tag && tag.attributes && tag.attributes.length > 0) {
  974. var attrs = tag.attributes;
  975. for (var i = attrs.length - 1; i >= 0; i--) {
  976. var attrName = attrs[i].name;
  977. var attrVal = attrs[i].value;
  978. // check for known booleans
  979. // the matching element property will return a value for typeof
  980. if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
  981. // the value of an included boolean attribute is typically an empty
  982. // string ('') which would equal false if we just check for a false value.
  983. // we also don't want support bad code like autoplay='false'
  984. attrVal = attrVal !== null ? true : false;
  985. }
  986. obj[attrName] = attrVal;
  987. }
  988. }
  989. return obj;
  990. }
  991. /**
  992. * Get the value of an element's attribute
  993. *
  994. * @param {Element} el
  995. * A DOM element
  996. *
  997. * @param {string} attribute
  998. * Attribute to get the value of
  999. *
  1000. * @return {string}
  1001. * value of the attribute
  1002. */
  1003. function getAttribute(el, attribute) {
  1004. return el.getAttribute(attribute);
  1005. }
  1006. /**
  1007. * Set the value of an element's attribute
  1008. *
  1009. * @param {Element} el
  1010. * A DOM element
  1011. *
  1012. * @param {string} attribute
  1013. * Attribute to set
  1014. *
  1015. * @param {string} value
  1016. * Value to set the attribute to
  1017. */
  1018. function setAttribute(el, attribute, value) {
  1019. el.setAttribute(attribute, value);
  1020. }
  1021. /**
  1022. * Remove an element's attribute
  1023. *
  1024. * @param {Element} el
  1025. * A DOM element
  1026. *
  1027. * @param {string} attribute
  1028. * Attribute to remove
  1029. */
  1030. function removeAttribute(el, attribute) {
  1031. el.removeAttribute(attribute);
  1032. }
  1033. /**
  1034. * Attempt to block the ability to select text while dragging controls
  1035. */
  1036. function blockTextSelection() {
  1037. document_1.body.focus();
  1038. document_1.onselectstart = function () {
  1039. return false;
  1040. };
  1041. }
  1042. /**
  1043. * Turn off text selection blocking
  1044. */
  1045. function unblockTextSelection() {
  1046. document_1.onselectstart = function () {
  1047. return true;
  1048. };
  1049. }
  1050. /**
  1051. * Identical to the native `getBoundingClientRect` function, but ensures that
  1052. * the method is supported at all (it is in all browsers we claim to support)
  1053. * and that the element is in the DOM before continuing.
  1054. *
  1055. * This wrapper function also shims properties which are not provided by some
  1056. * older browsers (namely, IE8).
  1057. *
  1058. * Additionally, some browsers do not support adding properties to a
  1059. * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
  1060. * properties (except `x` and `y` which are not widely supported). This helps
  1061. * avoid implementations where keys are non-enumerable.
  1062. *
  1063. * @param {Element} el
  1064. * Element whose `ClientRect` we want to calculate.
  1065. *
  1066. * @return {Object|undefined}
  1067. * Always returns a plain
  1068. */
  1069. function getBoundingClientRect(el) {
  1070. if (el && el.getBoundingClientRect && el.parentNode) {
  1071. var rect = el.getBoundingClientRect();
  1072. var result = {};
  1073. ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
  1074. if (rect[k] !== undefined) {
  1075. result[k] = rect[k];
  1076. }
  1077. });
  1078. if (!result.height) {
  1079. result.height = parseFloat(computedStyle(el, 'height'));
  1080. }
  1081. if (!result.width) {
  1082. result.width = parseFloat(computedStyle(el, 'width'));
  1083. }
  1084. return result;
  1085. }
  1086. }
  1087. /**
  1088. * The postion of a DOM element on the page.
  1089. *
  1090. * @typedef {Object} module:dom~Position
  1091. *
  1092. * @property {number} left
  1093. * Pixels to the left
  1094. *
  1095. * @property {number} top
  1096. * Pixels on top
  1097. */
  1098. /**
  1099. * Offset Left.
  1100. * getBoundingClientRect technique from
  1101. * John Resig
  1102. *
  1103. * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
  1104. *
  1105. * @param {Element} el
  1106. * Element from which to get offset
  1107. *
  1108. * @return {module:dom~Position}
  1109. * The position of the element that was passed in.
  1110. */
  1111. function findPosition(el) {
  1112. var box = void 0;
  1113. if (el.getBoundingClientRect && el.parentNode) {
  1114. box = el.getBoundingClientRect();
  1115. }
  1116. if (!box) {
  1117. return {
  1118. left: 0,
  1119. top: 0
  1120. };
  1121. }
  1122. var docEl = document_1.documentElement;
  1123. var body = document_1.body;
  1124. var clientLeft = docEl.clientLeft || body.clientLeft || 0;
  1125. var scrollLeft = window_1.pageXOffset || body.scrollLeft;
  1126. var left = box.left + scrollLeft - clientLeft;
  1127. var clientTop = docEl.clientTop || body.clientTop || 0;
  1128. var scrollTop = window_1.pageYOffset || body.scrollTop;
  1129. var top = box.top + scrollTop - clientTop;
  1130. // Android sometimes returns slightly off decimal values, so need to round
  1131. return {
  1132. left: Math.round(left),
  1133. top: Math.round(top)
  1134. };
  1135. }
  1136. /**
  1137. * x and y coordinates for a dom element or mouse pointer
  1138. *
  1139. * @typedef {Object} Dom~Coordinates
  1140. *
  1141. * @property {number} x
  1142. * x coordinate in pixels
  1143. *
  1144. * @property {number} y
  1145. * y coordinate in pixels
  1146. */
  1147. /**
  1148. * Get pointer position in element
  1149. * Returns an object with x and y coordinates.
  1150. * The base on the coordinates are the bottom left of the element.
  1151. *
  1152. * @param {Element} el
  1153. * Element on which to get the pointer position on
  1154. *
  1155. * @param {EventTarget~Event} event
  1156. * Event object
  1157. *
  1158. * @return {Dom~Coordinates}
  1159. * A Coordinates object corresponding to the mouse position.
  1160. *
  1161. */
  1162. function getPointerPosition(el, event) {
  1163. var position = {};
  1164. var box = findPosition(el);
  1165. var boxW = el.offsetWidth;
  1166. var boxH = el.offsetHeight;
  1167. var boxY = box.top;
  1168. var boxX = box.left;
  1169. var pageY = event.pageY;
  1170. var pageX = event.pageX;
  1171. if (event.changedTouches) {
  1172. pageX = event.changedTouches[0].pageX;
  1173. pageY = event.changedTouches[0].pageY;
  1174. }
  1175. position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
  1176. position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
  1177. return position;
  1178. }
  1179. /**
  1180. * Determines, via duck typing, whether or not a value is a text node.
  1181. *
  1182. * @param {Mixed} value
  1183. * Check if this value is a text node.
  1184. *
  1185. * @return {boolean}
  1186. * - True if it is a text node
  1187. * - False otherwise
  1188. */
  1189. function isTextNode(value) {
  1190. return isObject(value) && value.nodeType === 3;
  1191. }
  1192. /**
  1193. * Empties the contents of an element.
  1194. *
  1195. * @param {Element} el
  1196. * The element to empty children from
  1197. *
  1198. * @return {Element}
  1199. * The element with no children
  1200. */
  1201. function emptyEl(el) {
  1202. while (el.firstChild) {
  1203. el.removeChild(el.firstChild);
  1204. }
  1205. return el;
  1206. }
  1207. /**
  1208. * Normalizes content for eventual insertion into the DOM.
  1209. *
  1210. * This allows a wide range of content definition methods, but protects
  1211. * from falling into the trap of simply writing to `innerHTML`, which is
  1212. * an XSS concern.
  1213. *
  1214. * The content for an element can be passed in multiple types and
  1215. * combinations, whose behavior is as follows:
  1216. *
  1217. * @param {String|Element|TextNode|Array|Function} content
  1218. * - String: Normalized into a text node.
  1219. * - Element/TextNode: Passed through.
  1220. * - Array: A one-dimensional array of strings, elements, nodes, or functions
  1221. * (which return single strings, elements, or nodes).
  1222. * - Function: If the sole argument, is expected to produce a string, element,
  1223. * node, or array as defined above.
  1224. *
  1225. * @return {Array}
  1226. * All of the content that was passed in normalized.
  1227. */
  1228. function normalizeContent(content) {
  1229. // First, invoke content if it is a function. If it produces an array,
  1230. // that needs to happen before normalization.
  1231. if (typeof content === 'function') {
  1232. content = content();
  1233. }
  1234. // Next up, normalize to an array, so one or many items can be normalized,
  1235. // filtered, and returned.
  1236. return (Array.isArray(content) ? content : [content]).map(function (value) {
  1237. // First, invoke value if it is a function to produce a new value,
  1238. // which will be subsequently normalized to a Node of some kind.
  1239. if (typeof value === 'function') {
  1240. value = value();
  1241. }
  1242. if (isEl(value) || isTextNode(value)) {
  1243. return value;
  1244. }
  1245. if (typeof value === 'string' && /\S/.test(value)) {
  1246. return document_1.createTextNode(value);
  1247. }
  1248. }).filter(function (value) {
  1249. return value;
  1250. });
  1251. }
  1252. /**
  1253. * Normalizes and appends content to an element.
  1254. *
  1255. * @param {Element} el
  1256. * Element to append normalized content to.
  1257. *
  1258. *
  1259. * @param {String|Element|TextNode|Array|Function} content
  1260. * See the `content` argument of {@link dom:normalizeContent}
  1261. *
  1262. * @return {Element}
  1263. * The element with appended normalized content.
  1264. */
  1265. function appendContent(el, content) {
  1266. normalizeContent(content).forEach(function (node) {
  1267. return el.appendChild(node);
  1268. });
  1269. return el;
  1270. }
  1271. /**
  1272. * Normalizes and inserts content into an element; this is identical to
  1273. * `appendContent()`, except it empties the element first.
  1274. *
  1275. * @param {Element} el
  1276. * Element to insert normalized content into.
  1277. *
  1278. * @param {String|Element|TextNode|Array|Function} content
  1279. * See the `content` argument of {@link dom:normalizeContent}
  1280. *
  1281. * @return {Element}
  1282. * The element with inserted normalized content.
  1283. *
  1284. */
  1285. function insertContent(el, content) {
  1286. return appendContent(emptyEl(el), content);
  1287. }
  1288. /**
  1289. * Check if event was a single left click
  1290. *
  1291. * @param {EventTarget~Event} event
  1292. * Event object
  1293. *
  1294. * @return {boolean}
  1295. * - True if a left click
  1296. * - False if not a left click
  1297. */
  1298. function isSingleLeftClick(event) {
  1299. // Note: if you create something draggable, be sure to
  1300. // call it on both `mousedown` and `mousemove` event,
  1301. // otherwise `mousedown` should be enough for a button
  1302. if (event.button === undefined && event.buttons === undefined) {
  1303. // Why do we need `buttons` ?
  1304. // Because, middle mouse sometimes have this:
  1305. // e.button === 0 and e.buttons === 4
  1306. // Furthermore, we want to prevent combination click, something like
  1307. // HOLD middlemouse then left click, that would be
  1308. // e.button === 0, e.buttons === 5
  1309. // just `button` is not gonna work
  1310. // Alright, then what this block does ?
  1311. // this is for chrome `simulate mobile devices`
  1312. // I want to support this as well
  1313. return true;
  1314. }
  1315. if (event.button === 0 && event.buttons === undefined) {
  1316. // Touch screen, sometimes on some specific device, `buttons`
  1317. // doesn't have anything (safari on ios, blackberry...)
  1318. return true;
  1319. }
  1320. if (IE_VERSION === 9) {
  1321. // Ignore IE9
  1322. return true;
  1323. }
  1324. if (event.button !== 0 || event.buttons !== 1) {
  1325. // This is the reason we have those if else block above
  1326. // if any special case we can catch and let it slide
  1327. // we do it above, when get to here, this definitely
  1328. // is-not-left-click
  1329. return false;
  1330. }
  1331. return true;
  1332. }
  1333. /**
  1334. * Finds a single DOM element matching `selector` within the optional
  1335. * `context` of another DOM element (defaulting to `document`).
  1336. *
  1337. * @param {string} selector
  1338. * A valid CSS selector, which will be passed to `querySelector`.
  1339. *
  1340. * @param {Element|String} [context=document]
  1341. * A DOM element within which to query. Can also be a selector
  1342. * string in which case the first matching element will be used
  1343. * as context. If missing (or no element matches selector), falls
  1344. * back to `document`.
  1345. *
  1346. * @return {Element|null}
  1347. * The element that was found or null.
  1348. */
  1349. var $ = createQuerier('querySelector');
  1350. /**
  1351. * Finds a all DOM elements matching `selector` within the optional
  1352. * `context` of another DOM element (defaulting to `document`).
  1353. *
  1354. * @param {string} selector
  1355. * A valid CSS selector, which will be passed to `querySelectorAll`.
  1356. *
  1357. * @param {Element|String} [context=document]
  1358. * A DOM element within which to query. Can also be a selector
  1359. * string in which case the first matching element will be used
  1360. * as context. If missing (or no element matches selector), falls
  1361. * back to `document`.
  1362. *
  1363. * @return {NodeList}
  1364. * A element list of elements that were found. Will be empty if none were found.
  1365. *
  1366. */
  1367. var $$ = createQuerier('querySelectorAll');
  1368. var Dom = (Object.freeze || Object)({
  1369. isReal: isReal,
  1370. isEl: isEl,
  1371. isInFrame: isInFrame,
  1372. createEl: createEl,
  1373. textContent: textContent,
  1374. prependTo: prependTo,
  1375. hasClass: hasClass,
  1376. addClass: addClass,
  1377. removeClass: removeClass,
  1378. toggleClass: toggleClass,
  1379. setAttributes: setAttributes,
  1380. getAttributes: getAttributes,
  1381. getAttribute: getAttribute,
  1382. setAttribute: setAttribute,
  1383. removeAttribute: removeAttribute,
  1384. blockTextSelection: blockTextSelection,
  1385. unblockTextSelection: unblockTextSelection,
  1386. getBoundingClientRect: getBoundingClientRect,
  1387. findPosition: findPosition,
  1388. getPointerPosition: getPointerPosition,
  1389. isTextNode: isTextNode,
  1390. emptyEl: emptyEl,
  1391. normalizeContent: normalizeContent,
  1392. appendContent: appendContent,
  1393. insertContent: insertContent,
  1394. isSingleLeftClick: isSingleLeftClick,
  1395. $: $,
  1396. $$: $$
  1397. });
  1398. /**
  1399. * @file guid.js
  1400. * @module guid
  1401. */
  1402. /**
  1403. * Unique ID for an element or function
  1404. * @type {Number}
  1405. */
  1406. var _guid = 1;
  1407. /**
  1408. * Get a unique auto-incrementing ID by number that has not been returned before.
  1409. *
  1410. * @return {number}
  1411. * A new unique ID.
  1412. */
  1413. function newGUID() {
  1414. return _guid++;
  1415. }
  1416. /**
  1417. * @file dom-data.js
  1418. * @module dom-data
  1419. */
  1420. /**
  1421. * Element Data Store.
  1422. *
  1423. * Allows for binding data to an element without putting it directly on the
  1424. * element. Ex. Event listeners are stored here.
  1425. * (also from jsninja.com, slightly modified and updated for closure compiler)
  1426. *
  1427. * @type {Object}
  1428. * @private
  1429. */
  1430. var elData = {};
  1431. /*
  1432. * Unique attribute name to store an element's guid in
  1433. *
  1434. * @type {String}
  1435. * @constant
  1436. * @private
  1437. */
  1438. var elIdAttr = 'vdata' + new Date().getTime();
  1439. /**
  1440. * Returns the cache object where data for an element is stored
  1441. *
  1442. * @param {Element} el
  1443. * Element to store data for.
  1444. *
  1445. * @return {Object}
  1446. * The cache object for that el that was passed in.
  1447. */
  1448. function getData(el) {
  1449. var id = el[elIdAttr];
  1450. if (!id) {
  1451. id = el[elIdAttr] = newGUID();
  1452. }
  1453. if (!elData[id]) {
  1454. elData[id] = {};
  1455. }
  1456. return elData[id];
  1457. }
  1458. /**
  1459. * Returns whether or not an element has cached data
  1460. *
  1461. * @param {Element} el
  1462. * Check if this element has cached data.
  1463. *
  1464. * @return {boolean}
  1465. * - True if the DOM element has cached data.
  1466. * - False otherwise.
  1467. */
  1468. function hasData(el) {
  1469. var id = el[elIdAttr];
  1470. if (!id) {
  1471. return false;
  1472. }
  1473. return !!Object.getOwnPropertyNames(elData[id]).length;
  1474. }
  1475. /**
  1476. * Delete data for the element from the cache and the guid attr from getElementById
  1477. *
  1478. * @param {Element} el
  1479. * Remove cached data for this element.
  1480. */
  1481. function removeData(el) {
  1482. var id = el[elIdAttr];
  1483. if (!id) {
  1484. return;
  1485. }
  1486. // Remove all stored data
  1487. delete elData[id];
  1488. // Remove the elIdAttr property from the DOM node
  1489. try {
  1490. delete el[elIdAttr];
  1491. } catch (e) {
  1492. if (el.removeAttribute) {
  1493. el.removeAttribute(elIdAttr);
  1494. } else {
  1495. // IE doesn't appear to support removeAttribute on the document element
  1496. el[elIdAttr] = null;
  1497. }
  1498. }
  1499. }
  1500. /**
  1501. * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
  1502. * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
  1503. * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
  1504. * robust as jquery's, so there's probably some differences.
  1505. *
  1506. * @module events
  1507. */
  1508. /**
  1509. * Clean up the listener cache and dispatchers
  1510. *
  1511. * @param {Element|Object} elem
  1512. * Element to clean up
  1513. *
  1514. * @param {string} type
  1515. * Type of event to clean up
  1516. */
  1517. function _cleanUpEvents(elem, type) {
  1518. var data = getData(elem);
  1519. // Remove the events of a particular type if there are none left
  1520. if (data.handlers[type].length === 0) {
  1521. delete data.handlers[type];
  1522. // data.handlers[type] = null;
  1523. // Setting to null was causing an error with data.handlers
  1524. // Remove the meta-handler from the element
  1525. if (elem.removeEventListener) {
  1526. elem.removeEventListener(type, data.dispatcher, false);
  1527. } else if (elem.detachEvent) {
  1528. elem.detachEvent('on' + type, data.dispatcher);
  1529. }
  1530. }
  1531. // Remove the events object if there are no types left
  1532. if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
  1533. delete data.handlers;
  1534. delete data.dispatcher;
  1535. delete data.disabled;
  1536. }
  1537. // Finally remove the element data if there is no data left
  1538. if (Object.getOwnPropertyNames(data).length === 0) {
  1539. removeData(elem);
  1540. }
  1541. }
  1542. /**
  1543. * Loops through an array of event types and calls the requested method for each type.
  1544. *
  1545. * @param {Function} fn
  1546. * The event method we want to use.
  1547. *
  1548. * @param {Element|Object} elem
  1549. * Element or object to bind listeners to
  1550. *
  1551. * @param {string} type
  1552. * Type of event to bind to.
  1553. *
  1554. * @param {EventTarget~EventListener} callback
  1555. * Event listener.
  1556. */
  1557. function _handleMultipleEvents(fn, elem, types, callback) {
  1558. types.forEach(function (type) {
  1559. // Call the event method for each one of the types
  1560. fn(elem, type, callback);
  1561. });
  1562. }
  1563. /**
  1564. * Fix a native event to have standard property values
  1565. *
  1566. * @param {Object} event
  1567. * Event object to fix.
  1568. *
  1569. * @return {Object}
  1570. * Fixed event object.
  1571. */
  1572. function fixEvent(event) {
  1573. function returnTrue() {
  1574. return true;
  1575. }
  1576. function returnFalse() {
  1577. return false;
  1578. }
  1579. // Test if fixing up is needed
  1580. // Used to check if !event.stopPropagation instead of isPropagationStopped
  1581. // But native events return true for stopPropagation, but don't have
  1582. // other expected methods like isPropagationStopped. Seems to be a problem
  1583. // with the Javascript Ninja code. So we're just overriding all events now.
  1584. if (!event || !event.isPropagationStopped) {
  1585. var old = event || window_1.event;
  1586. event = {};
  1587. // Clone the old object so that we can modify the values event = {};
  1588. // IE8 Doesn't like when you mess with native event properties
  1589. // Firefox returns false for event.hasOwnProperty('type') and other props
  1590. // which makes copying more difficult.
  1591. // TODO: Probably best to create a whitelist of event props
  1592. for (var key in old) {
  1593. // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
  1594. // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
  1595. // and webkitMovementX/Y
  1596. if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
  1597. // Chrome 32+ warns if you try to copy deprecated returnValue, but
  1598. // we still want to if preventDefault isn't supported (IE8).
  1599. if (!(key === 'returnValue' && old.preventDefault)) {
  1600. event[key] = old[key];
  1601. }
  1602. }
  1603. }
  1604. // The event occurred on this element
  1605. if (!event.target) {
  1606. event.target = event.srcElement || document_1;
  1607. }
  1608. // Handle which other element the event is related to
  1609. if (!event.relatedTarget) {
  1610. event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
  1611. }
  1612. // Stop the default browser action
  1613. event.preventDefault = function () {
  1614. if (old.preventDefault) {
  1615. old.preventDefault();
  1616. }
  1617. event.returnValue = false;
  1618. old.returnValue = false;
  1619. event.defaultPrevented = true;
  1620. };
  1621. event.defaultPrevented = false;
  1622. // Stop the event from bubbling
  1623. event.stopPropagation = function () {
  1624. if (old.stopPropagation) {
  1625. old.stopPropagation();
  1626. }
  1627. event.cancelBubble = true;
  1628. old.cancelBubble = true;
  1629. event.isPropagationStopped = returnTrue;
  1630. };
  1631. event.isPropagationStopped = returnFalse;
  1632. // Stop the event from bubbling and executing other handlers
  1633. event.stopImmediatePropagation = function () {
  1634. if (old.stopImmediatePropagation) {
  1635. old.stopImmediatePropagation();
  1636. }
  1637. event.isImmediatePropagationStopped = returnTrue;
  1638. event.stopPropagation();
  1639. };
  1640. event.isImmediatePropagationStopped = returnFalse;
  1641. // Handle mouse position
  1642. if (event.clientX !== null && event.clientX !== undefined) {
  1643. var doc = document_1.documentElement;
  1644. var body = document_1.body;
  1645. event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
  1646. event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
  1647. }
  1648. // Handle key presses
  1649. event.which = event.charCode || event.keyCode;
  1650. // Fix button for mouse clicks:
  1651. // 0 == left; 1 == middle; 2 == right
  1652. if (event.button !== null && event.button !== undefined) {
  1653. // The following is disabled because it does not pass videojs-standard
  1654. // and... yikes.
  1655. /* eslint-disable */
  1656. event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
  1657. /* eslint-enable */
  1658. }
  1659. }
  1660. // Returns fixed-up instance
  1661. return event;
  1662. }
  1663. /**
  1664. * Whether passive event listeners are supported
  1665. */
  1666. var _supportsPassive = false;
  1667. (function () {
  1668. try {
  1669. var opts = Object.defineProperty({}, 'passive', {
  1670. get: function get() {
  1671. _supportsPassive = true;
  1672. }
  1673. });
  1674. window_1.addEventListener('test', null, opts);
  1675. window_1.removeEventListener('test', null, opts);
  1676. } catch (e) {
  1677. // disregard
  1678. }
  1679. })();
  1680. /**
  1681. * Touch events Chrome expects to be passive
  1682. */
  1683. var passiveEvents = ['touchstart', 'touchmove'];
  1684. /**
  1685. * Add an event listener to element
  1686. * It stores the handler function in a separate cache object
  1687. * and adds a generic handler to the element's event,
  1688. * along with a unique id (guid) to the element.
  1689. *
  1690. * @param {Element|Object} elem
  1691. * Element or object to bind listeners to
  1692. *
  1693. * @param {string|string[]} type
  1694. * Type of event to bind to.
  1695. *
  1696. * @param {EventTarget~EventListener} fn
  1697. * Event listener.
  1698. */
  1699. function on(elem, type, fn) {
  1700. if (Array.isArray(type)) {
  1701. return _handleMultipleEvents(on, elem, type, fn);
  1702. }
  1703. var data = getData(elem);
  1704. // We need a place to store all our handler data
  1705. if (!data.handlers) {
  1706. data.handlers = {};
  1707. }
  1708. if (!data.handlers[type]) {
  1709. data.handlers[type] = [];
  1710. }
  1711. if (!fn.guid) {
  1712. fn.guid = newGUID();
  1713. }
  1714. data.handlers[type].push(fn);
  1715. if (!data.dispatcher) {
  1716. data.disabled = false;
  1717. data.dispatcher = function (event, hash) {
  1718. if (data.disabled) {
  1719. return;
  1720. }
  1721. event = fixEvent(event);
  1722. var handlers = data.handlers[event.type];
  1723. if (handlers) {
  1724. // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
  1725. var handlersCopy = handlers.slice(0);
  1726. for (var m = 0, n = handlersCopy.length; m < n; m++) {
  1727. if (event.isImmediatePropagationStopped()) {
  1728. break;
  1729. } else {
  1730. try {
  1731. handlersCopy[m].call(elem, event, hash);
  1732. } catch (e) {
  1733. log.error(e);
  1734. }
  1735. }
  1736. }
  1737. }
  1738. };
  1739. }
  1740. if (data.handlers[type].length === 1) {
  1741. if (elem.addEventListener) {
  1742. var options = false;
  1743. if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
  1744. options = { passive: true };
  1745. }
  1746. elem.addEventListener(type, data.dispatcher, options);
  1747. } else if (elem.attachEvent) {
  1748. elem.attachEvent('on' + type, data.dispatcher);
  1749. }
  1750. }
  1751. }
  1752. /**
  1753. * Removes event listeners from an element
  1754. *
  1755. * @param {Element|Object} elem
  1756. * Object to remove listeners from.
  1757. *
  1758. * @param {string|string[]} [type]
  1759. * Type of listener to remove. Don't include to remove all events from element.
  1760. *
  1761. * @param {EventTarget~EventListener} [fn]
  1762. * Specific listener to remove. Don't include to remove listeners for an event
  1763. * type.
  1764. */
  1765. function off(elem, type, fn) {
  1766. // Don't want to add a cache object through getElData if not needed
  1767. if (!hasData(elem)) {
  1768. return;
  1769. }
  1770. var data = getData(elem);
  1771. // If no events exist, nothing to unbind
  1772. if (!data.handlers) {
  1773. return;
  1774. }
  1775. if (Array.isArray(type)) {
  1776. return _handleMultipleEvents(off, elem, type, fn);
  1777. }
  1778. // Utility function
  1779. var removeType = function removeType(el, t) {
  1780. data.handlers[t] = [];
  1781. _cleanUpEvents(el, t);
  1782. };
  1783. // Are we removing all bound events?
  1784. if (type === undefined) {
  1785. for (var t in data.handlers) {
  1786. if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
  1787. removeType(elem, t);
  1788. }
  1789. }
  1790. return;
  1791. }
  1792. var handlers = data.handlers[type];
  1793. // If no handlers exist, nothing to unbind
  1794. if (!handlers) {
  1795. return;
  1796. }
  1797. // If no listener was provided, remove all listeners for type
  1798. if (!fn) {
  1799. removeType(elem, type);
  1800. return;
  1801. }
  1802. // We're only removing a single handler
  1803. if (fn.guid) {
  1804. for (var n = 0; n < handlers.length; n++) {
  1805. if (handlers[n].guid === fn.guid) {
  1806. handlers.splice(n--, 1);
  1807. }
  1808. }
  1809. }
  1810. _cleanUpEvents(elem, type);
  1811. }
  1812. /**
  1813. * Trigger an event for an element
  1814. *
  1815. * @param {Element|Object} elem
  1816. * Element to trigger an event on
  1817. *
  1818. * @param {EventTarget~Event|string} event
  1819. * A string (the type) or an event object with a type attribute
  1820. *
  1821. * @param {Object} [hash]
  1822. * data hash to pass along with the event
  1823. *
  1824. * @return {boolean|undefined}
  1825. * - Returns the opposite of `defaultPrevented` if default was prevented
  1826. * - Otherwise returns undefined
  1827. */
  1828. function trigger(elem, event, hash) {
  1829. // Fetches element data and a reference to the parent (for bubbling).
  1830. // Don't want to add a data object to cache for every parent,
  1831. // so checking hasElData first.
  1832. var elemData = hasData(elem) ? getData(elem) : {};
  1833. var parent = elem.parentNode || elem.ownerDocument;
  1834. // type = event.type || event,
  1835. // handler;
  1836. // If an event name was passed as a string, creates an event out of it
  1837. if (typeof event === 'string') {
  1838. event = { type: event, target: elem };
  1839. } else if (!event.target) {
  1840. event.target = elem;
  1841. }
  1842. // Normalizes the event properties.
  1843. event = fixEvent(event);
  1844. // If the passed element has a dispatcher, executes the established handlers.
  1845. if (elemData.dispatcher) {
  1846. elemData.dispatcher.call(elem, event, hash);
  1847. }
  1848. // Unless explicitly stopped or the event does not bubble (e.g. media events)
  1849. // recursively calls this function to bubble the event up the DOM.
  1850. if (parent && !event.isPropagationStopped() && event.bubbles === true) {
  1851. trigger.call(null, parent, event, hash);
  1852. // If at the top of the DOM, triggers the default action unless disabled.
  1853. } else if (!parent && !event.defaultPrevented) {
  1854. var targetData = getData(event.target);
  1855. // Checks if the target has a default action for this event.
  1856. if (event.target[event.type]) {
  1857. // Temporarily disables event dispatching on the target as we have already executed the handler.
  1858. targetData.disabled = true;
  1859. // Executes the default action.
  1860. if (typeof event.target[event.type] === 'function') {
  1861. event.target[event.type]();
  1862. }
  1863. // Re-enables event dispatching.
  1864. targetData.disabled = false;
  1865. }
  1866. }
  1867. // Inform the triggerer if the default was prevented by returning false
  1868. return !event.defaultPrevented;
  1869. }
  1870. /**
  1871. * Trigger a listener only once for an event
  1872. *
  1873. * @param {Element|Object} elem
  1874. * Element or object to bind to.
  1875. *
  1876. * @param {string|string[]} type
  1877. * Name/type of event
  1878. *
  1879. * @param {Event~EventListener} fn
  1880. * Event Listener function
  1881. */
  1882. function one(elem, type, fn) {
  1883. if (Array.isArray(type)) {
  1884. return _handleMultipleEvents(one, elem, type, fn);
  1885. }
  1886. var func = function func() {
  1887. off(elem, type, func);
  1888. fn.apply(this, arguments);
  1889. };
  1890. // copy the guid to the new function so it can removed using the original function's ID
  1891. func.guid = fn.guid = fn.guid || newGUID();
  1892. on(elem, type, func);
  1893. }
  1894. var Events = (Object.freeze || Object)({
  1895. fixEvent: fixEvent,
  1896. on: on,
  1897. off: off,
  1898. trigger: trigger,
  1899. one: one
  1900. });
  1901. /**
  1902. * @file setup.js - Functions for setting up a player without
  1903. * user interaction based on the data-setup `attribute` of the video tag.
  1904. *
  1905. * @module setup
  1906. */
  1907. var _windowLoaded = false;
  1908. var videojs$2 = void 0;
  1909. /**
  1910. * Set up any tags that have a data-setup `attribute` when the player is started.
  1911. */
  1912. var autoSetup = function autoSetup() {
  1913. // Protect against breakage in non-browser environments and check global autoSetup option.
  1914. if (!isReal() || videojs$2.options.autoSetup === false) {
  1915. return;
  1916. }
  1917. // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
  1918. // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
  1919. // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
  1920. // var mediaEls = vids.concat(audios);
  1921. // Because IE8 doesn't support calling slice on a node list, we need to loop
  1922. // through each list of elements to build up a new, combined list of elements.
  1923. var vids = document_1.getElementsByTagName('video');
  1924. var audios = document_1.getElementsByTagName('audio');
  1925. var divs = document_1.getElementsByTagName('video-js');
  1926. var mediaEls = [];
  1927. if (vids && vids.length > 0) {
  1928. for (var i = 0, e = vids.length; i < e; i++) {
  1929. mediaEls.push(vids[i]);
  1930. }
  1931. }
  1932. if (audios && audios.length > 0) {
  1933. for (var _i = 0, _e = audios.length; _i < _e; _i++) {
  1934. mediaEls.push(audios[_i]);
  1935. }
  1936. }
  1937. if (divs && divs.length > 0) {
  1938. for (var _i2 = 0, _e2 = divs.length; _i2 < _e2; _i2++) {
  1939. mediaEls.push(divs[_i2]);
  1940. }
  1941. }
  1942. // Check if any media elements exist
  1943. if (mediaEls && mediaEls.length > 0) {
  1944. for (var _i3 = 0, _e3 = mediaEls.length; _i3 < _e3; _i3++) {
  1945. var mediaEl = mediaEls[_i3];
  1946. // Check if element exists, has getAttribute func.
  1947. // IE seems to consider typeof el.getAttribute == 'object' instead of
  1948. // 'function' like expected, at least when loading the player immediately.
  1949. if (mediaEl && mediaEl.getAttribute) {
  1950. // Make sure this player hasn't already been set up.
  1951. if (mediaEl.player === undefined) {
  1952. var options = mediaEl.getAttribute('data-setup');
  1953. // Check if data-setup attr exists.
  1954. // We only auto-setup if they've added the data-setup attr.
  1955. if (options !== null) {
  1956. // Create new video.js instance.
  1957. videojs$2(mediaEl);
  1958. }
  1959. }
  1960. // If getAttribute isn't defined, we need to wait for the DOM.
  1961. } else {
  1962. autoSetupTimeout(1);
  1963. break;
  1964. }
  1965. }
  1966. // No videos were found, so keep looping unless page is finished loading.
  1967. } else if (!_windowLoaded) {
  1968. autoSetupTimeout(1);
  1969. }
  1970. };
  1971. /**
  1972. * Wait until the page is loaded before running autoSetup. This will be called in
  1973. * autoSetup if `hasLoaded` returns false.
  1974. *
  1975. * @param {number} wait
  1976. * How long to wait in ms
  1977. *
  1978. * @param {module:videojs} [vjs]
  1979. * The videojs library function
  1980. */
  1981. function autoSetupTimeout(wait, vjs) {
  1982. if (vjs) {
  1983. videojs$2 = vjs;
  1984. }
  1985. window_1.setTimeout(autoSetup, wait);
  1986. }
  1987. if (isReal() && document_1.readyState === 'complete') {
  1988. _windowLoaded = true;
  1989. } else {
  1990. /**
  1991. * Listen for the load event on window, and set _windowLoaded to true.
  1992. *
  1993. * @listens load
  1994. */
  1995. one(window_1, 'load', function () {
  1996. _windowLoaded = true;
  1997. });
  1998. }
  1999. /**
  2000. * @file stylesheet.js
  2001. * @module stylesheet
  2002. */
  2003. /**
  2004. * Create a DOM syle element given a className for it.
  2005. *
  2006. * @param {string} className
  2007. * The className to add to the created style element.
  2008. *
  2009. * @return {Element}
  2010. * The element that was created.
  2011. */
  2012. var createStyleElement = function createStyleElement(className) {
  2013. var style = document_1.createElement('style');
  2014. style.className = className;
  2015. return style;
  2016. };
  2017. /**
  2018. * Add text to a DOM element.
  2019. *
  2020. * @param {Element} el
  2021. * The Element to add text content to.
  2022. *
  2023. * @param {string} content
  2024. * The text to add to the element.
  2025. */
  2026. var setTextContent = function setTextContent(el, content) {
  2027. if (el.styleSheet) {
  2028. el.styleSheet.cssText = content;
  2029. } else {
  2030. el.textContent = content;
  2031. }
  2032. };
  2033. /**
  2034. * @file fn.js
  2035. * @module fn
  2036. */
  2037. /**
  2038. * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
  2039. * It also stores a unique id on the function so it can be easily removed from events.
  2040. *
  2041. * @param {Mixed} context
  2042. * The object to bind as scope.
  2043. *
  2044. * @param {Function} fn
  2045. * The function to be bound to a scope.
  2046. *
  2047. * @param {number} [uid]
  2048. * An optional unique ID for the function to be set
  2049. *
  2050. * @return {Function}
  2051. * The new function that will be bound into the context given
  2052. */
  2053. var bind = function bind(context, fn, uid) {
  2054. // Make sure the function has a unique ID
  2055. if (!fn.guid) {
  2056. fn.guid = newGUID();
  2057. }
  2058. // Create the new function that changes the context
  2059. var bound = function bound() {
  2060. return fn.apply(context, arguments);
  2061. };
  2062. // Allow for the ability to individualize this function
  2063. // Needed in the case where multiple objects might share the same prototype
  2064. // IF both items add an event listener with the same function, then you try to remove just one
  2065. // it will remove both because they both have the same guid.
  2066. // when using this, you need to use the bind method when you remove the listener as well.
  2067. // currently used in text tracks
  2068. bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
  2069. return bound;
  2070. };
  2071. /**
  2072. * Wraps the given function, `fn`, with a new function that only invokes `fn`
  2073. * at most once per every `wait` milliseconds.
  2074. *
  2075. * @param {Function} fn
  2076. * The function to be throttled.
  2077. *
  2078. * @param {Number} wait
  2079. * The number of milliseconds by which to throttle.
  2080. *
  2081. * @return {Function}
  2082. */
  2083. var throttle = function throttle(fn, wait) {
  2084. var last = Date.now();
  2085. var throttled = function throttled() {
  2086. var now = Date.now();
  2087. if (now - last >= wait) {
  2088. fn.apply(undefined, arguments);
  2089. last = now;
  2090. }
  2091. };
  2092. return throttled;
  2093. };
  2094. /**
  2095. * Creates a debounced function that delays invoking `func` until after `wait`
  2096. * milliseconds have elapsed since the last time the debounced function was
  2097. * invoked.
  2098. *
  2099. * Inspired by lodash and underscore implementations.
  2100. *
  2101. * @param {Function} func
  2102. * The function to wrap with debounce behavior.
  2103. *
  2104. * @param {number} wait
  2105. * The number of milliseconds to wait after the last invocation.
  2106. *
  2107. * @param {boolean} [immediate]
  2108. * Whether or not to invoke the function immediately upon creation.
  2109. *
  2110. * @param {Object} [context=window]
  2111. * The "context" in which the debounced function should debounce. For
  2112. * example, if this function should be tied to a Video.js player,
  2113. * the player can be passed here. Alternatively, defaults to the
  2114. * global `window` object.
  2115. *
  2116. * @return {Function}
  2117. * A debounced function.
  2118. */
  2119. var debounce = function debounce(func, wait, immediate) {
  2120. var context = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : window_1;
  2121. var timeout = void 0;
  2122. var cancel = function cancel() {
  2123. context.clearTimeout(timeout);
  2124. timeout = null;
  2125. };
  2126. /* eslint-disable consistent-this */
  2127. var debounced = function debounced() {
  2128. var self = this;
  2129. var args = arguments;
  2130. var _later = function later() {
  2131. timeout = null;
  2132. _later = null;
  2133. if (!immediate) {
  2134. func.apply(self, args);
  2135. }
  2136. };
  2137. if (!timeout && immediate) {
  2138. func.apply(self, args);
  2139. }
  2140. context.clearTimeout(timeout);
  2141. timeout = context.setTimeout(_later, wait);
  2142. };
  2143. /* eslint-enable consistent-this */
  2144. debounced.cancel = cancel;
  2145. return debounced;
  2146. };
  2147. /**
  2148. * @file src/js/event-target.js
  2149. */
  2150. /**
  2151. * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
  2152. * adds shorthand functions that wrap around lengthy functions. For example:
  2153. * the `on` function is a wrapper around `addEventListener`.
  2154. *
  2155. * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
  2156. * @class EventTarget
  2157. */
  2158. var EventTarget = function EventTarget() {};
  2159. /**
  2160. * A Custom DOM event.
  2161. *
  2162. * @typedef {Object} EventTarget~Event
  2163. * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
  2164. */
  2165. /**
  2166. * All event listeners should follow the following format.
  2167. *
  2168. * @callback EventTarget~EventListener
  2169. * @this {EventTarget}
  2170. *
  2171. * @param {EventTarget~Event} event
  2172. * the event that triggered this function
  2173. *
  2174. * @param {Object} [hash]
  2175. * hash of data sent during the event
  2176. */
  2177. /**
  2178. * An object containing event names as keys and booleans as values.
  2179. *
  2180. * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
  2181. * will have extra functionality. See that function for more information.
  2182. *
  2183. * @property EventTarget.prototype.allowedEvents_
  2184. * @private
  2185. */
  2186. EventTarget.prototype.allowedEvents_ = {};
  2187. /**
  2188. * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
  2189. * function that will get called when an event with a certain name gets triggered.
  2190. *
  2191. * @param {string|string[]} type
  2192. * An event name or an array of event names.
  2193. *
  2194. * @param {EventTarget~EventListener} fn
  2195. * The function to call with `EventTarget`s
  2196. */
  2197. EventTarget.prototype.on = function (type, fn) {
  2198. // Remove the addEventListener alias before calling Events.on
  2199. // so we don't get into an infinite type loop
  2200. var ael = this.addEventListener;
  2201. this.addEventListener = function () {};
  2202. on(this, type, fn);
  2203. this.addEventListener = ael;
  2204. };
  2205. /**
  2206. * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
  2207. * the standard DOM API.
  2208. *
  2209. * @function
  2210. * @see {@link EventTarget#on}
  2211. */
  2212. EventTarget.prototype.addEventListener = EventTarget.prototype.on;
  2213. /**
  2214. * Removes an `event listener` for a specific event from an instance of `EventTarget`.
  2215. * This makes it so that the `event listener` will no longer get called when the
  2216. * named event happens.
  2217. *
  2218. * @param {string|string[]} type
  2219. * An event name or an array of event names.
  2220. *
  2221. * @param {EventTarget~EventListener} fn
  2222. * The function to remove.
  2223. */
  2224. EventTarget.prototype.off = function (type, fn) {
  2225. off(this, type, fn);
  2226. };
  2227. /**
  2228. * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
  2229. * the standard DOM API.
  2230. *
  2231. * @function
  2232. * @see {@link EventTarget#off}
  2233. */
  2234. EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
  2235. /**
  2236. * This function will add an `event listener` that gets triggered only once. After the
  2237. * first trigger it will get removed. This is like adding an `event listener`
  2238. * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
  2239. *
  2240. * @param {string|string[]} type
  2241. * An event name or an array of event names.
  2242. *
  2243. * @param {EventTarget~EventListener} fn
  2244. * The function to be called once for each event name.
  2245. */
  2246. EventTarget.prototype.one = function (type, fn) {
  2247. // Remove the addEventListener alialing Events.on
  2248. // so we don't get into an infinite type loop
  2249. var ael = this.addEventListener;
  2250. this.addEventListener = function () {};
  2251. one(this, type, fn);
  2252. this.addEventListener = ael;
  2253. };
  2254. /**
  2255. * This function causes an event to happen. This will then cause any `event listeners`
  2256. * that are waiting for that event, to get called. If there are no `event listeners`
  2257. * for an event then nothing will happen.
  2258. *
  2259. * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
  2260. * Trigger will also call the `on` + `uppercaseEventName` function.
  2261. *
  2262. * Example:
  2263. * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
  2264. * `onClick` if it exists.
  2265. *
  2266. * @param {string|EventTarget~Event|Object} event
  2267. * The name of the event, an `Event`, or an object with a key of type set to
  2268. * an event name.
  2269. */
  2270. EventTarget.prototype.trigger = function (event) {
  2271. var type = event.type || event;
  2272. if (typeof event === 'string') {
  2273. event = { type: type };
  2274. }
  2275. event = fixEvent(event);
  2276. if (this.allowedEvents_[type] && this['on' + type]) {
  2277. this['on' + type](event);
  2278. }
  2279. trigger(this, event);
  2280. };
  2281. /**
  2282. * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
  2283. * the standard DOM API.
  2284. *
  2285. * @function
  2286. * @see {@link EventTarget#trigger}
  2287. */
  2288. EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
  2289. /**
  2290. * @file mixins/evented.js
  2291. * @module evented
  2292. */
  2293. /**
  2294. * Returns whether or not an object has had the evented mixin applied.
  2295. *
  2296. * @param {Object} object
  2297. * An object to test.
  2298. *
  2299. * @return {boolean}
  2300. * Whether or not the object appears to be evented.
  2301. */
  2302. var isEvented = function isEvented(object) {
  2303. return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
  2304. return typeof object[k] === 'function';
  2305. });
  2306. };
  2307. /**
  2308. * Whether a value is a valid event type - non-empty string or array.
  2309. *
  2310. * @private
  2311. * @param {string|Array} type
  2312. * The type value to test.
  2313. *
  2314. * @return {boolean}
  2315. * Whether or not the type is a valid event type.
  2316. */
  2317. var isValidEventType = function isValidEventType(type) {
  2318. return (
  2319. // The regex here verifies that the `type` contains at least one non-
  2320. // whitespace character.
  2321. typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
  2322. );
  2323. };
  2324. /**
  2325. * Validates a value to determine if it is a valid event target. Throws if not.
  2326. *
  2327. * @private
  2328. * @throws {Error}
  2329. * If the target does not appear to be a valid event target.
  2330. *
  2331. * @param {Object} target
  2332. * The object to test.
  2333. */
  2334. var validateTarget = function validateTarget(target) {
  2335. if (!target.nodeName && !isEvented(target)) {
  2336. throw new Error('Invalid target; must be a DOM node or evented object.');
  2337. }
  2338. };
  2339. /**
  2340. * Validates a value to determine if it is a valid event target. Throws if not.
  2341. *
  2342. * @private
  2343. * @throws {Error}
  2344. * If the type does not appear to be a valid event type.
  2345. *
  2346. * @param {string|Array} type
  2347. * The type to test.
  2348. */
  2349. var validateEventType = function validateEventType(type) {
  2350. if (!isValidEventType(type)) {
  2351. throw new Error('Invalid event type; must be a non-empty string or array.');
  2352. }
  2353. };
  2354. /**
  2355. * Validates a value to determine if it is a valid listener. Throws if not.
  2356. *
  2357. * @private
  2358. * @throws {Error}
  2359. * If the listener is not a function.
  2360. *
  2361. * @param {Function} listener
  2362. * The listener to test.
  2363. */
  2364. var validateListener = function validateListener(listener) {
  2365. if (typeof listener !== 'function') {
  2366. throw new Error('Invalid listener; must be a function.');
  2367. }
  2368. };
  2369. /**
  2370. * Takes an array of arguments given to `on()` or `one()`, validates them, and
  2371. * normalizes them into an object.
  2372. *
  2373. * @private
  2374. * @param {Object} self
  2375. * The evented object on which `on()` or `one()` was called. This
  2376. * object will be bound as the `this` value for the listener.
  2377. *
  2378. * @param {Array} args
  2379. * An array of arguments passed to `on()` or `one()`.
  2380. *
  2381. * @return {Object}
  2382. * An object containing useful values for `on()` or `one()` calls.
  2383. */
  2384. var normalizeListenArgs = function normalizeListenArgs(self, args) {
  2385. // If the number of arguments is less than 3, the target is always the
  2386. // evented object itself.
  2387. var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
  2388. var target = void 0;
  2389. var type = void 0;
  2390. var listener = void 0;
  2391. if (isTargetingSelf) {
  2392. target = self.eventBusEl_;
  2393. // Deal with cases where we got 3 arguments, but we are still listening to
  2394. // the evented object itself.
  2395. if (args.length >= 3) {
  2396. args.shift();
  2397. }
  2398. type = args[0];
  2399. listener = args[1];
  2400. } else {
  2401. target = args[0];
  2402. type = args[1];
  2403. listener = args[2];
  2404. }
  2405. validateTarget(target);
  2406. validateEventType(type);
  2407. validateListener(listener);
  2408. listener = bind(self, listener);
  2409. return { isTargetingSelf: isTargetingSelf, target: target, type: type, listener: listener };
  2410. };
  2411. /**
  2412. * Adds the listener to the event type(s) on the target, normalizing for
  2413. * the type of target.
  2414. *
  2415. * @private
  2416. * @param {Element|Object} target
  2417. * A DOM node or evented object.
  2418. *
  2419. * @param {string} method
  2420. * The event binding method to use ("on" or "one").
  2421. *
  2422. * @param {string|Array} type
  2423. * One or more event type(s).
  2424. *
  2425. * @param {Function} listener
  2426. * A listener function.
  2427. */
  2428. var listen = function listen(target, method, type, listener) {
  2429. validateTarget(target);
  2430. if (target.nodeName) {
  2431. Events[method](target, type, listener);
  2432. } else {
  2433. target[method](type, listener);
  2434. }
  2435. };
  2436. /**
  2437. * Contains methods that provide event capabilites to an object which is passed
  2438. * to {@link module:evented|evented}.
  2439. *
  2440. * @mixin EventedMixin
  2441. */
  2442. var EventedMixin = {
  2443. /**
  2444. * Add a listener to an event (or events) on this object or another evented
  2445. * object.
  2446. *
  2447. * @param {string|Array|Element|Object} targetOrType
  2448. * If this is a string or array, it represents the event type(s)
  2449. * that will trigger the listener.
  2450. *
  2451. * Another evented object can be passed here instead, which will
  2452. * cause the listener to listen for events on _that_ object.
  2453. *
  2454. * In either case, the listener's `this` value will be bound to
  2455. * this object.
  2456. *
  2457. * @param {string|Array|Function} typeOrListener
  2458. * If the first argument was a string or array, this should be the
  2459. * listener function. Otherwise, this is a string or array of event
  2460. * type(s).
  2461. *
  2462. * @param {Function} [listener]
  2463. * If the first argument was another evented object, this will be
  2464. * the listener function.
  2465. */
  2466. on: function on$$1() {
  2467. var _this = this;
  2468. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  2469. args[_key] = arguments[_key];
  2470. }
  2471. var _normalizeListenArgs = normalizeListenArgs(this, args),
  2472. isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
  2473. target = _normalizeListenArgs.target,
  2474. type = _normalizeListenArgs.type,
  2475. listener = _normalizeListenArgs.listener;
  2476. listen(target, 'on', type, listener);
  2477. // If this object is listening to another evented object.
  2478. if (!isTargetingSelf) {
  2479. // If this object is disposed, remove the listener.
  2480. var removeListenerOnDispose = function removeListenerOnDispose() {
  2481. return _this.off(target, type, listener);
  2482. };
  2483. // Use the same function ID as the listener so we can remove it later it
  2484. // using the ID of the original listener.
  2485. removeListenerOnDispose.guid = listener.guid;
  2486. // Add a listener to the target's dispose event as well. This ensures
  2487. // that if the target is disposed BEFORE this object, we remove the
  2488. // removal listener that was just added. Otherwise, we create a memory leak.
  2489. var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
  2490. return _this.off('dispose', removeListenerOnDispose);
  2491. };
  2492. // Use the same function ID as the listener so we can remove it later
  2493. // it using the ID of the original listener.
  2494. removeRemoverOnTargetDispose.guid = listener.guid;
  2495. listen(this, 'on', 'dispose', removeListenerOnDispose);
  2496. listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
  2497. }
  2498. },
  2499. /**
  2500. * Add a listener to an event (or events) on this object or another evented
  2501. * object. The listener will only be called once and then removed.
  2502. *
  2503. * @param {string|Array|Element|Object} targetOrType
  2504. * If this is a string or array, it represents the event type(s)
  2505. * that will trigger the listener.
  2506. *
  2507. * Another evented object can be passed here instead, which will
  2508. * cause the listener to listen for events on _that_ object.
  2509. *
  2510. * In either case, the listener's `this` value will be bound to
  2511. * this object.
  2512. *
  2513. * @param {string|Array|Function} typeOrListener
  2514. * If the first argument was a string or array, this should be the
  2515. * listener function. Otherwise, this is a string or array of event
  2516. * type(s).
  2517. *
  2518. * @param {Function} [listener]
  2519. * If the first argument was another evented object, this will be
  2520. * the listener function.
  2521. */
  2522. one: function one$$1() {
  2523. var _this2 = this;
  2524. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  2525. args[_key2] = arguments[_key2];
  2526. }
  2527. var _normalizeListenArgs2 = normalizeListenArgs(this, args),
  2528. isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
  2529. target = _normalizeListenArgs2.target,
  2530. type = _normalizeListenArgs2.type,
  2531. listener = _normalizeListenArgs2.listener;
  2532. // Targeting this evented object.
  2533. if (isTargetingSelf) {
  2534. listen(target, 'one', type, listener);
  2535. // Targeting another evented object.
  2536. } else {
  2537. var wrapper = function wrapper() {
  2538. for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  2539. largs[_key3] = arguments[_key3];
  2540. }
  2541. _this2.off(target, type, wrapper);
  2542. listener.apply(null, largs);
  2543. };
  2544. // Use the same function ID as the listener so we can remove it later
  2545. // it using the ID of the original listener.
  2546. wrapper.guid = listener.guid;
  2547. listen(target, 'one', type, wrapper);
  2548. }
  2549. },
  2550. /**
  2551. * Removes listener(s) from event(s) on an evented object.
  2552. *
  2553. * @param {string|Array|Element|Object} [targetOrType]
  2554. * If this is a string or array, it represents the event type(s).
  2555. *
  2556. * Another evented object can be passed here instead, in which case
  2557. * ALL 3 arguments are _required_.
  2558. *
  2559. * @param {string|Array|Function} [typeOrListener]
  2560. * If the first argument was a string or array, this may be the
  2561. * listener function. Otherwise, this is a string or array of event
  2562. * type(s).
  2563. *
  2564. * @param {Function} [listener]
  2565. * If the first argument was another evented object, this will be
  2566. * the listener function; otherwise, _all_ listeners bound to the
  2567. * event type(s) will be removed.
  2568. */
  2569. off: function off$$1(targetOrType, typeOrListener, listener) {
  2570. // Targeting this evented object.
  2571. if (!targetOrType || isValidEventType(targetOrType)) {
  2572. off(this.eventBusEl_, targetOrType, typeOrListener);
  2573. // Targeting another evented object.
  2574. } else {
  2575. var target = targetOrType;
  2576. var type = typeOrListener;
  2577. // Fail fast and in a meaningful way!
  2578. validateTarget(target);
  2579. validateEventType(type);
  2580. validateListener(listener);
  2581. // Ensure there's at least a guid, even if the function hasn't been used
  2582. listener = bind(this, listener);
  2583. // Remove the dispose listener on this evented object, which was given
  2584. // the same guid as the event listener in on().
  2585. this.off('dispose', listener);
  2586. if (target.nodeName) {
  2587. off(target, type, listener);
  2588. off(target, 'dispose', listener);
  2589. } else if (isEvented(target)) {
  2590. target.off(type, listener);
  2591. target.off('dispose', listener);
  2592. }
  2593. }
  2594. },
  2595. /**
  2596. * Fire an event on this evented object, causing its listeners to be called.
  2597. *
  2598. * @param {string|Object} event
  2599. * An event type or an object with a type property.
  2600. *
  2601. * @param {Object} [hash]
  2602. * An additional object to pass along to listeners.
  2603. *
  2604. * @returns {boolean}
  2605. * Whether or not the default behavior was prevented.
  2606. */
  2607. trigger: function trigger$$1(event, hash) {
  2608. return trigger(this.eventBusEl_, event, hash);
  2609. }
  2610. };
  2611. /**
  2612. * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
  2613. *
  2614. * @param {Object} target
  2615. * The object to which to add event methods.
  2616. *
  2617. * @param {Object} [options={}]
  2618. * Options for customizing the mixin behavior.
  2619. *
  2620. * @param {String} [options.eventBusKey]
  2621. * By default, adds a `eventBusEl_` DOM element to the target object,
  2622. * which is used as an event bus. If the target object already has a
  2623. * DOM element that should be used, pass its key here.
  2624. *
  2625. * @return {Object}
  2626. * The target object.
  2627. */
  2628. function evented(target) {
  2629. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  2630. var eventBusKey = options.eventBusKey;
  2631. // Set or create the eventBusEl_.
  2632. if (eventBusKey) {
  2633. if (!target[eventBusKey].nodeName) {
  2634. throw new Error('The eventBusKey "' + eventBusKey + '" does not refer to an element.');
  2635. }
  2636. target.eventBusEl_ = target[eventBusKey];
  2637. } else {
  2638. target.eventBusEl_ = createEl('span', { className: 'vjs-event-bus' });
  2639. }
  2640. assign(target, EventedMixin);
  2641. // When any evented object is disposed, it removes all its listeners.
  2642. target.on('dispose', function () {
  2643. target.off();
  2644. window_1.setTimeout(function () {
  2645. target.eventBusEl_ = null;
  2646. }, 0);
  2647. });
  2648. return target;
  2649. }
  2650. /**
  2651. * @file mixins/stateful.js
  2652. * @module stateful
  2653. */
  2654. /**
  2655. * Contains methods that provide statefulness to an object which is passed
  2656. * to {@link module:stateful}.
  2657. *
  2658. * @mixin StatefulMixin
  2659. */
  2660. var StatefulMixin = {
  2661. /**
  2662. * A hash containing arbitrary keys and values representing the state of
  2663. * the object.
  2664. *
  2665. * @type {Object}
  2666. */
  2667. state: {},
  2668. /**
  2669. * Set the state of an object by mutating its
  2670. * {@link module:stateful~StatefulMixin.state|state} object in place.
  2671. *
  2672. * @fires module:stateful~StatefulMixin#statechanged
  2673. * @param {Object|Function} stateUpdates
  2674. * A new set of properties to shallow-merge into the plugin state.
  2675. * Can be a plain object or a function returning a plain object.
  2676. *
  2677. * @returns {Object|undefined}
  2678. * An object containing changes that occurred. If no changes
  2679. * occurred, returns `undefined`.
  2680. */
  2681. setState: function setState(stateUpdates) {
  2682. var _this = this;
  2683. // Support providing the `stateUpdates` state as a function.
  2684. if (typeof stateUpdates === 'function') {
  2685. stateUpdates = stateUpdates();
  2686. }
  2687. var changes = void 0;
  2688. each(stateUpdates, function (value, key) {
  2689. // Record the change if the value is different from what's in the
  2690. // current state.
  2691. if (_this.state[key] !== value) {
  2692. changes = changes || {};
  2693. changes[key] = {
  2694. from: _this.state[key],
  2695. to: value
  2696. };
  2697. }
  2698. _this.state[key] = value;
  2699. });
  2700. // Only trigger "statechange" if there were changes AND we have a trigger
  2701. // function. This allows us to not require that the target object be an
  2702. // evented object.
  2703. if (changes && isEvented(this)) {
  2704. /**
  2705. * An event triggered on an object that is both
  2706. * {@link module:stateful|stateful} and {@link module:evented|evented}
  2707. * indicating that its state has changed.
  2708. *
  2709. * @event module:stateful~StatefulMixin#statechanged
  2710. * @type {Object}
  2711. * @property {Object} changes
  2712. * A hash containing the properties that were changed and
  2713. * the values they were changed `from` and `to`.
  2714. */
  2715. this.trigger({
  2716. changes: changes,
  2717. type: 'statechanged'
  2718. });
  2719. }
  2720. return changes;
  2721. }
  2722. };
  2723. /**
  2724. * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
  2725. * object.
  2726. *
  2727. * If the target object is {@link module:evented|evented} and has a
  2728. * `handleStateChanged` method, that method will be automatically bound to the
  2729. * `statechanged` event on itself.
  2730. *
  2731. * @param {Object} target
  2732. * The object to be made stateful.
  2733. *
  2734. * @param {Object} [defaultState]
  2735. * A default set of properties to populate the newly-stateful object's
  2736. * `state` property.
  2737. *
  2738. * @returns {Object}
  2739. * Returns the `target`.
  2740. */
  2741. function stateful(target, defaultState) {
  2742. assign(target, StatefulMixin);
  2743. // This happens after the mixing-in because we need to replace the `state`
  2744. // added in that step.
  2745. target.state = assign({}, target.state, defaultState);
  2746. // Auto-bind the `handleStateChanged` method of the target object if it exists.
  2747. if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
  2748. target.on('statechanged', target.handleStateChanged);
  2749. }
  2750. return target;
  2751. }
  2752. /**
  2753. * @file to-title-case.js
  2754. * @module to-title-case
  2755. */
  2756. /**
  2757. * Uppercase the first letter of a string.
  2758. *
  2759. * @param {string} string
  2760. * String to be uppercased
  2761. *
  2762. * @return {string}
  2763. * The string with an uppercased first letter
  2764. */
  2765. function toTitleCase(string) {
  2766. if (typeof string !== 'string') {
  2767. return string;
  2768. }
  2769. return string.charAt(0).toUpperCase() + string.slice(1);
  2770. }
  2771. /**
  2772. * Compares the TitleCase versions of the two strings for equality.
  2773. *
  2774. * @param {string} str1
  2775. * The first string to compare
  2776. *
  2777. * @param {string} str2
  2778. * The second string to compare
  2779. *
  2780. * @return {boolean}
  2781. * Whether the TitleCase versions of the strings are equal
  2782. */
  2783. function titleCaseEquals(str1, str2) {
  2784. return toTitleCase(str1) === toTitleCase(str2);
  2785. }
  2786. /**
  2787. * @file merge-options.js
  2788. * @module merge-options
  2789. */
  2790. /**
  2791. * Deep-merge one or more options objects, recursively merging **only** plain
  2792. * object properties.
  2793. *
  2794. * @param {Object[]} sources
  2795. * One or more objects to merge into a new object.
  2796. *
  2797. * @returns {Object}
  2798. * A new object that is the merged result of all sources.
  2799. */
  2800. function mergeOptions() {
  2801. var result = {};
  2802. for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
  2803. sources[_key] = arguments[_key];
  2804. }
  2805. sources.forEach(function (source) {
  2806. if (!source) {
  2807. return;
  2808. }
  2809. each(source, function (value, key) {
  2810. if (!isPlain(value)) {
  2811. result[key] = value;
  2812. return;
  2813. }
  2814. if (!isPlain(result[key])) {
  2815. result[key] = {};
  2816. }
  2817. result[key] = mergeOptions(result[key], value);
  2818. });
  2819. });
  2820. return result;
  2821. }
  2822. /**
  2823. * Player Component - Base class for all UI objects
  2824. *
  2825. * @file component.js
  2826. */
  2827. /**
  2828. * Base class for all UI Components.
  2829. * Components are UI objects which represent both a javascript object and an element
  2830. * in the DOM. They can be children of other components, and can have
  2831. * children themselves.
  2832. *
  2833. * Components can also use methods from {@link EventTarget}
  2834. */
  2835. var Component = function () {
  2836. /**
  2837. * A callback that is called when a component is ready. Does not have any
  2838. * paramters and any callback value will be ignored.
  2839. *
  2840. * @callback Component~ReadyCallback
  2841. * @this Component
  2842. */
  2843. /**
  2844. * Creates an instance of this class.
  2845. *
  2846. * @param {Player} player
  2847. * The `Player` that this class should be attached to.
  2848. *
  2849. * @param {Object} [options]
  2850. * The key/value store of player options.
  2851. *
  2852. * @param {Object[]} [options.children]
  2853. * An array of children objects to intialize this component with. Children objects have
  2854. * a name property that will be used if more than one component of the same type needs to be
  2855. * added.
  2856. *
  2857. * @param {Component~ReadyCallback} [ready]
  2858. * Function that gets called when the `Component` is ready.
  2859. */
  2860. function Component(player, options, ready) {
  2861. classCallCheck(this, Component);
  2862. // The component might be the player itself and we can't pass `this` to super
  2863. if (!player && this.play) {
  2864. this.player_ = player = this; // eslint-disable-line
  2865. } else {
  2866. this.player_ = player;
  2867. }
  2868. // Make a copy of prototype.options_ to protect against overriding defaults
  2869. this.options_ = mergeOptions({}, this.options_);
  2870. // Updated options with supplied options
  2871. options = this.options_ = mergeOptions(this.options_, options);
  2872. // Get ID from options or options element if one is supplied
  2873. this.id_ = options.id || options.el && options.el.id;
  2874. // If there was no ID from the options, generate one
  2875. if (!this.id_) {
  2876. // Don't require the player ID function in the case of mock players
  2877. var id = player && player.id && player.id() || 'no_player';
  2878. this.id_ = id + '_component_' + newGUID();
  2879. }
  2880. this.name_ = options.name || null;
  2881. // Create element if one wasn't provided in options
  2882. if (options.el) {
  2883. this.el_ = options.el;
  2884. } else if (options.createEl !== false) {
  2885. this.el_ = this.createEl();
  2886. }
  2887. // if evented is anything except false, we want to mixin in evented
  2888. if (options.evented !== false) {
  2889. // Make this an evented object and use `el_`, if available, as its event bus
  2890. evented(this, { eventBusKey: this.el_ ? 'el_' : null });
  2891. }
  2892. stateful(this, this.constructor.defaultState);
  2893. this.children_ = [];
  2894. this.childIndex_ = {};
  2895. this.childNameIndex_ = {};
  2896. // Add any child components in options
  2897. if (options.initChildren !== false) {
  2898. this.initChildren();
  2899. }
  2900. this.ready(ready);
  2901. // Don't want to trigger ready here or it will before init is actually
  2902. // finished for all children that run this constructor
  2903. if (options.reportTouchActivity !== false) {
  2904. this.enableTouchActivity();
  2905. }
  2906. }
  2907. /**
  2908. * Dispose of the `Component` and all child components.
  2909. *
  2910. * @fires Component#dispose
  2911. */
  2912. Component.prototype.dispose = function dispose() {
  2913. /**
  2914. * Triggered when a `Component` is disposed.
  2915. *
  2916. * @event Component#dispose
  2917. * @type {EventTarget~Event}
  2918. *
  2919. * @property {boolean} [bubbles=false]
  2920. * set to false so that the close event does not
  2921. * bubble up
  2922. */
  2923. this.trigger({ type: 'dispose', bubbles: false });
  2924. // Dispose all children.
  2925. if (this.children_) {
  2926. for (var i = this.children_.length - 1; i >= 0; i--) {
  2927. if (this.children_[i].dispose) {
  2928. this.children_[i].dispose();
  2929. }
  2930. }
  2931. }
  2932. // Delete child references
  2933. this.children_ = null;
  2934. this.childIndex_ = null;
  2935. this.childNameIndex_ = null;
  2936. if (this.el_) {
  2937. // Remove element from DOM
  2938. if (this.el_.parentNode) {
  2939. this.el_.parentNode.removeChild(this.el_);
  2940. }
  2941. removeData(this.el_);
  2942. this.el_ = null;
  2943. }
  2944. // remove reference to the player after disposing of the element
  2945. this.player_ = null;
  2946. };
  2947. /**
  2948. * Return the {@link Player} that the `Component` has attached to.
  2949. *
  2950. * @return {Player}
  2951. * The player that this `Component` has attached to.
  2952. */
  2953. Component.prototype.player = function player() {
  2954. return this.player_;
  2955. };
  2956. /**
  2957. * Deep merge of options objects with new options.
  2958. * > Note: When both `obj` and `options` contain properties whose values are objects.
  2959. * The two properties get merged using {@link module:mergeOptions}
  2960. *
  2961. * @param {Object} obj
  2962. * The object that contains new options.
  2963. *
  2964. * @return {Object}
  2965. * A new object of `this.options_` and `obj` merged together.
  2966. *
  2967. * @deprecated since version 5
  2968. */
  2969. Component.prototype.options = function options(obj) {
  2970. log.warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
  2971. if (!obj) {
  2972. return this.options_;
  2973. }
  2974. this.options_ = mergeOptions(this.options_, obj);
  2975. return this.options_;
  2976. };
  2977. /**
  2978. * Get the `Component`s DOM element
  2979. *
  2980. * @return {Element}
  2981. * The DOM element for this `Component`.
  2982. */
  2983. Component.prototype.el = function el() {
  2984. return this.el_;
  2985. };
  2986. /**
  2987. * Create the `Component`s DOM element.
  2988. *
  2989. * @param {string} [tagName]
  2990. * Element's DOM node type. e.g. 'div'
  2991. *
  2992. * @param {Object} [properties]
  2993. * An object of properties that should be set.
  2994. *
  2995. * @param {Object} [attributes]
  2996. * An object of attributes that should be set.
  2997. *
  2998. * @return {Element}
  2999. * The element that gets created.
  3000. */
  3001. Component.prototype.createEl = function createEl$$1(tagName, properties, attributes) {
  3002. return createEl(tagName, properties, attributes);
  3003. };
  3004. /**
  3005. * Localize a string given the string in english.
  3006. *
  3007. * If tokens are provided, it'll try and run a simple token replacement on the provided string.
  3008. * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
  3009. *
  3010. * If a `defaultValue` is provided, it'll use that over `string`,
  3011. * if a value isn't found in provided language files.
  3012. * This is useful if you want to have a descriptive key for token replacement
  3013. * but have a succinct localized string and not require `en.json` to be included.
  3014. *
  3015. * Currently, it is used for the progress bar timing.
  3016. * ```js
  3017. * {
  3018. * "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
  3019. * }
  3020. * ```
  3021. * It is then used like so:
  3022. * ```js
  3023. * this.localize('progress bar timing: currentTime={1} duration{2}',
  3024. * [this.player_.currentTime(), this.player_.duration()],
  3025. * '{1} of {2}');
  3026. * ```
  3027. *
  3028. * Which outputs something like: `01:23 of 24:56`.
  3029. *
  3030. *
  3031. * @param {string} string
  3032. * The string to localize and the key to lookup in the language files.
  3033. * @param {string[]} [tokens]
  3034. * If the current item has token replacements, provide the tokens here.
  3035. * @param {string} [defaultValue]
  3036. * Defaults to `string`. Can be a default value to use for token replacement
  3037. * if the lookup key is needed to be separate.
  3038. *
  3039. * @return {string}
  3040. * The localized string or if no localization exists the english string.
  3041. */
  3042. Component.prototype.localize = function localize(string, tokens) {
  3043. var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : string;
  3044. var code = this.player_.language && this.player_.language();
  3045. var languages = this.player_.languages && this.player_.languages();
  3046. var language = languages && languages[code];
  3047. var primaryCode = code && code.split('-')[0];
  3048. var primaryLang = languages && languages[primaryCode];
  3049. var localizedString = defaultValue;
  3050. if (language && language[string]) {
  3051. localizedString = language[string];
  3052. } else if (primaryLang && primaryLang[string]) {
  3053. localizedString = primaryLang[string];
  3054. }
  3055. if (tokens) {
  3056. localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
  3057. var value = tokens[index - 1];
  3058. var ret = value;
  3059. if (typeof value === 'undefined') {
  3060. ret = match;
  3061. }
  3062. return ret;
  3063. });
  3064. }
  3065. return localizedString;
  3066. };
  3067. /**
  3068. * Return the `Component`s DOM element. This is where children get inserted.
  3069. * This will usually be the the same as the element returned in {@link Component#el}.
  3070. *
  3071. * @return {Element}
  3072. * The content element for this `Component`.
  3073. */
  3074. Component.prototype.contentEl = function contentEl() {
  3075. return this.contentEl_ || this.el_;
  3076. };
  3077. /**
  3078. * Get this `Component`s ID
  3079. *
  3080. * @return {string}
  3081. * The id of this `Component`
  3082. */
  3083. Component.prototype.id = function id() {
  3084. return this.id_;
  3085. };
  3086. /**
  3087. * Get the `Component`s name. The name gets used to reference the `Component`
  3088. * and is set during registration.
  3089. *
  3090. * @return {string}
  3091. * The name of this `Component`.
  3092. */
  3093. Component.prototype.name = function name() {
  3094. return this.name_;
  3095. };
  3096. /**
  3097. * Get an array of all child components
  3098. *
  3099. * @return {Array}
  3100. * The children
  3101. */
  3102. Component.prototype.children = function children() {
  3103. return this.children_;
  3104. };
  3105. /**
  3106. * Returns the child `Component` with the given `id`.
  3107. *
  3108. * @param {string} id
  3109. * The id of the child `Component` to get.
  3110. *
  3111. * @return {Component|undefined}
  3112. * The child `Component` with the given `id` or undefined.
  3113. */
  3114. Component.prototype.getChildById = function getChildById(id) {
  3115. return this.childIndex_[id];
  3116. };
  3117. /**
  3118. * Returns the child `Component` with the given `name`.
  3119. *
  3120. * @param {string} name
  3121. * The name of the child `Component` to get.
  3122. *
  3123. * @return {Component|undefined}
  3124. * The child `Component` with the given `name` or undefined.
  3125. */
  3126. Component.prototype.getChild = function getChild(name) {
  3127. if (!name) {
  3128. return;
  3129. }
  3130. name = toTitleCase(name);
  3131. return this.childNameIndex_[name];
  3132. };
  3133. /**
  3134. * Add a child `Component` inside the current `Component`.
  3135. *
  3136. *
  3137. * @param {string|Component} child
  3138. * The name or instance of a child to add.
  3139. *
  3140. * @param {Object} [options={}]
  3141. * The key/value store of options that will get passed to children of
  3142. * the child.
  3143. *
  3144. * @param {number} [index=this.children_.length]
  3145. * The index to attempt to add a child into.
  3146. *
  3147. * @return {Component}
  3148. * The `Component` that gets added as a child. When using a string the
  3149. * `Component` will get created by this process.
  3150. */
  3151. Component.prototype.addChild = function addChild(child) {
  3152. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  3153. var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
  3154. var component = void 0;
  3155. var componentName = void 0;
  3156. // If child is a string, create component with options
  3157. if (typeof child === 'string') {
  3158. componentName = toTitleCase(child);
  3159. var componentClassName = options.componentClass || componentName;
  3160. // Set name through options
  3161. options.name = componentName;
  3162. // Create a new object & element for this controls set
  3163. // If there's no .player_, this is a player
  3164. var ComponentClass = Component.getComponent(componentClassName);
  3165. if (!ComponentClass) {
  3166. throw new Error('Component ' + componentClassName + ' does not exist');
  3167. }
  3168. // data stored directly on the videojs object may be
  3169. // misidentified as a component to retain
  3170. // backwards-compatibility with 4.x. check to make sure the
  3171. // component class can be instantiated.
  3172. if (typeof ComponentClass !== 'function') {
  3173. return null;
  3174. }
  3175. component = new ComponentClass(this.player_ || this, options);
  3176. // child is a component instance
  3177. } else {
  3178. component = child;
  3179. }
  3180. this.children_.splice(index, 0, component);
  3181. if (typeof component.id === 'function') {
  3182. this.childIndex_[component.id()] = component;
  3183. }
  3184. // If a name wasn't used to create the component, check if we can use the
  3185. // name function of the component
  3186. componentName = componentName || component.name && toTitleCase(component.name());
  3187. if (componentName) {
  3188. this.childNameIndex_[componentName] = component;
  3189. }
  3190. // Add the UI object's element to the container div (box)
  3191. // Having an element is not required
  3192. if (typeof component.el === 'function' && component.el()) {
  3193. var childNodes = this.contentEl().children;
  3194. var refNode = childNodes[index] || null;
  3195. this.contentEl().insertBefore(component.el(), refNode);
  3196. }
  3197. // Return so it can stored on parent object if desired.
  3198. return component;
  3199. };
  3200. /**
  3201. * Remove a child `Component` from this `Component`s list of children. Also removes
  3202. * the child `Component`s element from this `Component`s element.
  3203. *
  3204. * @param {Component} component
  3205. * The child `Component` to remove.
  3206. */
  3207. Component.prototype.removeChild = function removeChild(component) {
  3208. if (typeof component === 'string') {
  3209. component = this.getChild(component);
  3210. }
  3211. if (!component || !this.children_) {
  3212. return;
  3213. }
  3214. var childFound = false;
  3215. for (var i = this.children_.length - 1; i >= 0; i--) {
  3216. if (this.children_[i] === component) {
  3217. childFound = true;
  3218. this.children_.splice(i, 1);
  3219. break;
  3220. }
  3221. }
  3222. if (!childFound) {
  3223. return;
  3224. }
  3225. this.childIndex_[component.id()] = null;
  3226. this.childNameIndex_[component.name()] = null;
  3227. var compEl = component.el();
  3228. if (compEl && compEl.parentNode === this.contentEl()) {
  3229. this.contentEl().removeChild(component.el());
  3230. }
  3231. };
  3232. /**
  3233. * Add and initialize default child `Component`s based upon options.
  3234. */
  3235. Component.prototype.initChildren = function initChildren() {
  3236. var _this = this;
  3237. var children = this.options_.children;
  3238. if (children) {
  3239. // `this` is `parent`
  3240. var parentOptions = this.options_;
  3241. var handleAdd = function handleAdd(child) {
  3242. var name = child.name;
  3243. var opts = child.opts;
  3244. // Allow options for children to be set at the parent options
  3245. // e.g. videojs(id, { controlBar: false });
  3246. // instead of videojs(id, { children: { controlBar: false });
  3247. if (parentOptions[name] !== undefined) {
  3248. opts = parentOptions[name];
  3249. }
  3250. // Allow for disabling default components
  3251. // e.g. options['children']['posterImage'] = false
  3252. if (opts === false) {
  3253. return;
  3254. }
  3255. // Allow options to be passed as a simple boolean if no configuration
  3256. // is necessary.
  3257. if (opts === true) {
  3258. opts = {};
  3259. }
  3260. // We also want to pass the original player options
  3261. // to each component as well so they don't need to
  3262. // reach back into the player for options later.
  3263. opts.playerOptions = _this.options_.playerOptions;
  3264. // Create and add the child component.
  3265. // Add a direct reference to the child by name on the parent instance.
  3266. // If two of the same component are used, different names should be supplied
  3267. // for each
  3268. var newChild = _this.addChild(name, opts);
  3269. if (newChild) {
  3270. _this[name] = newChild;
  3271. }
  3272. };
  3273. // Allow for an array of children details to passed in the options
  3274. var workingChildren = void 0;
  3275. var Tech = Component.getComponent('Tech');
  3276. if (Array.isArray(children)) {
  3277. workingChildren = children;
  3278. } else {
  3279. workingChildren = Object.keys(children);
  3280. }
  3281. workingChildren
  3282. // children that are in this.options_ but also in workingChildren would
  3283. // give us extra children we do not want. So, we want to filter them out.
  3284. .concat(Object.keys(this.options_).filter(function (child) {
  3285. return !workingChildren.some(function (wchild) {
  3286. if (typeof wchild === 'string') {
  3287. return child === wchild;
  3288. }
  3289. return child === wchild.name;
  3290. });
  3291. })).map(function (child) {
  3292. var name = void 0;
  3293. var opts = void 0;
  3294. if (typeof child === 'string') {
  3295. name = child;
  3296. opts = children[name] || _this.options_[name] || {};
  3297. } else {
  3298. name = child.name;
  3299. opts = child;
  3300. }
  3301. return { name: name, opts: opts };
  3302. }).filter(function (child) {
  3303. // we have to make sure that child.name isn't in the techOrder since
  3304. // techs are registerd as Components but can't aren't compatible
  3305. // See https://github.com/videojs/video.js/issues/2772
  3306. var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
  3307. return c && !Tech.isTech(c);
  3308. }).forEach(handleAdd);
  3309. }
  3310. };
  3311. /**
  3312. * Builds the default DOM class name. Should be overriden by sub-components.
  3313. *
  3314. * @return {string}
  3315. * The DOM class name for this object.
  3316. *
  3317. * @abstract
  3318. */
  3319. Component.prototype.buildCSSClass = function buildCSSClass() {
  3320. // Child classes can include a function that does:
  3321. // return 'CLASS NAME' + this._super();
  3322. return '';
  3323. };
  3324. /**
  3325. * Bind a listener to the component's ready state.
  3326. * Different from event listeners in that if the ready event has already happened
  3327. * it will trigger the function immediately.
  3328. *
  3329. * @return {Component}
  3330. * Returns itself; method can be chained.
  3331. */
  3332. Component.prototype.ready = function ready(fn) {
  3333. var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  3334. if (!fn) {
  3335. return;
  3336. }
  3337. if (!this.isReady_) {
  3338. this.readyQueue_ = this.readyQueue_ || [];
  3339. this.readyQueue_.push(fn);
  3340. return;
  3341. }
  3342. if (sync) {
  3343. fn.call(this);
  3344. } else {
  3345. // Call the function asynchronously by default for consistency
  3346. this.setTimeout(fn, 1);
  3347. }
  3348. };
  3349. /**
  3350. * Trigger all the ready listeners for this `Component`.
  3351. *
  3352. * @fires Component#ready
  3353. */
  3354. Component.prototype.triggerReady = function triggerReady() {
  3355. this.isReady_ = true;
  3356. // Ensure ready is triggered asynchronously
  3357. this.setTimeout(function () {
  3358. var readyQueue = this.readyQueue_;
  3359. // Reset Ready Queue
  3360. this.readyQueue_ = [];
  3361. if (readyQueue && readyQueue.length > 0) {
  3362. readyQueue.forEach(function (fn) {
  3363. fn.call(this);
  3364. }, this);
  3365. }
  3366. // Allow for using event listeners also
  3367. /**
  3368. * Triggered when a `Component` is ready.
  3369. *
  3370. * @event Component#ready
  3371. * @type {EventTarget~Event}
  3372. */
  3373. this.trigger('ready');
  3374. }, 1);
  3375. };
  3376. /**
  3377. * Find a single DOM element matching a `selector`. This can be within the `Component`s
  3378. * `contentEl()` or another custom context.
  3379. *
  3380. * @param {string} selector
  3381. * A valid CSS selector, which will be passed to `querySelector`.
  3382. *
  3383. * @param {Element|string} [context=this.contentEl()]
  3384. * A DOM element within which to query. Can also be a selector string in
  3385. * which case the first matching element will get used as context. If
  3386. * missing `this.contentEl()` gets used. If `this.contentEl()` returns
  3387. * nothing it falls back to `document`.
  3388. *
  3389. * @return {Element|null}
  3390. * the dom element that was found, or null
  3391. *
  3392. * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
  3393. */
  3394. Component.prototype.$ = function $$$1(selector, context) {
  3395. return $(selector, context || this.contentEl());
  3396. };
  3397. /**
  3398. * Finds all DOM element matching a `selector`. This can be within the `Component`s
  3399. * `contentEl()` or another custom context.
  3400. *
  3401. * @param {string} selector
  3402. * A valid CSS selector, which will be passed to `querySelectorAll`.
  3403. *
  3404. * @param {Element|string} [context=this.contentEl()]
  3405. * A DOM element within which to query. Can also be a selector string in
  3406. * which case the first matching element will get used as context. If
  3407. * missing `this.contentEl()` gets used. If `this.contentEl()` returns
  3408. * nothing it falls back to `document`.
  3409. *
  3410. * @return {NodeList}
  3411. * a list of dom elements that were found
  3412. *
  3413. * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
  3414. */
  3415. Component.prototype.$$ = function $$$$1(selector, context) {
  3416. return $$(selector, context || this.contentEl());
  3417. };
  3418. /**
  3419. * Check if a component's element has a CSS class name.
  3420. *
  3421. * @param {string} classToCheck
  3422. * CSS class name to check.
  3423. *
  3424. * @return {boolean}
  3425. * - True if the `Component` has the class.
  3426. * - False if the `Component` does not have the class`
  3427. */
  3428. Component.prototype.hasClass = function hasClass$$1(classToCheck) {
  3429. return hasClass(this.el_, classToCheck);
  3430. };
  3431. /**
  3432. * Add a CSS class name to the `Component`s element.
  3433. *
  3434. * @param {string} classToAdd
  3435. * CSS class name to add
  3436. */
  3437. Component.prototype.addClass = function addClass$$1(classToAdd) {
  3438. addClass(this.el_, classToAdd);
  3439. };
  3440. /**
  3441. * Remove a CSS class name from the `Component`s element.
  3442. *
  3443. * @param {string} classToRemove
  3444. * CSS class name to remove
  3445. */
  3446. Component.prototype.removeClass = function removeClass$$1(classToRemove) {
  3447. removeClass(this.el_, classToRemove);
  3448. };
  3449. /**
  3450. * Add or remove a CSS class name from the component's element.
  3451. * - `classToToggle` gets added when {@link Component#hasClass} would return false.
  3452. * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
  3453. *
  3454. * @param {string} classToToggle
  3455. * The class to add or remove based on (@link Component#hasClass}
  3456. *
  3457. * @param {boolean|Dom~predicate} [predicate]
  3458. * An {@link Dom~predicate} function or a boolean
  3459. */
  3460. Component.prototype.toggleClass = function toggleClass$$1(classToToggle, predicate) {
  3461. toggleClass(this.el_, classToToggle, predicate);
  3462. };
  3463. /**
  3464. * Show the `Component`s element if it is hidden by removing the
  3465. * 'vjs-hidden' class name from it.
  3466. */
  3467. Component.prototype.show = function show() {
  3468. this.removeClass('vjs-hidden');
  3469. };
  3470. /**
  3471. * Hide the `Component`s element if it is currently showing by adding the
  3472. * 'vjs-hidden` class name to it.
  3473. */
  3474. Component.prototype.hide = function hide() {
  3475. this.addClass('vjs-hidden');
  3476. };
  3477. /**
  3478. * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
  3479. * class name to it. Used during fadeIn/fadeOut.
  3480. *
  3481. * @private
  3482. */
  3483. Component.prototype.lockShowing = function lockShowing() {
  3484. this.addClass('vjs-lock-showing');
  3485. };
  3486. /**
  3487. * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
  3488. * class name from it. Used during fadeIn/fadeOut.
  3489. *
  3490. * @private
  3491. */
  3492. Component.prototype.unlockShowing = function unlockShowing() {
  3493. this.removeClass('vjs-lock-showing');
  3494. };
  3495. /**
  3496. * Get the value of an attribute on the `Component`s element.
  3497. *
  3498. * @param {string} attribute
  3499. * Name of the attribute to get the value from.
  3500. *
  3501. * @return {string|null}
  3502. * - The value of the attribute that was asked for.
  3503. * - Can be an empty string on some browsers if the attribute does not exist
  3504. * or has no value
  3505. * - Most browsers will return null if the attibute does not exist or has
  3506. * no value.
  3507. *
  3508. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
  3509. */
  3510. Component.prototype.getAttribute = function getAttribute$$1(attribute) {
  3511. return getAttribute(this.el_, attribute);
  3512. };
  3513. /**
  3514. * Set the value of an attribute on the `Component`'s element
  3515. *
  3516. * @param {string} attribute
  3517. * Name of the attribute to set.
  3518. *
  3519. * @param {string} value
  3520. * Value to set the attribute to.
  3521. *
  3522. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
  3523. */
  3524. Component.prototype.setAttribute = function setAttribute$$1(attribute, value) {
  3525. setAttribute(this.el_, attribute, value);
  3526. };
  3527. /**
  3528. * Remove an attribute from the `Component`s element.
  3529. *
  3530. * @param {string} attribute
  3531. * Name of the attribute to remove.
  3532. *
  3533. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
  3534. */
  3535. Component.prototype.removeAttribute = function removeAttribute$$1(attribute) {
  3536. removeAttribute(this.el_, attribute);
  3537. };
  3538. /**
  3539. * Get or set the width of the component based upon the CSS styles.
  3540. * See {@link Component#dimension} for more detailed information.
  3541. *
  3542. * @param {number|string} [num]
  3543. * The width that you want to set postfixed with '%', 'px' or nothing.
  3544. *
  3545. * @param {boolean} [skipListeners]
  3546. * Skip the componentresize event trigger
  3547. *
  3548. * @return {number|string}
  3549. * The width when getting, zero if there is no width. Can be a string
  3550. * postpixed with '%' or 'px'.
  3551. */
  3552. Component.prototype.width = function width(num, skipListeners) {
  3553. return this.dimension('width', num, skipListeners);
  3554. };
  3555. /**
  3556. * Get or set the height of the component based upon the CSS styles.
  3557. * See {@link Component#dimension} for more detailed information.
  3558. *
  3559. * @param {number|string} [num]
  3560. * The height that you want to set postfixed with '%', 'px' or nothing.
  3561. *
  3562. * @param {boolean} [skipListeners]
  3563. * Skip the componentresize event trigger
  3564. *
  3565. * @return {number|string}
  3566. * The width when getting, zero if there is no width. Can be a string
  3567. * postpixed with '%' or 'px'.
  3568. */
  3569. Component.prototype.height = function height(num, skipListeners) {
  3570. return this.dimension('height', num, skipListeners);
  3571. };
  3572. /**
  3573. * Set both the width and height of the `Component` element at the same time.
  3574. *
  3575. * @param {number|string} width
  3576. * Width to set the `Component`s element to.
  3577. *
  3578. * @param {number|string} height
  3579. * Height to set the `Component`s element to.
  3580. */
  3581. Component.prototype.dimensions = function dimensions(width, height) {
  3582. // Skip componentresize listeners on width for optimization
  3583. this.width(width, true);
  3584. this.height(height);
  3585. };
  3586. /**
  3587. * Get or set width or height of the `Component` element. This is the shared code
  3588. * for the {@link Component#width} and {@link Component#height}.
  3589. *
  3590. * Things to know:
  3591. * - If the width or height in an number this will return the number postfixed with 'px'.
  3592. * - If the width/height is a percent this will return the percent postfixed with '%'
  3593. * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
  3594. * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
  3595. * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
  3596. * for more information
  3597. * - If you want the computed style of the component, use {@link Component#currentWidth}
  3598. * and {@link {Component#currentHeight}
  3599. *
  3600. * @fires Component#componentresize
  3601. *
  3602. * @param {string} widthOrHeight
  3603. 8 'width' or 'height'
  3604. *
  3605. * @param {number|string} [num]
  3606. 8 New dimension
  3607. *
  3608. * @param {boolean} [skipListeners]
  3609. * Skip componentresize event trigger
  3610. *
  3611. * @return {number}
  3612. * The dimension when getting or 0 if unset
  3613. */
  3614. Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
  3615. if (num !== undefined) {
  3616. // Set to zero if null or literally NaN (NaN !== NaN)
  3617. if (num === null || num !== num) {
  3618. num = 0;
  3619. }
  3620. // Check if using css width/height (% or px) and adjust
  3621. if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
  3622. this.el_.style[widthOrHeight] = num;
  3623. } else if (num === 'auto') {
  3624. this.el_.style[widthOrHeight] = '';
  3625. } else {
  3626. this.el_.style[widthOrHeight] = num + 'px';
  3627. }
  3628. // skipListeners allows us to avoid triggering the resize event when setting both width and height
  3629. if (!skipListeners) {
  3630. /**
  3631. * Triggered when a component is resized.
  3632. *
  3633. * @event Component#componentresize
  3634. * @type {EventTarget~Event}
  3635. */
  3636. this.trigger('componentresize');
  3637. }
  3638. return;
  3639. }
  3640. // Not setting a value, so getting it
  3641. // Make sure element exists
  3642. if (!this.el_) {
  3643. return 0;
  3644. }
  3645. // Get dimension value from style
  3646. var val = this.el_.style[widthOrHeight];
  3647. var pxIndex = val.indexOf('px');
  3648. if (pxIndex !== -1) {
  3649. // Return the pixel value with no 'px'
  3650. return parseInt(val.slice(0, pxIndex), 10);
  3651. }
  3652. // No px so using % or no style was set, so falling back to offsetWidth/height
  3653. // If component has display:none, offset will return 0
  3654. // TODO: handle display:none and no dimension style using px
  3655. return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
  3656. };
  3657. /**
  3658. * Get the computed width or the height of the component's element.
  3659. *
  3660. * Uses `window.getComputedStyle`.
  3661. *
  3662. * @param {string} widthOrHeight
  3663. * A string containing 'width' or 'height'. Whichever one you want to get.
  3664. *
  3665. * @return {number}
  3666. * The dimension that gets asked for or 0 if nothing was set
  3667. * for that dimension.
  3668. */
  3669. Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
  3670. var computedWidthOrHeight = 0;
  3671. if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
  3672. throw new Error('currentDimension only accepts width or height value');
  3673. }
  3674. if (typeof window_1.getComputedStyle === 'function') {
  3675. var computedStyle = window_1.getComputedStyle(this.el_);
  3676. computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
  3677. }
  3678. // remove 'px' from variable and parse as integer
  3679. computedWidthOrHeight = parseFloat(computedWidthOrHeight);
  3680. // if the computed value is still 0, it's possible that the browser is lying
  3681. // and we want to check the offset values.
  3682. // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
  3683. if (computedWidthOrHeight === 0) {
  3684. var rule = 'offset' + toTitleCase(widthOrHeight);
  3685. computedWidthOrHeight = this.el_[rule];
  3686. }
  3687. return computedWidthOrHeight;
  3688. };
  3689. /**
  3690. * An object that contains width and height values of the `Component`s
  3691. * computed style. Uses `window.getComputedStyle`.
  3692. *
  3693. * @typedef {Object} Component~DimensionObject
  3694. *
  3695. * @property {number} width
  3696. * The width of the `Component`s computed style.
  3697. *
  3698. * @property {number} height
  3699. * The height of the `Component`s computed style.
  3700. */
  3701. /**
  3702. * Get an object that contains computed width and height values of the
  3703. * component's element.
  3704. *
  3705. * Uses `window.getComputedStyle`.
  3706. *
  3707. * @return {Component~DimensionObject}
  3708. * The computed dimensions of the component's element.
  3709. */
  3710. Component.prototype.currentDimensions = function currentDimensions() {
  3711. return {
  3712. width: this.currentDimension('width'),
  3713. height: this.currentDimension('height')
  3714. };
  3715. };
  3716. /**
  3717. * Get the computed width of the component's element.
  3718. *
  3719. * Uses `window.getComputedStyle`.
  3720. *
  3721. * @return {number}
  3722. * The computed width of the component's element.
  3723. */
  3724. Component.prototype.currentWidth = function currentWidth() {
  3725. return this.currentDimension('width');
  3726. };
  3727. /**
  3728. * Get the computed height of the component's element.
  3729. *
  3730. * Uses `window.getComputedStyle`.
  3731. *
  3732. * @return {number}
  3733. * The computed height of the component's element.
  3734. */
  3735. Component.prototype.currentHeight = function currentHeight() {
  3736. return this.currentDimension('height');
  3737. };
  3738. /**
  3739. * Set the focus to this component
  3740. */
  3741. Component.prototype.focus = function focus() {
  3742. this.el_.focus();
  3743. };
  3744. /**
  3745. * Remove the focus from this component
  3746. */
  3747. Component.prototype.blur = function blur() {
  3748. this.el_.blur();
  3749. };
  3750. /**
  3751. * Emit a 'tap' events when touch event support gets detected. This gets used to
  3752. * support toggling the controls through a tap on the video. They get enabled
  3753. * because every sub-component would have extra overhead otherwise.
  3754. *
  3755. * @private
  3756. * @fires Component#tap
  3757. * @listens Component#touchstart
  3758. * @listens Component#touchmove
  3759. * @listens Component#touchleave
  3760. * @listens Component#touchcancel
  3761. * @listens Component#touchend
  3762. */
  3763. Component.prototype.emitTapEvents = function emitTapEvents() {
  3764. // Track the start time so we can determine how long the touch lasted
  3765. var touchStart = 0;
  3766. var firstTouch = null;
  3767. // Maximum movement allowed during a touch event to still be considered a tap
  3768. // Other popular libs use anywhere from 2 (hammer.js) to 15,
  3769. // so 10 seems like a nice, round number.
  3770. var tapMovementThreshold = 10;
  3771. // The maximum length a touch can be while still being considered a tap
  3772. var touchTimeThreshold = 200;
  3773. var couldBeTap = void 0;
  3774. this.on('touchstart', function (event) {
  3775. // If more than one finger, don't consider treating this as a click
  3776. if (event.touches.length === 1) {
  3777. // Copy pageX/pageY from the object
  3778. firstTouch = {
  3779. pageX: event.touches[0].pageX,
  3780. pageY: event.touches[0].pageY
  3781. };
  3782. // Record start time so we can detect a tap vs. "touch and hold"
  3783. touchStart = new Date().getTime();
  3784. // Reset couldBeTap tracking
  3785. couldBeTap = true;
  3786. }
  3787. });
  3788. this.on('touchmove', function (event) {
  3789. // If more than one finger, don't consider treating this as a click
  3790. if (event.touches.length > 1) {
  3791. couldBeTap = false;
  3792. } else if (firstTouch) {
  3793. // Some devices will throw touchmoves for all but the slightest of taps.
  3794. // So, if we moved only a small distance, this could still be a tap
  3795. var xdiff = event.touches[0].pageX - firstTouch.pageX;
  3796. var ydiff = event.touches[0].pageY - firstTouch.pageY;
  3797. var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
  3798. if (touchDistance > tapMovementThreshold) {
  3799. couldBeTap = false;
  3800. }
  3801. }
  3802. });
  3803. var noTap = function noTap() {
  3804. couldBeTap = false;
  3805. };
  3806. // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
  3807. this.on('touchleave', noTap);
  3808. this.on('touchcancel', noTap);
  3809. // When the touch ends, measure how long it took and trigger the appropriate
  3810. // event
  3811. this.on('touchend', function (event) {
  3812. firstTouch = null;
  3813. // Proceed only if the touchmove/leave/cancel event didn't happen
  3814. if (couldBeTap === true) {
  3815. // Measure how long the touch lasted
  3816. var touchTime = new Date().getTime() - touchStart;
  3817. // Make sure the touch was less than the threshold to be considered a tap
  3818. if (touchTime < touchTimeThreshold) {
  3819. // Don't let browser turn this into a click
  3820. event.preventDefault();
  3821. /**
  3822. * Triggered when a `Component` is tapped.
  3823. *
  3824. * @event Component#tap
  3825. * @type {EventTarget~Event}
  3826. */
  3827. this.trigger('tap');
  3828. // It may be good to copy the touchend event object and change the
  3829. // type to tap, if the other event properties aren't exact after
  3830. // Events.fixEvent runs (e.g. event.target)
  3831. }
  3832. }
  3833. });
  3834. };
  3835. /**
  3836. * This function reports user activity whenever touch events happen. This can get
  3837. * turned off by any sub-components that wants touch events to act another way.
  3838. *
  3839. * Report user touch activity when touch events occur. User activity gets used to
  3840. * determine when controls should show/hide. It is simple when it comes to mouse
  3841. * events, because any mouse event should show the controls. So we capture mouse
  3842. * events that bubble up to the player and report activity when that happens.
  3843. * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
  3844. * controls. So touch events can't help us at the player level either.
  3845. *
  3846. * User activity gets checked asynchronously. So what could happen is a tap event
  3847. * on the video turns the controls off. Then the `touchend` event bubbles up to
  3848. * the player. Which, if it reported user activity, would turn the controls right
  3849. * back on. We also don't want to completely block touch events from bubbling up.
  3850. * Furthermore a `touchmove` event and anything other than a tap, should not turn
  3851. * controls back on.
  3852. *
  3853. * @listens Component#touchstart
  3854. * @listens Component#touchmove
  3855. * @listens Component#touchend
  3856. * @listens Component#touchcancel
  3857. */
  3858. Component.prototype.enableTouchActivity = function enableTouchActivity() {
  3859. // Don't continue if the root player doesn't support reporting user activity
  3860. if (!this.player() || !this.player().reportUserActivity) {
  3861. return;
  3862. }
  3863. // listener for reporting that the user is active
  3864. var report = bind(this.player(), this.player().reportUserActivity);
  3865. var touchHolding = void 0;
  3866. this.on('touchstart', function () {
  3867. report();
  3868. // For as long as the they are touching the device or have their mouse down,
  3869. // we consider them active even if they're not moving their finger or mouse.
  3870. // So we want to continue to update that they are active
  3871. this.clearInterval(touchHolding);
  3872. // report at the same interval as activityCheck
  3873. touchHolding = this.setInterval(report, 250);
  3874. });
  3875. var touchEnd = function touchEnd(event) {
  3876. report();
  3877. // stop the interval that maintains activity if the touch is holding
  3878. this.clearInterval(touchHolding);
  3879. };
  3880. this.on('touchmove', report);
  3881. this.on('touchend', touchEnd);
  3882. this.on('touchcancel', touchEnd);
  3883. };
  3884. /**
  3885. * A callback that has no parameters and is bound into `Component`s context.
  3886. *
  3887. * @callback Component~GenericCallback
  3888. * @this Component
  3889. */
  3890. /**
  3891. * Creates a function that runs after an `x` millisecond timeout. This function is a
  3892. * wrapper around `window.setTimeout`. There are a few reasons to use this one
  3893. * instead though:
  3894. * 1. It gets cleared via {@link Component#clearTimeout} when
  3895. * {@link Component#dispose} gets called.
  3896. * 2. The function callback will gets turned into a {@link Component~GenericCallback}
  3897. *
  3898. * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
  3899. * will cause its dispose listener not to get cleaned up! Please use
  3900. * {@link Component#clearTimeout} or {@link Component#dispose} instead.
  3901. *
  3902. * @param {Component~GenericCallback} fn
  3903. * The function that will be run after `timeout`.
  3904. *
  3905. * @param {number} timeout
  3906. * Timeout in milliseconds to delay before executing the specified function.
  3907. *
  3908. * @return {number}
  3909. * Returns a timeout ID that gets used to identify the timeout. It can also
  3910. * get used in {@link Component#clearTimeout} to clear the timeout that
  3911. * was set.
  3912. *
  3913. * @listens Component#dispose
  3914. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
  3915. */
  3916. Component.prototype.setTimeout = function setTimeout(fn, timeout) {
  3917. var _this2 = this;
  3918. // declare as variables so they are properly available in timeout function
  3919. // eslint-disable-next-line
  3920. var timeoutId, disposeFn;
  3921. fn = bind(this, fn);
  3922. timeoutId = window_1.setTimeout(function () {
  3923. _this2.off('dispose', disposeFn);
  3924. fn();
  3925. }, timeout);
  3926. disposeFn = function disposeFn() {
  3927. return _this2.clearTimeout(timeoutId);
  3928. };
  3929. disposeFn.guid = 'vjs-timeout-' + timeoutId;
  3930. this.on('dispose', disposeFn);
  3931. return timeoutId;
  3932. };
  3933. /**
  3934. * Clears a timeout that gets created via `window.setTimeout` or
  3935. * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
  3936. * use this function instead of `window.clearTimout`. If you don't your dispose
  3937. * listener will not get cleaned up until {@link Component#dispose}!
  3938. *
  3939. * @param {number} timeoutId
  3940. * The id of the timeout to clear. The return value of
  3941. * {@link Component#setTimeout} or `window.setTimeout`.
  3942. *
  3943. * @return {number}
  3944. * Returns the timeout id that was cleared.
  3945. *
  3946. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
  3947. */
  3948. Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
  3949. window_1.clearTimeout(timeoutId);
  3950. var disposeFn = function disposeFn() {};
  3951. disposeFn.guid = 'vjs-timeout-' + timeoutId;
  3952. this.off('dispose', disposeFn);
  3953. return timeoutId;
  3954. };
  3955. /**
  3956. * Creates a function that gets run every `x` milliseconds. This function is a wrapper
  3957. * around `window.setInterval`. There are a few reasons to use this one instead though.
  3958. * 1. It gets cleared via {@link Component#clearInterval} when
  3959. * {@link Component#dispose} gets called.
  3960. * 2. The function callback will be a {@link Component~GenericCallback}
  3961. *
  3962. * @param {Component~GenericCallback} fn
  3963. * The function to run every `x` seconds.
  3964. *
  3965. * @param {number} interval
  3966. * Execute the specified function every `x` milliseconds.
  3967. *
  3968. * @return {number}
  3969. * Returns an id that can be used to identify the interval. It can also be be used in
  3970. * {@link Component#clearInterval} to clear the interval.
  3971. *
  3972. * @listens Component#dispose
  3973. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
  3974. */
  3975. Component.prototype.setInterval = function setInterval(fn, interval) {
  3976. var _this3 = this;
  3977. fn = bind(this, fn);
  3978. var intervalId = window_1.setInterval(fn, interval);
  3979. var disposeFn = function disposeFn() {
  3980. return _this3.clearInterval(intervalId);
  3981. };
  3982. disposeFn.guid = 'vjs-interval-' + intervalId;
  3983. this.on('dispose', disposeFn);
  3984. return intervalId;
  3985. };
  3986. /**
  3987. * Clears an interval that gets created via `window.setInterval` or
  3988. * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
  3989. * use this function instead of `window.clearInterval`. If you don't your dispose
  3990. * listener will not get cleaned up until {@link Component#dispose}!
  3991. *
  3992. * @param {number} intervalId
  3993. * The id of the interval to clear. The return value of
  3994. * {@link Component#setInterval} or `window.setInterval`.
  3995. *
  3996. * @return {number}
  3997. * Returns the interval id that was cleared.
  3998. *
  3999. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
  4000. */
  4001. Component.prototype.clearInterval = function clearInterval(intervalId) {
  4002. window_1.clearInterval(intervalId);
  4003. var disposeFn = function disposeFn() {};
  4004. disposeFn.guid = 'vjs-interval-' + intervalId;
  4005. this.off('dispose', disposeFn);
  4006. return intervalId;
  4007. };
  4008. /**
  4009. * Queues up a callback to be passed to requestAnimationFrame (rAF), but
  4010. * with a few extra bonuses:
  4011. *
  4012. * - Supports browsers that do not support rAF by falling back to
  4013. * {@link Component#setTimeout}.
  4014. *
  4015. * - The callback is turned into a {@link Component~GenericCallback} (i.e.
  4016. * bound to the component).
  4017. *
  4018. * - Automatic cancellation of the rAF callback is handled if the component
  4019. * is disposed before it is called.
  4020. *
  4021. * @param {Component~GenericCallback} fn
  4022. * A function that will be bound to this component and executed just
  4023. * before the browser's next repaint.
  4024. *
  4025. * @return {number}
  4026. * Returns an rAF ID that gets used to identify the timeout. It can
  4027. * also be used in {@link Component#cancelAnimationFrame} to cancel
  4028. * the animation frame callback.
  4029. *
  4030. * @listens Component#dispose
  4031. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
  4032. */
  4033. Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) {
  4034. var _this4 = this;
  4035. // declare as variables so they are properly available in rAF function
  4036. // eslint-disable-next-line
  4037. var id, disposeFn;
  4038. if (this.supportsRaf_) {
  4039. fn = bind(this, fn);
  4040. id = window_1.requestAnimationFrame(function () {
  4041. _this4.off('dispose', disposeFn);
  4042. fn();
  4043. });
  4044. disposeFn = function disposeFn() {
  4045. return _this4.cancelAnimationFrame(id);
  4046. };
  4047. disposeFn.guid = 'vjs-raf-' + id;
  4048. this.on('dispose', disposeFn);
  4049. return id;
  4050. }
  4051. // Fall back to using a timer.
  4052. return this.setTimeout(fn, 1000 / 60);
  4053. };
  4054. /**
  4055. * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
  4056. * (rAF).
  4057. *
  4058. * If you queue an rAF callback via {@link Component#requestAnimationFrame},
  4059. * use this function instead of `window.cancelAnimationFrame`. If you don't,
  4060. * your dispose listener will not get cleaned up until {@link Component#dispose}!
  4061. *
  4062. * @param {number} id
  4063. * The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
  4064. *
  4065. * @return {number}
  4066. * Returns the rAF ID that was cleared.
  4067. *
  4068. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
  4069. */
  4070. Component.prototype.cancelAnimationFrame = function cancelAnimationFrame(id) {
  4071. if (this.supportsRaf_) {
  4072. window_1.cancelAnimationFrame(id);
  4073. var disposeFn = function disposeFn() {};
  4074. disposeFn.guid = 'vjs-raf-' + id;
  4075. this.off('dispose', disposeFn);
  4076. return id;
  4077. }
  4078. // Fall back to using a timer.
  4079. return this.clearTimeout(id);
  4080. };
  4081. /**
  4082. * Register a `Component` with `videojs` given the name and the component.
  4083. *
  4084. * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
  4085. * should be registered using {@link Tech.registerTech} or
  4086. * {@link videojs:videojs.registerTech}.
  4087. *
  4088. * > NOTE: This function can also be seen on videojs as
  4089. * {@link videojs:videojs.registerComponent}.
  4090. *
  4091. * @param {string} name
  4092. * The name of the `Component` to register.
  4093. *
  4094. * @param {Component} ComponentToRegister
  4095. * The `Component` class to register.
  4096. *
  4097. * @return {Component}
  4098. * The `Component` that was registered.
  4099. */
  4100. Component.registerComponent = function registerComponent(name, ComponentToRegister) {
  4101. if (typeof name !== 'string' || !name) {
  4102. throw new Error('Illegal component name, "' + name + '"; must be a non-empty string.');
  4103. }
  4104. var Tech = Component.getComponent('Tech');
  4105. // We need to make sure this check is only done if Tech has been registered.
  4106. var isTech = Tech && Tech.isTech(ComponentToRegister);
  4107. var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
  4108. if (isTech || !isComp) {
  4109. var reason = void 0;
  4110. if (isTech) {
  4111. reason = 'techs must be registered using Tech.registerTech()';
  4112. } else {
  4113. reason = 'must be a Component subclass';
  4114. }
  4115. throw new Error('Illegal component, "' + name + '"; ' + reason + '.');
  4116. }
  4117. name = toTitleCase(name);
  4118. if (!Component.components_) {
  4119. Component.components_ = {};
  4120. }
  4121. var Player = Component.getComponent('Player');
  4122. if (name === 'Player' && Player && Player.players) {
  4123. var players = Player.players;
  4124. var playerNames = Object.keys(players);
  4125. // If we have players that were disposed, then their name will still be
  4126. // in Players.players. So, we must loop through and verify that the value
  4127. // for each item is not null. This allows registration of the Player component
  4128. // after all players have been disposed or before any were created.
  4129. if (players && playerNames.length > 0 && playerNames.map(function (pname) {
  4130. return players[pname];
  4131. }).every(Boolean)) {
  4132. throw new Error('Can not register Player component after player has been created.');
  4133. }
  4134. }
  4135. Component.components_[name] = ComponentToRegister;
  4136. return ComponentToRegister;
  4137. };
  4138. /**
  4139. * Get a `Component` based on the name it was registered with.
  4140. *
  4141. * @param {string} name
  4142. * The Name of the component to get.
  4143. *
  4144. * @return {Component}
  4145. * The `Component` that got registered under the given name.
  4146. *
  4147. * @deprecated In `videojs` 6 this will not return `Component`s that were not
  4148. * registered using {@link Component.registerComponent}. Currently we
  4149. * check the global `videojs` object for a `Component` name and
  4150. * return that if it exists.
  4151. */
  4152. Component.getComponent = function getComponent(name) {
  4153. if (!name) {
  4154. return;
  4155. }
  4156. name = toTitleCase(name);
  4157. if (Component.components_ && Component.components_[name]) {
  4158. return Component.components_[name];
  4159. }
  4160. };
  4161. return Component;
  4162. }();
  4163. /**
  4164. * Whether or not this component supports `requestAnimationFrame`.
  4165. *
  4166. * This is exposed primarily for testing purposes.
  4167. *
  4168. * @private
  4169. * @type {Boolean}
  4170. */
  4171. Component.prototype.supportsRaf_ = typeof window_1.requestAnimationFrame === 'function' && typeof window_1.cancelAnimationFrame === 'function';
  4172. Component.registerComponent('Component', Component);
  4173. /**
  4174. * @file time-ranges.js
  4175. * @module time-ranges
  4176. */
  4177. /**
  4178. * Returns the time for the specified index at the start or end
  4179. * of a TimeRange object.
  4180. *
  4181. * @function time-ranges:indexFunction
  4182. *
  4183. * @param {number} [index=0]
  4184. * The range number to return the time for.
  4185. *
  4186. * @return {number}
  4187. * The time that offset at the specified index.
  4188. *
  4189. * @depricated index must be set to a value, in the future this will throw an error.
  4190. */
  4191. /**
  4192. * An object that contains ranges of time for various reasons.
  4193. *
  4194. * @typedef {Object} TimeRange
  4195. *
  4196. * @property {number} length
  4197. * The number of time ranges represented by this Object
  4198. *
  4199. * @property {time-ranges:indexFunction} start
  4200. * Returns the time offset at which a specified time range begins.
  4201. *
  4202. * @property {time-ranges:indexFunction} end
  4203. * Returns the time offset at which a specified time range ends.
  4204. *
  4205. * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
  4206. */
  4207. /**
  4208. * Check if any of the time ranges are over the maximum index.
  4209. *
  4210. * @param {string} fnName
  4211. * The function name to use for logging
  4212. *
  4213. * @param {number} index
  4214. * The index to check
  4215. *
  4216. * @param {number} maxIndex
  4217. * The maximum possible index
  4218. *
  4219. * @throws {Error} if the timeRanges provided are over the maxIndex
  4220. */
  4221. function rangeCheck(fnName, index, maxIndex) {
  4222. if (typeof index !== 'number' || index < 0 || index > maxIndex) {
  4223. throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is non-numeric or out of bounds (0-' + maxIndex + ').');
  4224. }
  4225. }
  4226. /**
  4227. * Get the time for the specified index at the start or end
  4228. * of a TimeRange object.
  4229. *
  4230. * @param {string} fnName
  4231. * The function name to use for logging
  4232. *
  4233. * @param {string} valueIndex
  4234. * The proprety that should be used to get the time. should be 'start' or 'end'
  4235. *
  4236. * @param {Array} ranges
  4237. * An array of time ranges
  4238. *
  4239. * @param {Array} [rangeIndex=0]
  4240. * The index to start the search at
  4241. *
  4242. * @return {number}
  4243. * The time that offset at the specified index.
  4244. *
  4245. *
  4246. * @depricated rangeIndex must be set to a value, in the future this will throw an error.
  4247. * @throws {Error} if rangeIndex is more than the length of ranges
  4248. */
  4249. function getRange(fnName, valueIndex, ranges, rangeIndex) {
  4250. rangeCheck(fnName, rangeIndex, ranges.length - 1);
  4251. return ranges[rangeIndex][valueIndex];
  4252. }
  4253. /**
  4254. * Create a time range object given ranges of time.
  4255. *
  4256. * @param {Array} [ranges]
  4257. * An array of time ranges.
  4258. */
  4259. function createTimeRangesObj(ranges) {
  4260. if (ranges === undefined || ranges.length === 0) {
  4261. return {
  4262. length: 0,
  4263. start: function start() {
  4264. throw new Error('This TimeRanges object is empty');
  4265. },
  4266. end: function end() {
  4267. throw new Error('This TimeRanges object is empty');
  4268. }
  4269. };
  4270. }
  4271. return {
  4272. length: ranges.length,
  4273. start: getRange.bind(null, 'start', 0, ranges),
  4274. end: getRange.bind(null, 'end', 1, ranges)
  4275. };
  4276. }
  4277. /**
  4278. * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
  4279. *
  4280. * @param {number|Array} start
  4281. * The start of a single range or an array of ranges
  4282. *
  4283. * @param {number} end
  4284. * The end of a single range.
  4285. *
  4286. * @private
  4287. */
  4288. function createTimeRanges(start, end) {
  4289. if (Array.isArray(start)) {
  4290. return createTimeRangesObj(start);
  4291. } else if (start === undefined || end === undefined) {
  4292. return createTimeRangesObj();
  4293. }
  4294. return createTimeRangesObj([[start, end]]);
  4295. }
  4296. /**
  4297. * @file buffer.js
  4298. * @module buffer
  4299. */
  4300. /**
  4301. * Compute the percentage of the media that has been buffered.
  4302. *
  4303. * @param {TimeRange} buffered
  4304. * The current `TimeRange` object representing buffered time ranges
  4305. *
  4306. * @param {number} duration
  4307. * Total duration of the media
  4308. *
  4309. * @return {number}
  4310. * Percent buffered of the total duration in decimal form.
  4311. */
  4312. function bufferedPercent(buffered, duration) {
  4313. var bufferedDuration = 0;
  4314. var start = void 0;
  4315. var end = void 0;
  4316. if (!duration) {
  4317. return 0;
  4318. }
  4319. if (!buffered || !buffered.length) {
  4320. buffered = createTimeRanges(0, 0);
  4321. }
  4322. for (var i = 0; i < buffered.length; i++) {
  4323. start = buffered.start(i);
  4324. end = buffered.end(i);
  4325. // buffered end can be bigger than duration by a very small fraction
  4326. if (end > duration) {
  4327. end = duration;
  4328. }
  4329. bufferedDuration += end - start;
  4330. }
  4331. return bufferedDuration / duration;
  4332. }
  4333. /**
  4334. * @file fullscreen-api.js
  4335. * @module fullscreen-api
  4336. * @private
  4337. */
  4338. /**
  4339. * Store the browser-specific methods for the fullscreen API.
  4340. *
  4341. * @type {Object}
  4342. * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
  4343. * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
  4344. */
  4345. var FullscreenApi = {};
  4346. // browser API methods
  4347. var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
  4348. // WebKit
  4349. ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
  4350. // Old WebKit (Safari 5.1)
  4351. ['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
  4352. // Mozilla
  4353. ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
  4354. // Microsoft
  4355. ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
  4356. var specApi = apiMap[0];
  4357. var browserApi = void 0;
  4358. // determine the supported set of functions
  4359. for (var i = 0; i < apiMap.length; i++) {
  4360. // check for exitFullscreen function
  4361. if (apiMap[i][1] in document_1) {
  4362. browserApi = apiMap[i];
  4363. break;
  4364. }
  4365. }
  4366. // map the browser API names to the spec API names
  4367. if (browserApi) {
  4368. for (var _i = 0; _i < browserApi.length; _i++) {
  4369. FullscreenApi[specApi[_i]] = browserApi[_i];
  4370. }
  4371. }
  4372. /**
  4373. * @file media-error.js
  4374. */
  4375. /**
  4376. * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
  4377. *
  4378. * @param {number|string|Object|MediaError} value
  4379. * This can be of multiple types:
  4380. * - number: should be a standard error code
  4381. * - string: an error message (the code will be 0)
  4382. * - Object: arbitrary properties
  4383. * - `MediaError` (native): used to populate a video.js `MediaError` object
  4384. * - `MediaError` (video.js): will return itself if it's already a
  4385. * video.js `MediaError` object.
  4386. *
  4387. * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
  4388. * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
  4389. *
  4390. * @class MediaError
  4391. */
  4392. function MediaError(value) {
  4393. // Allow redundant calls to this constructor to avoid having `instanceof`
  4394. // checks peppered around the code.
  4395. if (value instanceof MediaError) {
  4396. return value;
  4397. }
  4398. if (typeof value === 'number') {
  4399. this.code = value;
  4400. } else if (typeof value === 'string') {
  4401. // default code is zero, so this is a custom error
  4402. this.message = value;
  4403. } else if (isObject(value)) {
  4404. // We assign the `code` property manually because native `MediaError` objects
  4405. // do not expose it as an own/enumerable property of the object.
  4406. if (typeof value.code === 'number') {
  4407. this.code = value.code;
  4408. }
  4409. assign(this, value);
  4410. }
  4411. if (!this.message) {
  4412. this.message = MediaError.defaultMessages[this.code] || '';
  4413. }
  4414. }
  4415. /**
  4416. * The error code that refers two one of the defined `MediaError` types
  4417. *
  4418. * @type {Number}
  4419. */
  4420. MediaError.prototype.code = 0;
  4421. /**
  4422. * An optional message that to show with the error. Message is not part of the HTML5
  4423. * video spec but allows for more informative custom errors.
  4424. *
  4425. * @type {String}
  4426. */
  4427. MediaError.prototype.message = '';
  4428. /**
  4429. * An optional status code that can be set by plugins to allow even more detail about
  4430. * the error. For example a plugin might provide a specific HTTP status code and an
  4431. * error message for that code. Then when the plugin gets that error this class will
  4432. * know how to display an error message for it. This allows a custom message to show
  4433. * up on the `Player` error overlay.
  4434. *
  4435. * @type {Array}
  4436. */
  4437. MediaError.prototype.status = null;
  4438. /**
  4439. * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
  4440. * specification listed under {@link MediaError} for more information.
  4441. *
  4442. * @enum {array}
  4443. * @readonly
  4444. * @property {string} 0 - MEDIA_ERR_CUSTOM
  4445. * @property {string} 1 - MEDIA_ERR_CUSTOM
  4446. * @property {string} 2 - MEDIA_ERR_ABORTED
  4447. * @property {string} 3 - MEDIA_ERR_NETWORK
  4448. * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
  4449. * @property {string} 5 - MEDIA_ERR_ENCRYPTED
  4450. */
  4451. MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
  4452. /**
  4453. * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
  4454. *
  4455. * @type {Array}
  4456. * @constant
  4457. */
  4458. MediaError.defaultMessages = {
  4459. 1: 'You aborted the media playback',
  4460. 2: 'A network error caused the media download to fail part-way.',
  4461. 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
  4462. 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
  4463. 5: 'The media is encrypted and we do not have the keys to decrypt it.'
  4464. };
  4465. // Add types as properties on MediaError
  4466. // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
  4467. for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
  4468. MediaError[MediaError.errorTypes[errNum]] = errNum;
  4469. // values should be accessible on both the class and instance
  4470. MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
  4471. }
  4472. var tuple = SafeParseTuple;
  4473. function SafeParseTuple(obj, reviver) {
  4474. var json;
  4475. var error = null;
  4476. try {
  4477. json = JSON.parse(obj, reviver);
  4478. } catch (err) {
  4479. error = err;
  4480. }
  4481. return [error, json]
  4482. }
  4483. /**
  4484. * Returns whether an object is `Promise`-like (i.e. has a `then` method).
  4485. *
  4486. * @param {Object} value
  4487. * An object that may or may not be `Promise`-like.
  4488. *
  4489. * @return {Boolean}
  4490. * Whether or not the object is `Promise`-like.
  4491. */
  4492. function isPromise(value) {
  4493. return value !== undefined && value !== null && typeof value.then === 'function';
  4494. }
  4495. /**
  4496. * Silence a Promise-like object.
  4497. *
  4498. * This is useful for avoiding non-harmful, but potentially confusing "uncaught
  4499. * play promise" rejection error messages.
  4500. *
  4501. * @param {Object} value
  4502. * An object that may or may not be `Promise`-like.
  4503. */
  4504. function silencePromise(value) {
  4505. if (isPromise(value)) {
  4506. value.then(null, function (e) {});
  4507. }
  4508. }
  4509. /**
  4510. * @file text-track-list-converter.js Utilities for capturing text track state and
  4511. * re-creating tracks based on a capture.
  4512. *
  4513. * @module text-track-list-converter
  4514. */
  4515. /**
  4516. * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
  4517. * represents the {@link TextTrack}'s state.
  4518. *
  4519. * @param {TextTrack} track
  4520. * The text track to query.
  4521. *
  4522. * @return {Object}
  4523. * A serializable javascript representation of the TextTrack.
  4524. * @private
  4525. */
  4526. var trackToJson_ = function trackToJson_(track) {
  4527. var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
  4528. if (track[prop]) {
  4529. acc[prop] = track[prop];
  4530. }
  4531. return acc;
  4532. }, {
  4533. cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
  4534. return {
  4535. startTime: cue.startTime,
  4536. endTime: cue.endTime,
  4537. text: cue.text,
  4538. id: cue.id
  4539. };
  4540. })
  4541. });
  4542. return ret;
  4543. };
  4544. /**
  4545. * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
  4546. * state of all {@link TextTrack}s currently configured. The return array is compatible with
  4547. * {@link text-track-list-converter:jsonToTextTracks}.
  4548. *
  4549. * @param {Tech} tech
  4550. * The tech object to query
  4551. *
  4552. * @return {Array}
  4553. * A serializable javascript representation of the {@link Tech}s
  4554. * {@link TextTrackList}.
  4555. */
  4556. var textTracksToJson = function textTracksToJson(tech) {
  4557. var trackEls = tech.$$('track');
  4558. var trackObjs = Array.prototype.map.call(trackEls, function (t) {
  4559. return t.track;
  4560. });
  4561. var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
  4562. var json = trackToJson_(trackEl.track);
  4563. if (trackEl.src) {
  4564. json.src = trackEl.src;
  4565. }
  4566. return json;
  4567. });
  4568. return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
  4569. return trackObjs.indexOf(track) === -1;
  4570. }).map(trackToJson_));
  4571. };
  4572. /**
  4573. * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
  4574. * object {@link TextTrack} representations.
  4575. *
  4576. * @param {Array} json
  4577. * An array of `TextTrack` representation objects, like those that would be
  4578. * produced by `textTracksToJson`.
  4579. *
  4580. * @param {Tech} tech
  4581. * The `Tech` to create the `TextTrack`s on.
  4582. */
  4583. var jsonToTextTracks = function jsonToTextTracks(json, tech) {
  4584. json.forEach(function (track) {
  4585. var addedTrack = tech.addRemoteTextTrack(track).track;
  4586. if (!track.src && track.cues) {
  4587. track.cues.forEach(function (cue) {
  4588. return addedTrack.addCue(cue);
  4589. });
  4590. }
  4591. });
  4592. return tech.textTracks();
  4593. };
  4594. var textTrackConverter = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
  4595. /**
  4596. * @file modal-dialog.js
  4597. */
  4598. var MODAL_CLASS_NAME = 'vjs-modal-dialog';
  4599. var ESC = 27;
  4600. /**
  4601. * The `ModalDialog` displays over the video and its controls, which blocks
  4602. * interaction with the player until it is closed.
  4603. *
  4604. * Modal dialogs include a "Close" button and will close when that button
  4605. * is activated - or when ESC is pressed anywhere.
  4606. *
  4607. * @extends Component
  4608. */
  4609. var ModalDialog = function (_Component) {
  4610. inherits(ModalDialog, _Component);
  4611. /**
  4612. * Create an instance of this class.
  4613. *
  4614. * @param {Player} player
  4615. * The `Player` that this class should be attached to.
  4616. *
  4617. * @param {Object} [options]
  4618. * The key/value store of player options.
  4619. *
  4620. * @param {Mixed} [options.content=undefined]
  4621. * Provide customized content for this modal.
  4622. *
  4623. * @param {string} [options.description]
  4624. * A text description for the modal, primarily for accessibility.
  4625. *
  4626. * @param {boolean} [options.fillAlways=false]
  4627. * Normally, modals are automatically filled only the first time
  4628. * they open. This tells the modal to refresh its content
  4629. * every time it opens.
  4630. *
  4631. * @param {string} [options.label]
  4632. * A text label for the modal, primarily for accessibility.
  4633. *
  4634. * @param {boolean} [options.temporary=true]
  4635. * If `true`, the modal can only be opened once; it will be
  4636. * disposed as soon as it's closed.
  4637. *
  4638. * @param {boolean} [options.uncloseable=false]
  4639. * If `true`, the user will not be able to close the modal
  4640. * through the UI in the normal ways. Programmatic closing is
  4641. * still possible.
  4642. */
  4643. function ModalDialog(player, options) {
  4644. classCallCheck(this, ModalDialog);
  4645. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  4646. _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
  4647. _this.closeable(!_this.options_.uncloseable);
  4648. _this.content(_this.options_.content);
  4649. // Make sure the contentEl is defined AFTER any children are initialized
  4650. // because we only want the contents of the modal in the contentEl
  4651. // (not the UI elements like the close button).
  4652. _this.contentEl_ = createEl('div', {
  4653. className: MODAL_CLASS_NAME + '-content'
  4654. }, {
  4655. role: 'document'
  4656. });
  4657. _this.descEl_ = createEl('p', {
  4658. className: MODAL_CLASS_NAME + '-description vjs-control-text',
  4659. id: _this.el().getAttribute('aria-describedby')
  4660. });
  4661. textContent(_this.descEl_, _this.description());
  4662. _this.el_.appendChild(_this.descEl_);
  4663. _this.el_.appendChild(_this.contentEl_);
  4664. return _this;
  4665. }
  4666. /**
  4667. * Create the `ModalDialog`'s DOM element
  4668. *
  4669. * @return {Element}
  4670. * The DOM element that gets created.
  4671. */
  4672. ModalDialog.prototype.createEl = function createEl$$1() {
  4673. return _Component.prototype.createEl.call(this, 'div', {
  4674. className: this.buildCSSClass(),
  4675. tabIndex: -1
  4676. }, {
  4677. 'aria-describedby': this.id() + '_description',
  4678. 'aria-hidden': 'true',
  4679. 'aria-label': this.label(),
  4680. 'role': 'dialog'
  4681. });
  4682. };
  4683. ModalDialog.prototype.dispose = function dispose() {
  4684. this.contentEl_ = null;
  4685. this.descEl_ = null;
  4686. this.previouslyActiveEl_ = null;
  4687. _Component.prototype.dispose.call(this);
  4688. };
  4689. /**
  4690. * Builds the default DOM `className`.
  4691. *
  4692. * @return {string}
  4693. * The DOM `className` for this object.
  4694. */
  4695. ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
  4696. return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
  4697. };
  4698. /**
  4699. * Handles `keydown` events on the document, looking for ESC, which closes
  4700. * the modal.
  4701. *
  4702. * @param {EventTarget~Event} e
  4703. * The keypress that triggered this event.
  4704. *
  4705. * @listens keydown
  4706. */
  4707. ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
  4708. if (e.which === ESC && this.closeable()) {
  4709. this.close();
  4710. }
  4711. };
  4712. /**
  4713. * Returns the label string for this modal. Primarily used for accessibility.
  4714. *
  4715. * @return {string}
  4716. * the localized or raw label of this modal.
  4717. */
  4718. ModalDialog.prototype.label = function label() {
  4719. return this.localize(this.options_.label || 'Modal Window');
  4720. };
  4721. /**
  4722. * Returns the description string for this modal. Primarily used for
  4723. * accessibility.
  4724. *
  4725. * @return {string}
  4726. * The localized or raw description of this modal.
  4727. */
  4728. ModalDialog.prototype.description = function description() {
  4729. var desc = this.options_.description || this.localize('This is a modal window.');
  4730. // Append a universal closeability message if the modal is closeable.
  4731. if (this.closeable()) {
  4732. desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
  4733. }
  4734. return desc;
  4735. };
  4736. /**
  4737. * Opens the modal.
  4738. *
  4739. * @fires ModalDialog#beforemodalopen
  4740. * @fires ModalDialog#modalopen
  4741. */
  4742. ModalDialog.prototype.open = function open() {
  4743. if (!this.opened_) {
  4744. var player = this.player();
  4745. /**
  4746. * Fired just before a `ModalDialog` is opened.
  4747. *
  4748. * @event ModalDialog#beforemodalopen
  4749. * @type {EventTarget~Event}
  4750. */
  4751. this.trigger('beforemodalopen');
  4752. this.opened_ = true;
  4753. // Fill content if the modal has never opened before and
  4754. // never been filled.
  4755. if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
  4756. this.fill();
  4757. }
  4758. // If the player was playing, pause it and take note of its previously
  4759. // playing state.
  4760. this.wasPlaying_ = !player.paused();
  4761. if (this.options_.pauseOnOpen && this.wasPlaying_) {
  4762. player.pause();
  4763. }
  4764. if (this.closeable()) {
  4765. this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
  4766. }
  4767. // Hide controls and note if they were enabled.
  4768. this.hadControls_ = player.controls();
  4769. player.controls(false);
  4770. this.show();
  4771. this.conditionalFocus_();
  4772. this.el().setAttribute('aria-hidden', 'false');
  4773. /**
  4774. * Fired just after a `ModalDialog` is opened.
  4775. *
  4776. * @event ModalDialog#modalopen
  4777. * @type {EventTarget~Event}
  4778. */
  4779. this.trigger('modalopen');
  4780. this.hasBeenOpened_ = true;
  4781. }
  4782. };
  4783. /**
  4784. * If the `ModalDialog` is currently open or closed.
  4785. *
  4786. * @param {boolean} [value]
  4787. * If given, it will open (`true`) or close (`false`) the modal.
  4788. *
  4789. * @return {boolean}
  4790. * the current open state of the modaldialog
  4791. */
  4792. ModalDialog.prototype.opened = function opened(value) {
  4793. if (typeof value === 'boolean') {
  4794. this[value ? 'open' : 'close']();
  4795. }
  4796. return this.opened_;
  4797. };
  4798. /**
  4799. * Closes the modal, does nothing if the `ModalDialog` is
  4800. * not open.
  4801. *
  4802. * @fires ModalDialog#beforemodalclose
  4803. * @fires ModalDialog#modalclose
  4804. */
  4805. ModalDialog.prototype.close = function close() {
  4806. if (!this.opened_) {
  4807. return;
  4808. }
  4809. var player = this.player();
  4810. /**
  4811. * Fired just before a `ModalDialog` is closed.
  4812. *
  4813. * @event ModalDialog#beforemodalclose
  4814. * @type {EventTarget~Event}
  4815. */
  4816. this.trigger('beforemodalclose');
  4817. this.opened_ = false;
  4818. if (this.wasPlaying_ && this.options_.pauseOnOpen) {
  4819. player.play();
  4820. }
  4821. if (this.closeable()) {
  4822. this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
  4823. }
  4824. if (this.hadControls_) {
  4825. player.controls(true);
  4826. }
  4827. this.hide();
  4828. this.el().setAttribute('aria-hidden', 'true');
  4829. /**
  4830. * Fired just after a `ModalDialog` is closed.
  4831. *
  4832. * @event ModalDialog#modalclose
  4833. * @type {EventTarget~Event}
  4834. */
  4835. this.trigger('modalclose');
  4836. this.conditionalBlur_();
  4837. if (this.options_.temporary) {
  4838. this.dispose();
  4839. }
  4840. };
  4841. /**
  4842. * Check to see if the `ModalDialog` is closeable via the UI.
  4843. *
  4844. * @param {boolean} [value]
  4845. * If given as a boolean, it will set the `closeable` option.
  4846. *
  4847. * @return {boolean}
  4848. * Returns the final value of the closable option.
  4849. */
  4850. ModalDialog.prototype.closeable = function closeable(value) {
  4851. if (typeof value === 'boolean') {
  4852. var closeable = this.closeable_ = !!value;
  4853. var close = this.getChild('closeButton');
  4854. // If this is being made closeable and has no close button, add one.
  4855. if (closeable && !close) {
  4856. // The close button should be a child of the modal - not its
  4857. // content element, so temporarily change the content element.
  4858. var temp = this.contentEl_;
  4859. this.contentEl_ = this.el_;
  4860. close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' });
  4861. this.contentEl_ = temp;
  4862. this.on(close, 'close', this.close);
  4863. }
  4864. // If this is being made uncloseable and has a close button, remove it.
  4865. if (!closeable && close) {
  4866. this.off(close, 'close', this.close);
  4867. this.removeChild(close);
  4868. close.dispose();
  4869. }
  4870. }
  4871. return this.closeable_;
  4872. };
  4873. /**
  4874. * Fill the modal's content element with the modal's "content" option.
  4875. * The content element will be emptied before this change takes place.
  4876. */
  4877. ModalDialog.prototype.fill = function fill() {
  4878. this.fillWith(this.content());
  4879. };
  4880. /**
  4881. * Fill the modal's content element with arbitrary content.
  4882. * The content element will be emptied before this change takes place.
  4883. *
  4884. * @fires ModalDialog#beforemodalfill
  4885. * @fires ModalDialog#modalfill
  4886. *
  4887. * @param {Mixed} [content]
  4888. * The same rules apply to this as apply to the `content` option.
  4889. */
  4890. ModalDialog.prototype.fillWith = function fillWith(content) {
  4891. var contentEl = this.contentEl();
  4892. var parentEl = contentEl.parentNode;
  4893. var nextSiblingEl = contentEl.nextSibling;
  4894. /**
  4895. * Fired just before a `ModalDialog` is filled with content.
  4896. *
  4897. * @event ModalDialog#beforemodalfill
  4898. * @type {EventTarget~Event}
  4899. */
  4900. this.trigger('beforemodalfill');
  4901. this.hasBeenFilled_ = true;
  4902. // Detach the content element from the DOM before performing
  4903. // manipulation to avoid modifying the live DOM multiple times.
  4904. parentEl.removeChild(contentEl);
  4905. this.empty();
  4906. insertContent(contentEl, content);
  4907. /**
  4908. * Fired just after a `ModalDialog` is filled with content.
  4909. *
  4910. * @event ModalDialog#modalfill
  4911. * @type {EventTarget~Event}
  4912. */
  4913. this.trigger('modalfill');
  4914. // Re-inject the re-filled content element.
  4915. if (nextSiblingEl) {
  4916. parentEl.insertBefore(contentEl, nextSiblingEl);
  4917. } else {
  4918. parentEl.appendChild(contentEl);
  4919. }
  4920. // make sure that the close button is last in the dialog DOM
  4921. var closeButton = this.getChild('closeButton');
  4922. if (closeButton) {
  4923. parentEl.appendChild(closeButton.el_);
  4924. }
  4925. };
  4926. /**
  4927. * Empties the content element. This happens anytime the modal is filled.
  4928. *
  4929. * @fires ModalDialog#beforemodalempty
  4930. * @fires ModalDialog#modalempty
  4931. */
  4932. ModalDialog.prototype.empty = function empty() {
  4933. /**
  4934. * Fired just before a `ModalDialog` is emptied.
  4935. *
  4936. * @event ModalDialog#beforemodalempty
  4937. * @type {EventTarget~Event}
  4938. */
  4939. this.trigger('beforemodalempty');
  4940. emptyEl(this.contentEl());
  4941. /**
  4942. * Fired just after a `ModalDialog` is emptied.
  4943. *
  4944. * @event ModalDialog#modalempty
  4945. * @type {EventTarget~Event}
  4946. */
  4947. this.trigger('modalempty');
  4948. };
  4949. /**
  4950. * Gets or sets the modal content, which gets normalized before being
  4951. * rendered into the DOM.
  4952. *
  4953. * This does not update the DOM or fill the modal, but it is called during
  4954. * that process.
  4955. *
  4956. * @param {Mixed} [value]
  4957. * If defined, sets the internal content value to be used on the
  4958. * next call(s) to `fill`. This value is normalized before being
  4959. * inserted. To "clear" the internal content value, pass `null`.
  4960. *
  4961. * @return {Mixed}
  4962. * The current content of the modal dialog
  4963. */
  4964. ModalDialog.prototype.content = function content(value) {
  4965. if (typeof value !== 'undefined') {
  4966. this.content_ = value;
  4967. }
  4968. return this.content_;
  4969. };
  4970. /**
  4971. * conditionally focus the modal dialog if focus was previously on the player.
  4972. *
  4973. * @private
  4974. */
  4975. ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() {
  4976. var activeEl = document_1.activeElement;
  4977. var playerEl = this.player_.el_;
  4978. this.previouslyActiveEl_ = null;
  4979. if (playerEl.contains(activeEl) || playerEl === activeEl) {
  4980. this.previouslyActiveEl_ = activeEl;
  4981. this.focus();
  4982. this.on(document_1, 'keydown', this.handleKeyDown);
  4983. }
  4984. };
  4985. /**
  4986. * conditionally blur the element and refocus the last focused element
  4987. *
  4988. * @private
  4989. */
  4990. ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() {
  4991. if (this.previouslyActiveEl_) {
  4992. this.previouslyActiveEl_.focus();
  4993. this.previouslyActiveEl_ = null;
  4994. }
  4995. this.off(document_1, 'keydown', this.handleKeyDown);
  4996. };
  4997. /**
  4998. * Keydown handler. Attached when modal is focused.
  4999. *
  5000. * @listens keydown
  5001. */
  5002. ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) {
  5003. // exit early if it isn't a tab key
  5004. if (event.which !== 9) {
  5005. return;
  5006. }
  5007. var focusableEls = this.focusableEls_();
  5008. var activeEl = this.el_.querySelector(':focus');
  5009. var focusIndex = void 0;
  5010. for (var i = 0; i < focusableEls.length; i++) {
  5011. if (activeEl === focusableEls[i]) {
  5012. focusIndex = i;
  5013. break;
  5014. }
  5015. }
  5016. if (document_1.activeElement === this.el_) {
  5017. focusIndex = 0;
  5018. }
  5019. if (event.shiftKey && focusIndex === 0) {
  5020. focusableEls[focusableEls.length - 1].focus();
  5021. event.preventDefault();
  5022. } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
  5023. focusableEls[0].focus();
  5024. event.preventDefault();
  5025. }
  5026. };
  5027. /**
  5028. * get all focusable elements
  5029. *
  5030. * @private
  5031. */
  5032. ModalDialog.prototype.focusableEls_ = function focusableEls_() {
  5033. var allChildren = this.el_.querySelectorAll('*');
  5034. return Array.prototype.filter.call(allChildren, function (child) {
  5035. return (child instanceof window_1.HTMLAnchorElement || child instanceof window_1.HTMLAreaElement) && child.hasAttribute('href') || (child instanceof window_1.HTMLInputElement || child instanceof window_1.HTMLSelectElement || child instanceof window_1.HTMLTextAreaElement || child instanceof window_1.HTMLButtonElement) && !child.hasAttribute('disabled') || child instanceof window_1.HTMLIFrameElement || child instanceof window_1.HTMLObjectElement || child instanceof window_1.HTMLEmbedElement || child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1 || child.hasAttribute('contenteditable');
  5036. });
  5037. };
  5038. return ModalDialog;
  5039. }(Component);
  5040. /**
  5041. * Default options for `ModalDialog` default options.
  5042. *
  5043. * @type {Object}
  5044. * @private
  5045. */
  5046. ModalDialog.prototype.options_ = {
  5047. pauseOnOpen: true,
  5048. temporary: true
  5049. };
  5050. Component.registerComponent('ModalDialog', ModalDialog);
  5051. /**
  5052. * @file track-list.js
  5053. */
  5054. /**
  5055. * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
  5056. * {@link VideoTrackList}
  5057. *
  5058. * @extends EventTarget
  5059. */
  5060. var TrackList = function (_EventTarget) {
  5061. inherits(TrackList, _EventTarget);
  5062. /**
  5063. * Create an instance of this class
  5064. *
  5065. * @param {Track[]} tracks
  5066. * A list of tracks to initialize the list with.
  5067. *
  5068. * @param {Object} [list]
  5069. * The child object with inheritance done manually for ie8.
  5070. *
  5071. * @abstract
  5072. */
  5073. function TrackList() {
  5074. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5075. var _ret;
  5076. var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  5077. classCallCheck(this, TrackList);
  5078. var _this = possibleConstructorReturn(this, _EventTarget.call(this));
  5079. if (!list) {
  5080. list = _this; // eslint-disable-line
  5081. if (IS_IE8) {
  5082. list = document_1.createElement('custom');
  5083. for (var prop in TrackList.prototype) {
  5084. if (prop !== 'constructor') {
  5085. list[prop] = TrackList.prototype[prop];
  5086. }
  5087. }
  5088. }
  5089. }
  5090. list.tracks_ = [];
  5091. /**
  5092. * @memberof TrackList
  5093. * @member {number} length
  5094. * The current number of `Track`s in the this Trackist.
  5095. * @instance
  5096. */
  5097. Object.defineProperty(list, 'length', {
  5098. get: function get$$1() {
  5099. return this.tracks_.length;
  5100. }
  5101. });
  5102. for (var i = 0; i < tracks.length; i++) {
  5103. list.addTrack(tracks[i]);
  5104. }
  5105. // must return the object, as for ie8 it will not be this
  5106. // but a reference to a document object
  5107. return _ret = list, possibleConstructorReturn(_this, _ret);
  5108. }
  5109. /**
  5110. * Add a {@link Track} to the `TrackList`
  5111. *
  5112. * @param {Track} track
  5113. * The audio, video, or text track to add to the list.
  5114. *
  5115. * @fires TrackList#addtrack
  5116. */
  5117. TrackList.prototype.addTrack = function addTrack(track) {
  5118. var index = this.tracks_.length;
  5119. if (!('' + index in this)) {
  5120. Object.defineProperty(this, index, {
  5121. get: function get$$1() {
  5122. return this.tracks_[index];
  5123. }
  5124. });
  5125. }
  5126. // Do not add duplicate tracks
  5127. if (this.tracks_.indexOf(track) === -1) {
  5128. this.tracks_.push(track);
  5129. /**
  5130. * Triggered when a track is added to a track list.
  5131. *
  5132. * @event TrackList#addtrack
  5133. * @type {EventTarget~Event}
  5134. * @property {Track} track
  5135. * A reference to track that was added.
  5136. */
  5137. this.trigger({
  5138. track: track,
  5139. type: 'addtrack'
  5140. });
  5141. }
  5142. };
  5143. /**
  5144. * Remove a {@link Track} from the `TrackList`
  5145. *
  5146. * @param {Track} rtrack
  5147. * The audio, video, or text track to remove from the list.
  5148. *
  5149. * @fires TrackList#removetrack
  5150. */
  5151. TrackList.prototype.removeTrack = function removeTrack(rtrack) {
  5152. var track = void 0;
  5153. for (var i = 0, l = this.length; i < l; i++) {
  5154. if (this[i] === rtrack) {
  5155. track = this[i];
  5156. if (track.off) {
  5157. track.off();
  5158. }
  5159. this.tracks_.splice(i, 1);
  5160. break;
  5161. }
  5162. }
  5163. if (!track) {
  5164. return;
  5165. }
  5166. /**
  5167. * Triggered when a track is removed from track list.
  5168. *
  5169. * @event TrackList#removetrack
  5170. * @type {EventTarget~Event}
  5171. * @property {Track} track
  5172. * A reference to track that was removed.
  5173. */
  5174. this.trigger({
  5175. track: track,
  5176. type: 'removetrack'
  5177. });
  5178. };
  5179. /**
  5180. * Get a Track from the TrackList by a tracks id
  5181. *
  5182. * @param {String} id - the id of the track to get
  5183. * @method getTrackById
  5184. * @return {Track}
  5185. * @private
  5186. */
  5187. TrackList.prototype.getTrackById = function getTrackById(id) {
  5188. var result = null;
  5189. for (var i = 0, l = this.length; i < l; i++) {
  5190. var track = this[i];
  5191. if (track.id === id) {
  5192. result = track;
  5193. break;
  5194. }
  5195. }
  5196. return result;
  5197. };
  5198. return TrackList;
  5199. }(EventTarget);
  5200. /**
  5201. * Triggered when a different track is selected/enabled.
  5202. *
  5203. * @event TrackList#change
  5204. * @type {EventTarget~Event}
  5205. */
  5206. /**
  5207. * Events that can be called with on + eventName. See {@link EventHandler}.
  5208. *
  5209. * @property {Object} TrackList#allowedEvents_
  5210. * @private
  5211. */
  5212. TrackList.prototype.allowedEvents_ = {
  5213. change: 'change',
  5214. addtrack: 'addtrack',
  5215. removetrack: 'removetrack'
  5216. };
  5217. // emulate attribute EventHandler support to allow for feature detection
  5218. for (var event in TrackList.prototype.allowedEvents_) {
  5219. TrackList.prototype['on' + event] = null;
  5220. }
  5221. /**
  5222. * @file audio-track-list.js
  5223. */
  5224. /**
  5225. * Anywhere we call this function we diverge from the spec
  5226. * as we only support one enabled audiotrack at a time
  5227. *
  5228. * @param {AudioTrackList} list
  5229. * list to work on
  5230. *
  5231. * @param {AudioTrack} track
  5232. * The track to skip
  5233. *
  5234. * @private
  5235. */
  5236. var disableOthers = function disableOthers(list, track) {
  5237. for (var i = 0; i < list.length; i++) {
  5238. if (!Object.keys(list[i]).length || track.id === list[i].id) {
  5239. continue;
  5240. }
  5241. // another audio track is enabled, disable it
  5242. list[i].enabled = false;
  5243. }
  5244. };
  5245. /**
  5246. * The current list of {@link AudioTrack} for a media file.
  5247. *
  5248. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
  5249. * @extends TrackList
  5250. */
  5251. var AudioTrackList = function (_TrackList) {
  5252. inherits(AudioTrackList, _TrackList);
  5253. /**
  5254. * Create an instance of this class.
  5255. *
  5256. * @param {AudioTrack[]} [tracks=[]]
  5257. * A list of `AudioTrack` to instantiate the list with.
  5258. */
  5259. function AudioTrackList() {
  5260. var _this, _ret;
  5261. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5262. classCallCheck(this, AudioTrackList);
  5263. var list = void 0;
  5264. // make sure only 1 track is enabled
  5265. // sorted from last index to first index
  5266. for (var i = tracks.length - 1; i >= 0; i--) {
  5267. if (tracks[i].enabled) {
  5268. disableOthers(tracks, tracks[i]);
  5269. break;
  5270. }
  5271. }
  5272. // IE8 forces us to implement inheritance ourselves
  5273. // as it does not support Object.defineProperty properly
  5274. if (IS_IE8) {
  5275. list = document_1.createElement('custom');
  5276. for (var prop in TrackList.prototype) {
  5277. if (prop !== 'constructor') {
  5278. list[prop] = TrackList.prototype[prop];
  5279. }
  5280. }
  5281. for (var _prop in AudioTrackList.prototype) {
  5282. if (_prop !== 'constructor') {
  5283. list[_prop] = AudioTrackList.prototype[_prop];
  5284. }
  5285. }
  5286. }
  5287. list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  5288. list.changing_ = false;
  5289. return _ret = list, possibleConstructorReturn(_this, _ret);
  5290. }
  5291. /**
  5292. * Add an {@link AudioTrack} to the `AudioTrackList`.
  5293. *
  5294. * @param {AudioTrack} track
  5295. * The AudioTrack to add to the list
  5296. *
  5297. * @fires TrackList#addtrack
  5298. */
  5299. AudioTrackList.prototype.addTrack = function addTrack(track) {
  5300. var _this2 = this;
  5301. if (track.enabled) {
  5302. disableOthers(this, track);
  5303. }
  5304. _TrackList.prototype.addTrack.call(this, track);
  5305. // native tracks don't have this
  5306. if (!track.addEventListener) {
  5307. return;
  5308. }
  5309. /**
  5310. * @listens AudioTrack#enabledchange
  5311. * @fires TrackList#change
  5312. */
  5313. track.addEventListener('enabledchange', function () {
  5314. // when we are disabling other tracks (since we don't support
  5315. // more than one track at a time) we will set changing_
  5316. // to true so that we don't trigger additional change events
  5317. if (_this2.changing_) {
  5318. return;
  5319. }
  5320. _this2.changing_ = true;
  5321. disableOthers(_this2, track);
  5322. _this2.changing_ = false;
  5323. _this2.trigger('change');
  5324. });
  5325. };
  5326. return AudioTrackList;
  5327. }(TrackList);
  5328. /**
  5329. * @file video-track-list.js
  5330. */
  5331. /**
  5332. * Un-select all other {@link VideoTrack}s that are selected.
  5333. *
  5334. * @param {VideoTrackList} list
  5335. * list to work on
  5336. *
  5337. * @param {VideoTrack} track
  5338. * The track to skip
  5339. *
  5340. * @private
  5341. */
  5342. var disableOthers$1 = function disableOthers(list, track) {
  5343. for (var i = 0; i < list.length; i++) {
  5344. if (!Object.keys(list[i]).length || track.id === list[i].id) {
  5345. continue;
  5346. }
  5347. // another video track is enabled, disable it
  5348. list[i].selected = false;
  5349. }
  5350. };
  5351. /**
  5352. * The current list of {@link VideoTrack} for a video.
  5353. *
  5354. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
  5355. * @extends TrackList
  5356. */
  5357. var VideoTrackList = function (_TrackList) {
  5358. inherits(VideoTrackList, _TrackList);
  5359. /**
  5360. * Create an instance of this class.
  5361. *
  5362. * @param {VideoTrack[]} [tracks=[]]
  5363. * A list of `VideoTrack` to instantiate the list with.
  5364. */
  5365. function VideoTrackList() {
  5366. var _this, _ret;
  5367. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5368. classCallCheck(this, VideoTrackList);
  5369. var list = void 0;
  5370. // make sure only 1 track is enabled
  5371. // sorted from last index to first index
  5372. for (var i = tracks.length - 1; i >= 0; i--) {
  5373. if (tracks[i].selected) {
  5374. disableOthers$1(tracks, tracks[i]);
  5375. break;
  5376. }
  5377. }
  5378. // IE8 forces us to implement inheritance ourselves
  5379. // as it does not support Object.defineProperty properly
  5380. if (IS_IE8) {
  5381. list = document_1.createElement('custom');
  5382. for (var prop in TrackList.prototype) {
  5383. if (prop !== 'constructor') {
  5384. list[prop] = TrackList.prototype[prop];
  5385. }
  5386. }
  5387. for (var _prop in VideoTrackList.prototype) {
  5388. if (_prop !== 'constructor') {
  5389. list[_prop] = VideoTrackList.prototype[_prop];
  5390. }
  5391. }
  5392. }
  5393. list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  5394. list.changing_ = false;
  5395. /**
  5396. * @member {number} VideoTrackList#selectedIndex
  5397. * The current index of the selected {@link VideoTrack`}.
  5398. */
  5399. Object.defineProperty(list, 'selectedIndex', {
  5400. get: function get$$1() {
  5401. for (var _i = 0; _i < this.length; _i++) {
  5402. if (this[_i].selected) {
  5403. return _i;
  5404. }
  5405. }
  5406. return -1;
  5407. },
  5408. set: function set$$1() {}
  5409. });
  5410. return _ret = list, possibleConstructorReturn(_this, _ret);
  5411. }
  5412. /**
  5413. * Add a {@link VideoTrack} to the `VideoTrackList`.
  5414. *
  5415. * @param {VideoTrack} track
  5416. * The VideoTrack to add to the list
  5417. *
  5418. * @fires TrackList#addtrack
  5419. */
  5420. VideoTrackList.prototype.addTrack = function addTrack(track) {
  5421. var _this2 = this;
  5422. if (track.selected) {
  5423. disableOthers$1(this, track);
  5424. }
  5425. _TrackList.prototype.addTrack.call(this, track);
  5426. // native tracks don't have this
  5427. if (!track.addEventListener) {
  5428. return;
  5429. }
  5430. /**
  5431. * @listens VideoTrack#selectedchange
  5432. * @fires TrackList#change
  5433. */
  5434. track.addEventListener('selectedchange', function () {
  5435. if (_this2.changing_) {
  5436. return;
  5437. }
  5438. _this2.changing_ = true;
  5439. disableOthers$1(_this2, track);
  5440. _this2.changing_ = false;
  5441. _this2.trigger('change');
  5442. });
  5443. };
  5444. return VideoTrackList;
  5445. }(TrackList);
  5446. /**
  5447. * @file text-track-list.js
  5448. */
  5449. /**
  5450. * The current list of {@link TextTrack} for a media file.
  5451. *
  5452. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
  5453. * @extends TrackList
  5454. */
  5455. var TextTrackList = function (_TrackList) {
  5456. inherits(TextTrackList, _TrackList);
  5457. /**
  5458. * Create an instance of this class.
  5459. *
  5460. * @param {TextTrack[]} [tracks=[]]
  5461. * A list of `TextTrack` to instantiate the list with.
  5462. */
  5463. function TextTrackList() {
  5464. var _this, _ret;
  5465. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5466. classCallCheck(this, TextTrackList);
  5467. var list = void 0;
  5468. // IE8 forces us to implement inheritance ourselves
  5469. // as it does not support Object.defineProperty properly
  5470. if (IS_IE8) {
  5471. list = document_1.createElement('custom');
  5472. for (var prop in TrackList.prototype) {
  5473. if (prop !== 'constructor') {
  5474. list[prop] = TrackList.prototype[prop];
  5475. }
  5476. }
  5477. for (var _prop in TextTrackList.prototype) {
  5478. if (_prop !== 'constructor') {
  5479. list[_prop] = TextTrackList.prototype[_prop];
  5480. }
  5481. }
  5482. }
  5483. list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  5484. return _ret = list, possibleConstructorReturn(_this, _ret);
  5485. }
  5486. /**
  5487. * Add a {@link TextTrack} to the `TextTrackList`
  5488. *
  5489. * @param {TextTrack} track
  5490. * The text track to add to the list.
  5491. *
  5492. * @fires TrackList#addtrack
  5493. */
  5494. TextTrackList.prototype.addTrack = function addTrack(track) {
  5495. _TrackList.prototype.addTrack.call(this, track);
  5496. /**
  5497. * @listens TextTrack#modechange
  5498. * @fires TrackList#change
  5499. */
  5500. track.addEventListener('modechange', bind(this, function () {
  5501. this.trigger('change');
  5502. }));
  5503. var nonLanguageTextTrackKind = ['metadata', 'chapters'];
  5504. if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
  5505. track.addEventListener('modechange', bind(this, function () {
  5506. this.trigger('selectedlanguagechange');
  5507. }));
  5508. }
  5509. };
  5510. return TextTrackList;
  5511. }(TrackList);
  5512. /**
  5513. * @file html-track-element-list.js
  5514. */
  5515. /**
  5516. * The current list of {@link HtmlTrackElement}s.
  5517. */
  5518. var HtmlTrackElementList = function () {
  5519. /**
  5520. * Create an instance of this class.
  5521. *
  5522. * @param {HtmlTrackElement[]} [tracks=[]]
  5523. * A list of `HtmlTrackElement` to instantiate the list with.
  5524. */
  5525. function HtmlTrackElementList() {
  5526. var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5527. classCallCheck(this, HtmlTrackElementList);
  5528. var list = this; // eslint-disable-line
  5529. if (IS_IE8) {
  5530. list = document_1.createElement('custom');
  5531. for (var prop in HtmlTrackElementList.prototype) {
  5532. if (prop !== 'constructor') {
  5533. list[prop] = HtmlTrackElementList.prototype[prop];
  5534. }
  5535. }
  5536. }
  5537. list.trackElements_ = [];
  5538. /**
  5539. * @memberof HtmlTrackElementList
  5540. * @member {number} length
  5541. * The current number of `Track`s in the this Trackist.
  5542. * @instance
  5543. */
  5544. Object.defineProperty(list, 'length', {
  5545. get: function get$$1() {
  5546. return this.trackElements_.length;
  5547. }
  5548. });
  5549. for (var i = 0, length = trackElements.length; i < length; i++) {
  5550. list.addTrackElement_(trackElements[i]);
  5551. }
  5552. if (IS_IE8) {
  5553. return list;
  5554. }
  5555. }
  5556. /**
  5557. * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
  5558. *
  5559. * @param {HtmlTrackElement} trackElement
  5560. * The track element to add to the list.
  5561. *
  5562. * @private
  5563. */
  5564. HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
  5565. var index = this.trackElements_.length;
  5566. if (!('' + index in this)) {
  5567. Object.defineProperty(this, index, {
  5568. get: function get$$1() {
  5569. return this.trackElements_[index];
  5570. }
  5571. });
  5572. }
  5573. // Do not add duplicate elements
  5574. if (this.trackElements_.indexOf(trackElement) === -1) {
  5575. this.trackElements_.push(trackElement);
  5576. }
  5577. };
  5578. /**
  5579. * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
  5580. * {@link TextTrack}.
  5581. *
  5582. * @param {TextTrack} track
  5583. * The track associated with a track element.
  5584. *
  5585. * @return {HtmlTrackElement|undefined}
  5586. * The track element that was found or undefined.
  5587. *
  5588. * @private
  5589. */
  5590. HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
  5591. var trackElement_ = void 0;
  5592. for (var i = 0, length = this.trackElements_.length; i < length; i++) {
  5593. if (track === this.trackElements_[i].track) {
  5594. trackElement_ = this.trackElements_[i];
  5595. break;
  5596. }
  5597. }
  5598. return trackElement_;
  5599. };
  5600. /**
  5601. * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
  5602. *
  5603. * @param {HtmlTrackElement} trackElement
  5604. * The track element to remove from the list.
  5605. *
  5606. * @private
  5607. */
  5608. HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
  5609. for (var i = 0, length = this.trackElements_.length; i < length; i++) {
  5610. if (trackElement === this.trackElements_[i]) {
  5611. this.trackElements_.splice(i, 1);
  5612. break;
  5613. }
  5614. }
  5615. };
  5616. return HtmlTrackElementList;
  5617. }();
  5618. /**
  5619. * @file text-track-cue-list.js
  5620. */
  5621. /**
  5622. * @typedef {Object} TextTrackCueList~TextTrackCue
  5623. *
  5624. * @property {string} id
  5625. * The unique id for this text track cue
  5626. *
  5627. * @property {number} startTime
  5628. * The start time for this text track cue
  5629. *
  5630. * @property {number} endTime
  5631. * The end time for this text track cue
  5632. *
  5633. * @property {boolean} pauseOnExit
  5634. * Pause when the end time is reached if true.
  5635. *
  5636. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
  5637. */
  5638. /**
  5639. * A List of TextTrackCues.
  5640. *
  5641. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
  5642. */
  5643. var TextTrackCueList = function () {
  5644. /**
  5645. * Create an instance of this class..
  5646. *
  5647. * @param {Array} cues
  5648. * A list of cues to be initialized with
  5649. */
  5650. function TextTrackCueList(cues) {
  5651. classCallCheck(this, TextTrackCueList);
  5652. var list = this; // eslint-disable-line
  5653. if (IS_IE8) {
  5654. list = document_1.createElement('custom');
  5655. for (var prop in TextTrackCueList.prototype) {
  5656. if (prop !== 'constructor') {
  5657. list[prop] = TextTrackCueList.prototype[prop];
  5658. }
  5659. }
  5660. }
  5661. TextTrackCueList.prototype.setCues_.call(list, cues);
  5662. /**
  5663. * @memberof TextTrackCueList
  5664. * @member {number} length
  5665. * The current number of `TextTrackCue`s in the TextTrackCueList.
  5666. * @instance
  5667. */
  5668. Object.defineProperty(list, 'length', {
  5669. get: function get$$1() {
  5670. return this.length_;
  5671. }
  5672. });
  5673. if (IS_IE8) {
  5674. return list;
  5675. }
  5676. }
  5677. /**
  5678. * A setter for cues in this list. Creates getters
  5679. * an an index for the cues.
  5680. *
  5681. * @param {Array} cues
  5682. * An array of cues to set
  5683. *
  5684. * @private
  5685. */
  5686. TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
  5687. var oldLength = this.length || 0;
  5688. var i = 0;
  5689. var l = cues.length;
  5690. this.cues_ = cues;
  5691. this.length_ = cues.length;
  5692. var defineProp = function defineProp(index) {
  5693. if (!('' + index in this)) {
  5694. Object.defineProperty(this, '' + index, {
  5695. get: function get$$1() {
  5696. return this.cues_[index];
  5697. }
  5698. });
  5699. }
  5700. };
  5701. if (oldLength < l) {
  5702. i = oldLength;
  5703. for (; i < l; i++) {
  5704. defineProp.call(this, i);
  5705. }
  5706. }
  5707. };
  5708. /**
  5709. * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
  5710. *
  5711. * @param {string} id
  5712. * The id of the cue that should be searched for.
  5713. *
  5714. * @return {TextTrackCueList~TextTrackCue|null}
  5715. * A single cue or null if none was found.
  5716. */
  5717. TextTrackCueList.prototype.getCueById = function getCueById(id) {
  5718. var result = null;
  5719. for (var i = 0, l = this.length; i < l; i++) {
  5720. var cue = this[i];
  5721. if (cue.id === id) {
  5722. result = cue;
  5723. break;
  5724. }
  5725. }
  5726. return result;
  5727. };
  5728. return TextTrackCueList;
  5729. }();
  5730. /**
  5731. * @file track-kinds.js
  5732. */
  5733. /**
  5734. * All possible `VideoTrackKind`s
  5735. *
  5736. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
  5737. * @typedef VideoTrack~Kind
  5738. * @enum
  5739. */
  5740. var VideoTrackKind = {
  5741. alternative: 'alternative',
  5742. captions: 'captions',
  5743. main: 'main',
  5744. sign: 'sign',
  5745. subtitles: 'subtitles',
  5746. commentary: 'commentary'
  5747. };
  5748. /**
  5749. * All possible `AudioTrackKind`s
  5750. *
  5751. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
  5752. * @typedef AudioTrack~Kind
  5753. * @enum
  5754. */
  5755. var AudioTrackKind = {
  5756. 'alternative': 'alternative',
  5757. 'descriptions': 'descriptions',
  5758. 'main': 'main',
  5759. 'main-desc': 'main-desc',
  5760. 'translation': 'translation',
  5761. 'commentary': 'commentary'
  5762. };
  5763. /**
  5764. * All possible `TextTrackKind`s
  5765. *
  5766. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
  5767. * @typedef TextTrack~Kind
  5768. * @enum
  5769. */
  5770. var TextTrackKind = {
  5771. subtitles: 'subtitles',
  5772. captions: 'captions',
  5773. descriptions: 'descriptions',
  5774. chapters: 'chapters',
  5775. metadata: 'metadata'
  5776. };
  5777. /**
  5778. * All possible `TextTrackMode`s
  5779. *
  5780. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
  5781. * @typedef TextTrack~Mode
  5782. * @enum
  5783. */
  5784. var TextTrackMode = {
  5785. disabled: 'disabled',
  5786. hidden: 'hidden',
  5787. showing: 'showing'
  5788. };
  5789. /**
  5790. * @file track.js
  5791. */
  5792. /**
  5793. * A Track class that contains all of the common functionality for {@link AudioTrack},
  5794. * {@link VideoTrack}, and {@link TextTrack}.
  5795. *
  5796. * > Note: This class should not be used directly
  5797. *
  5798. * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
  5799. * @extends EventTarget
  5800. * @abstract
  5801. */
  5802. var Track = function (_EventTarget) {
  5803. inherits(Track, _EventTarget);
  5804. /**
  5805. * Create an instance of this class.
  5806. *
  5807. * @param {Object} [options={}]
  5808. * Object of option names and values
  5809. *
  5810. * @param {string} [options.kind='']
  5811. * A valid kind for the track type you are creating.
  5812. *
  5813. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  5814. * A unique id for this AudioTrack.
  5815. *
  5816. * @param {string} [options.label='']
  5817. * The menu label for this track.
  5818. *
  5819. * @param {string} [options.language='']
  5820. * A valid two character language code.
  5821. *
  5822. * @abstract
  5823. */
  5824. function Track() {
  5825. var _ret;
  5826. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  5827. classCallCheck(this, Track);
  5828. var _this = possibleConstructorReturn(this, _EventTarget.call(this));
  5829. var track = _this; // eslint-disable-line
  5830. if (IS_IE8) {
  5831. track = document_1.createElement('custom');
  5832. for (var prop in Track.prototype) {
  5833. if (prop !== 'constructor') {
  5834. track[prop] = Track.prototype[prop];
  5835. }
  5836. }
  5837. }
  5838. var trackProps = {
  5839. id: options.id || 'vjs_track_' + newGUID(),
  5840. kind: options.kind || '',
  5841. label: options.label || '',
  5842. language: options.language || ''
  5843. };
  5844. /**
  5845. * @memberof Track
  5846. * @member {string} id
  5847. * The id of this track. Cannot be changed after creation.
  5848. * @instance
  5849. *
  5850. * @readonly
  5851. */
  5852. /**
  5853. * @memberof Track
  5854. * @member {string} kind
  5855. * The kind of track that this is. Cannot be changed after creation.
  5856. * @instance
  5857. *
  5858. * @readonly
  5859. */
  5860. /**
  5861. * @memberof Track
  5862. * @member {string} label
  5863. * The label of this track. Cannot be changed after creation.
  5864. * @instance
  5865. *
  5866. * @readonly
  5867. */
  5868. /**
  5869. * @memberof Track
  5870. * @member {string} language
  5871. * The two letter language code for this track. Cannot be changed after
  5872. * creation.
  5873. * @instance
  5874. *
  5875. * @readonly
  5876. */
  5877. var _loop = function _loop(key) {
  5878. Object.defineProperty(track, key, {
  5879. get: function get$$1() {
  5880. return trackProps[key];
  5881. },
  5882. set: function set$$1() {}
  5883. });
  5884. };
  5885. for (var key in trackProps) {
  5886. _loop(key);
  5887. }
  5888. return _ret = track, possibleConstructorReturn(_this, _ret);
  5889. }
  5890. return Track;
  5891. }(EventTarget);
  5892. /**
  5893. * @file url.js
  5894. * @module url
  5895. */
  5896. /**
  5897. * @typedef {Object} url:URLObject
  5898. *
  5899. * @property {string} protocol
  5900. * The protocol of the url that was parsed.
  5901. *
  5902. * @property {string} hostname
  5903. * The hostname of the url that was parsed.
  5904. *
  5905. * @property {string} port
  5906. * The port of the url that was parsed.
  5907. *
  5908. * @property {string} pathname
  5909. * The pathname of the url that was parsed.
  5910. *
  5911. * @property {string} search
  5912. * The search query of the url that was parsed.
  5913. *
  5914. * @property {string} hash
  5915. * The hash of the url that was parsed.
  5916. *
  5917. * @property {string} host
  5918. * The host of the url that was parsed.
  5919. */
  5920. /**
  5921. * Resolve and parse the elements of a URL.
  5922. *
  5923. * @param {String} url
  5924. * The url to parse
  5925. *
  5926. * @return {url:URLObject}
  5927. * An object of url details
  5928. */
  5929. var parseUrl = function parseUrl(url) {
  5930. var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];
  5931. // add the url to an anchor and let the browser parse the URL
  5932. var a = document_1.createElement('a');
  5933. a.href = url;
  5934. // IE8 (and 9?) Fix
  5935. // ie8 doesn't parse the URL correctly until the anchor is actually
  5936. // added to the body, and an innerHTML is needed to trigger the parsing
  5937. var addToBody = a.host === '' && a.protocol !== 'file:';
  5938. var div = void 0;
  5939. if (addToBody) {
  5940. div = document_1.createElement('div');
  5941. div.innerHTML = '<a href="' + url + '"></a>';
  5942. a = div.firstChild;
  5943. // prevent the div from affecting layout
  5944. div.setAttribute('style', 'display:none; position:absolute;');
  5945. document_1.body.appendChild(div);
  5946. }
  5947. // Copy the specific URL properties to a new object
  5948. // This is also needed for IE8 because the anchor loses its
  5949. // properties when it's removed from the dom
  5950. var details = {};
  5951. for (var i = 0; i < props.length; i++) {
  5952. details[props[i]] = a[props[i]];
  5953. }
  5954. // IE9 adds the port to the host property unlike everyone else. If
  5955. // a port identifier is added for standard ports, strip it.
  5956. if (details.protocol === 'http:') {
  5957. details.host = details.host.replace(/:80$/, '');
  5958. }
  5959. if (details.protocol === 'https:') {
  5960. details.host = details.host.replace(/:443$/, '');
  5961. }
  5962. if (!details.protocol) {
  5963. details.protocol = window_1.location.protocol;
  5964. }
  5965. if (addToBody) {
  5966. document_1.body.removeChild(div);
  5967. }
  5968. return details;
  5969. };
  5970. /**
  5971. * Get absolute version of relative URL. Used to tell flash correct URL.
  5972. *
  5973. *
  5974. * @param {string} url
  5975. * URL to make absolute
  5976. *
  5977. * @return {string}
  5978. * Absolute URL
  5979. *
  5980. * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
  5981. */
  5982. var getAbsoluteURL = function getAbsoluteURL(url) {
  5983. // Check if absolute URL
  5984. if (!url.match(/^https?:\/\//)) {
  5985. // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
  5986. var div = document_1.createElement('div');
  5987. div.innerHTML = '<a href="' + url + '">x</a>';
  5988. url = div.firstChild.href;
  5989. }
  5990. return url;
  5991. };
  5992. /**
  5993. * Returns the extension of the passed file name. It will return an empty string
  5994. * if passed an invalid path.
  5995. *
  5996. * @param {string} path
  5997. * The fileName path like '/path/to/file.mp4'
  5998. *
  5999. * @returns {string}
  6000. * The extension in lower case or an empty string if no
  6001. * extension could be found.
  6002. */
  6003. var getFileExtension = function getFileExtension(path) {
  6004. if (typeof path === 'string') {
  6005. var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
  6006. var pathParts = splitPathRe.exec(path);
  6007. if (pathParts) {
  6008. return pathParts.pop().toLowerCase();
  6009. }
  6010. }
  6011. return '';
  6012. };
  6013. /**
  6014. * Returns whether the url passed is a cross domain request or not.
  6015. *
  6016. * @param {string} url
  6017. * The url to check.
  6018. *
  6019. * @return {boolean}
  6020. * Whether it is a cross domain request or not.
  6021. */
  6022. var isCrossOrigin = function isCrossOrigin(url) {
  6023. var winLoc = window_1.location;
  6024. var urlInfo = parseUrl(url);
  6025. // IE8 protocol relative urls will return ':' for protocol
  6026. var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
  6027. // Check if url is for another domain/origin
  6028. // IE8 doesn't know location.origin, so we won't rely on it here
  6029. var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
  6030. return crossOrigin;
  6031. };
  6032. var Url = (Object.freeze || Object)({
  6033. parseUrl: parseUrl,
  6034. getAbsoluteURL: getAbsoluteURL,
  6035. getFileExtension: getFileExtension,
  6036. isCrossOrigin: isCrossOrigin
  6037. });
  6038. var isFunction_1 = isFunction;
  6039. var toString$1 = Object.prototype.toString;
  6040. function isFunction (fn) {
  6041. var string = toString$1.call(fn);
  6042. return string === '[object Function]' ||
  6043. (typeof fn === 'function' && string !== '[object RegExp]') ||
  6044. (typeof window !== 'undefined' &&
  6045. // IE8 and below
  6046. (fn === window.setTimeout ||
  6047. fn === window.alert ||
  6048. fn === window.confirm ||
  6049. fn === window.prompt))
  6050. }
  6051. var trim_1 = createCommonjsModule(function (module, exports) {
  6052. exports = module.exports = trim;
  6053. function trim(str){
  6054. return str.replace(/^\s*|\s*$/g, '');
  6055. }
  6056. exports.left = function(str){
  6057. return str.replace(/^\s*/, '');
  6058. };
  6059. exports.right = function(str){
  6060. return str.replace(/\s*$/, '');
  6061. };
  6062. });
  6063. var fnToStr = Function.prototype.toString;
  6064. var constructorRegex = /^\s*class\b/;
  6065. var isES6ClassFn = function isES6ClassFunction(value) {
  6066. try {
  6067. var fnStr = fnToStr.call(value);
  6068. return constructorRegex.test(fnStr);
  6069. } catch (e) {
  6070. return false; // not a function
  6071. }
  6072. };
  6073. var tryFunctionObject = function tryFunctionToStr(value) {
  6074. try {
  6075. if (isES6ClassFn(value)) { return false; }
  6076. fnToStr.call(value);
  6077. return true;
  6078. } catch (e) {
  6079. return false;
  6080. }
  6081. };
  6082. var toStr$1 = Object.prototype.toString;
  6083. var fnClass = '[object Function]';
  6084. var genClass = '[object GeneratorFunction]';
  6085. var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
  6086. var isCallable = function isCallable(value) {
  6087. if (!value) { return false; }
  6088. if (typeof value !== 'function' && typeof value !== 'object') { return false; }
  6089. if (typeof value === 'function' && !value.prototype) { return true; }
  6090. if (hasToStringTag) { return tryFunctionObject(value); }
  6091. if (isES6ClassFn(value)) { return false; }
  6092. var strClass = toStr$1.call(value);
  6093. return strClass === fnClass || strClass === genClass;
  6094. };
  6095. var toStr = Object.prototype.toString;
  6096. var hasOwnProperty = Object.prototype.hasOwnProperty;
  6097. var forEachArray$1 = function forEachArray(array, iterator, receiver) {
  6098. for (var i = 0, len = array.length; i < len; i++) {
  6099. if (hasOwnProperty.call(array, i)) {
  6100. if (receiver == null) {
  6101. iterator(array[i], i, array);
  6102. } else {
  6103. iterator.call(receiver, array[i], i, array);
  6104. }
  6105. }
  6106. }
  6107. };
  6108. var forEachString = function forEachString(string, iterator, receiver) {
  6109. for (var i = 0, len = string.length; i < len; i++) {
  6110. // no such thing as a sparse string.
  6111. if (receiver == null) {
  6112. iterator(string.charAt(i), i, string);
  6113. } else {
  6114. iterator.call(receiver, string.charAt(i), i, string);
  6115. }
  6116. }
  6117. };
  6118. var forEachObject = function forEachObject(object, iterator, receiver) {
  6119. for (var k in object) {
  6120. if (hasOwnProperty.call(object, k)) {
  6121. if (receiver == null) {
  6122. iterator(object[k], k, object);
  6123. } else {
  6124. iterator.call(receiver, object[k], k, object);
  6125. }
  6126. }
  6127. }
  6128. };
  6129. var forEach = function forEach(list, iterator, thisArg) {
  6130. if (!isCallable(iterator)) {
  6131. throw new TypeError('iterator must be a function');
  6132. }
  6133. var receiver;
  6134. if (arguments.length >= 3) {
  6135. receiver = thisArg;
  6136. }
  6137. if (toStr.call(list) === '[object Array]') {
  6138. forEachArray$1(list, iterator, receiver);
  6139. } else if (typeof list === 'string') {
  6140. forEachString(list, iterator, receiver);
  6141. } else {
  6142. forEachObject(list, iterator, receiver);
  6143. }
  6144. };
  6145. var forEach_1 = forEach;
  6146. var isArray = function(arg) {
  6147. return Object.prototype.toString.call(arg) === '[object Array]';
  6148. };
  6149. var parseHeaders = function (headers) {
  6150. if (!headers)
  6151. return {}
  6152. var result = {};
  6153. forEach_1(
  6154. trim_1(headers).split('\n')
  6155. , function (row) {
  6156. var index = row.indexOf(':')
  6157. , key = trim_1(row.slice(0, index)).toLowerCase()
  6158. , value = trim_1(row.slice(index + 1));
  6159. if (typeof(result[key]) === 'undefined') {
  6160. result[key] = value;
  6161. } else if (isArray(result[key])) {
  6162. result[key].push(value);
  6163. } else {
  6164. result[key] = [ result[key], value ];
  6165. }
  6166. }
  6167. );
  6168. return result
  6169. };
  6170. var immutable = extend;
  6171. var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
  6172. function extend() {
  6173. var target = {};
  6174. for (var i = 0; i < arguments.length; i++) {
  6175. var source = arguments[i];
  6176. for (var key in source) {
  6177. if (hasOwnProperty$1.call(source, key)) {
  6178. target[key] = source[key];
  6179. }
  6180. }
  6181. }
  6182. return target
  6183. }
  6184. var xhr = createXHR;
  6185. createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop;
  6186. createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest;
  6187. forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
  6188. createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
  6189. options = initParams(uri, options, callback);
  6190. options.method = method.toUpperCase();
  6191. return _createXHR(options)
  6192. };
  6193. });
  6194. function forEachArray(array, iterator) {
  6195. for (var i = 0; i < array.length; i++) {
  6196. iterator(array[i]);
  6197. }
  6198. }
  6199. function isEmpty(obj){
  6200. for(var i in obj){
  6201. if(obj.hasOwnProperty(i)) return false
  6202. }
  6203. return true
  6204. }
  6205. function initParams(uri, options, callback) {
  6206. var params = uri;
  6207. if (isFunction_1(options)) {
  6208. callback = options;
  6209. if (typeof uri === "string") {
  6210. params = {uri:uri};
  6211. }
  6212. } else {
  6213. params = immutable(options, {uri: uri});
  6214. }
  6215. params.callback = callback;
  6216. return params
  6217. }
  6218. function createXHR(uri, options, callback) {
  6219. options = initParams(uri, options, callback);
  6220. return _createXHR(options)
  6221. }
  6222. function _createXHR(options) {
  6223. if(typeof options.callback === "undefined"){
  6224. throw new Error("callback argument missing")
  6225. }
  6226. var called = false;
  6227. var callback = function cbOnce(err, response, body){
  6228. if(!called){
  6229. called = true;
  6230. options.callback(err, response, body);
  6231. }
  6232. };
  6233. function readystatechange() {
  6234. if (xhr.readyState === 4) {
  6235. setTimeout(loadFunc, 0);
  6236. }
  6237. }
  6238. function getBody() {
  6239. // Chrome with requestType=blob throws errors arround when even testing access to responseText
  6240. var body = undefined;
  6241. if (xhr.response) {
  6242. body = xhr.response;
  6243. } else {
  6244. body = xhr.responseText || getXml(xhr);
  6245. }
  6246. if (isJson) {
  6247. try {
  6248. body = JSON.parse(body);
  6249. } catch (e) {}
  6250. }
  6251. return body
  6252. }
  6253. function errorFunc(evt) {
  6254. clearTimeout(timeoutTimer);
  6255. if(!(evt instanceof Error)){
  6256. evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") );
  6257. }
  6258. evt.statusCode = 0;
  6259. return callback(evt, failureResponse)
  6260. }
  6261. // will load the data & process the response in a special response object
  6262. function loadFunc() {
  6263. if (aborted) return
  6264. var status;
  6265. clearTimeout(timeoutTimer);
  6266. if(options.useXDR && xhr.status===undefined) {
  6267. //IE8 CORS GET successful response doesn't have a status field, but body is fine
  6268. status = 200;
  6269. } else {
  6270. status = (xhr.status === 1223 ? 204 : xhr.status);
  6271. }
  6272. var response = failureResponse;
  6273. var err = null;
  6274. if (status !== 0){
  6275. response = {
  6276. body: getBody(),
  6277. statusCode: status,
  6278. method: method,
  6279. headers: {},
  6280. url: uri,
  6281. rawRequest: xhr
  6282. };
  6283. if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
  6284. response.headers = parseHeaders(xhr.getAllResponseHeaders());
  6285. }
  6286. } else {
  6287. err = new Error("Internal XMLHttpRequest Error");
  6288. }
  6289. return callback(err, response, response.body)
  6290. }
  6291. var xhr = options.xhr || null;
  6292. if (!xhr) {
  6293. if (options.cors || options.useXDR) {
  6294. xhr = new createXHR.XDomainRequest();
  6295. }else{
  6296. xhr = new createXHR.XMLHttpRequest();
  6297. }
  6298. }
  6299. var key;
  6300. var aborted;
  6301. var uri = xhr.url = options.uri || options.url;
  6302. var method = xhr.method = options.method || "GET";
  6303. var body = options.body || options.data;
  6304. var headers = xhr.headers = options.headers || {};
  6305. var sync = !!options.sync;
  6306. var isJson = false;
  6307. var timeoutTimer;
  6308. var failureResponse = {
  6309. body: undefined,
  6310. headers: {},
  6311. statusCode: 0,
  6312. method: method,
  6313. url: uri,
  6314. rawRequest: xhr
  6315. };
  6316. if ("json" in options && options.json !== false) {
  6317. isJson = true;
  6318. headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
  6319. if (method !== "GET" && method !== "HEAD") {
  6320. headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
  6321. body = JSON.stringify(options.json === true ? body : options.json);
  6322. }
  6323. }
  6324. xhr.onreadystatechange = readystatechange;
  6325. xhr.onload = loadFunc;
  6326. xhr.onerror = errorFunc;
  6327. // IE9 must have onprogress be set to a unique function.
  6328. xhr.onprogress = function () {
  6329. // IE must die
  6330. };
  6331. xhr.onabort = function(){
  6332. aborted = true;
  6333. };
  6334. xhr.ontimeout = errorFunc;
  6335. xhr.open(method, uri, !sync, options.username, options.password);
  6336. //has to be after open
  6337. if(!sync) {
  6338. xhr.withCredentials = !!options.withCredentials;
  6339. }
  6340. // Cannot set timeout with sync request
  6341. // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
  6342. // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
  6343. if (!sync && options.timeout > 0 ) {
  6344. timeoutTimer = setTimeout(function(){
  6345. if (aborted) return
  6346. aborted = true;//IE9 may still call readystatechange
  6347. xhr.abort("timeout");
  6348. var e = new Error("XMLHttpRequest timeout");
  6349. e.code = "ETIMEDOUT";
  6350. errorFunc(e);
  6351. }, options.timeout );
  6352. }
  6353. if (xhr.setRequestHeader) {
  6354. for(key in headers){
  6355. if(headers.hasOwnProperty(key)){
  6356. xhr.setRequestHeader(key, headers[key]);
  6357. }
  6358. }
  6359. } else if (options.headers && !isEmpty(options.headers)) {
  6360. throw new Error("Headers cannot be set on an XDomainRequest object")
  6361. }
  6362. if ("responseType" in options) {
  6363. xhr.responseType = options.responseType;
  6364. }
  6365. if ("beforeSend" in options &&
  6366. typeof options.beforeSend === "function"
  6367. ) {
  6368. options.beforeSend(xhr);
  6369. }
  6370. // Microsoft Edge browser sends "undefined" when send is called with undefined value.
  6371. // XMLHttpRequest spec says to pass null as body to indicate no body
  6372. // See https://github.com/naugtur/xhr/issues/100.
  6373. xhr.send(body || null);
  6374. return xhr
  6375. }
  6376. function getXml(xhr) {
  6377. if (xhr.responseType === "document") {
  6378. return xhr.responseXML
  6379. }
  6380. var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
  6381. if (xhr.responseType === "" && !firefoxBugTakenEffect) {
  6382. return xhr.responseXML
  6383. }
  6384. return null
  6385. }
  6386. function noop() {}
  6387. /**
  6388. * @file text-track.js
  6389. */
  6390. /**
  6391. * Takes a webvtt file contents and parses it into cues
  6392. *
  6393. * @param {string} srcContent
  6394. * webVTT file contents
  6395. *
  6396. * @param {TextTrack} track
  6397. * TextTrack to add cues to. Cues come from the srcContent.
  6398. *
  6399. * @private
  6400. */
  6401. var parseCues = function parseCues(srcContent, track) {
  6402. var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, window_1.WebVTT.StringDecoder());
  6403. var errors = [];
  6404. parser.oncue = function (cue) {
  6405. track.addCue(cue);
  6406. };
  6407. parser.onparsingerror = function (error) {
  6408. errors.push(error);
  6409. };
  6410. parser.onflush = function () {
  6411. track.trigger({
  6412. type: 'loadeddata',
  6413. target: track
  6414. });
  6415. };
  6416. parser.parse(srcContent);
  6417. if (errors.length > 0) {
  6418. if (window_1.console && window_1.console.groupCollapsed) {
  6419. window_1.console.groupCollapsed('Text Track parsing errors for ' + track.src);
  6420. }
  6421. errors.forEach(function (error) {
  6422. return log.error(error);
  6423. });
  6424. if (window_1.console && window_1.console.groupEnd) {
  6425. window_1.console.groupEnd();
  6426. }
  6427. }
  6428. parser.flush();
  6429. };
  6430. /**
  6431. * Load a `TextTrack` from a specifed url.
  6432. *
  6433. * @param {string} src
  6434. * Url to load track from.
  6435. *
  6436. * @param {TextTrack} track
  6437. * Track to add cues to. Comes from the content at the end of `url`.
  6438. *
  6439. * @private
  6440. */
  6441. var loadTrack = function loadTrack(src, track) {
  6442. var opts = {
  6443. uri: src
  6444. };
  6445. var crossOrigin = isCrossOrigin(src);
  6446. if (crossOrigin) {
  6447. opts.cors = crossOrigin;
  6448. }
  6449. xhr(opts, bind(this, function (err, response, responseBody) {
  6450. if (err) {
  6451. return log.error(err, response);
  6452. }
  6453. track.loaded_ = true;
  6454. // Make sure that vttjs has loaded, otherwise, wait till it finished loading
  6455. // NOTE: this is only used for the alt/video.novtt.js build
  6456. if (typeof window_1.WebVTT !== 'function') {
  6457. if (track.tech_) {
  6458. var loadHandler = function loadHandler() {
  6459. return parseCues(responseBody, track);
  6460. };
  6461. track.tech_.on('vttjsloaded', loadHandler);
  6462. track.tech_.on('vttjserror', function () {
  6463. log.error('vttjs failed to load, stopping trying to process ' + track.src);
  6464. track.tech_.off('vttjsloaded', loadHandler);
  6465. });
  6466. }
  6467. } else {
  6468. parseCues(responseBody, track);
  6469. }
  6470. }));
  6471. };
  6472. /**
  6473. * A representation of a single `TextTrack`.
  6474. *
  6475. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
  6476. * @extends Track
  6477. */
  6478. var TextTrack = function (_Track) {
  6479. inherits(TextTrack, _Track);
  6480. /**
  6481. * Create an instance of this class.
  6482. *
  6483. * @param {Object} options={}
  6484. * Object of option names and values
  6485. *
  6486. * @param {Tech} options.tech
  6487. * A reference to the tech that owns this TextTrack.
  6488. *
  6489. * @param {TextTrack~Kind} [options.kind='subtitles']
  6490. * A valid text track kind.
  6491. *
  6492. * @param {TextTrack~Mode} [options.mode='disabled']
  6493. * A valid text track mode.
  6494. *
  6495. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6496. * A unique id for this TextTrack.
  6497. *
  6498. * @param {string} [options.label='']
  6499. * The menu label for this track.
  6500. *
  6501. * @param {string} [options.language='']
  6502. * A valid two character language code.
  6503. *
  6504. * @param {string} [options.srclang='']
  6505. * A valid two character language code. An alternative, but deprioritized
  6506. * vesion of `options.language`
  6507. *
  6508. * @param {string} [options.src]
  6509. * A url to TextTrack cues.
  6510. *
  6511. * @param {boolean} [options.default]
  6512. * If this track should default to on or off.
  6513. */
  6514. function TextTrack() {
  6515. var _this, _ret;
  6516. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6517. classCallCheck(this, TextTrack);
  6518. if (!options.tech) {
  6519. throw new Error('A tech was not provided.');
  6520. }
  6521. var settings = mergeOptions(options, {
  6522. kind: TextTrackKind[options.kind] || 'subtitles',
  6523. language: options.language || options.srclang || ''
  6524. });
  6525. var mode = TextTrackMode[settings.mode] || 'disabled';
  6526. var default_ = settings['default'];
  6527. if (settings.kind === 'metadata' || settings.kind === 'chapters') {
  6528. mode = 'hidden';
  6529. }
  6530. // on IE8 this will be a document element
  6531. // for every other browser this will be a normal object
  6532. var tt = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  6533. tt.tech_ = settings.tech;
  6534. if (IS_IE8) {
  6535. for (var prop in TextTrack.prototype) {
  6536. if (prop !== 'constructor') {
  6537. tt[prop] = TextTrack.prototype[prop];
  6538. }
  6539. }
  6540. }
  6541. tt.cues_ = [];
  6542. tt.activeCues_ = [];
  6543. var cues = new TextTrackCueList(tt.cues_);
  6544. var activeCues = new TextTrackCueList(tt.activeCues_);
  6545. var changed = false;
  6546. var timeupdateHandler = bind(tt, function () {
  6547. // Accessing this.activeCues for the side-effects of updating itself
  6548. // due to it's nature as a getter function. Do not remove or cues will
  6549. // stop updating!
  6550. // Use the setter to prevent deletion from uglify (pure_getters rule)
  6551. this.activeCues = this.activeCues;
  6552. if (changed) {
  6553. this.trigger('cuechange');
  6554. changed = false;
  6555. }
  6556. });
  6557. if (mode !== 'disabled') {
  6558. tt.tech_.ready(function () {
  6559. tt.tech_.on('timeupdate', timeupdateHandler);
  6560. }, true);
  6561. }
  6562. /**
  6563. * @memberof TextTrack
  6564. * @member {boolean} default
  6565. * If this track was set to be on or off by default. Cannot be changed after
  6566. * creation.
  6567. * @instance
  6568. *
  6569. * @readonly
  6570. */
  6571. Object.defineProperty(tt, 'default', {
  6572. get: function get$$1() {
  6573. return default_;
  6574. },
  6575. set: function set$$1() {}
  6576. });
  6577. /**
  6578. * @memberof TextTrack
  6579. * @member {string} mode
  6580. * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
  6581. * not be set if setting to an invalid mode.
  6582. * @instance
  6583. *
  6584. * @fires TextTrack#modechange
  6585. */
  6586. Object.defineProperty(tt, 'mode', {
  6587. get: function get$$1() {
  6588. return mode;
  6589. },
  6590. set: function set$$1(newMode) {
  6591. var _this2 = this;
  6592. if (!TextTrackMode[newMode]) {
  6593. return;
  6594. }
  6595. mode = newMode;
  6596. if (mode !== 'disabled') {
  6597. this.tech_.ready(function () {
  6598. _this2.tech_.on('timeupdate', timeupdateHandler);
  6599. }, true);
  6600. } else {
  6601. this.tech_.off('timeupdate', timeupdateHandler);
  6602. }
  6603. /**
  6604. * An event that fires when mode changes on this track. This allows
  6605. * the TextTrackList that holds this track to act accordingly.
  6606. *
  6607. * > Note: This is not part of the spec!
  6608. *
  6609. * @event TextTrack#modechange
  6610. * @type {EventTarget~Event}
  6611. */
  6612. this.trigger('modechange');
  6613. }
  6614. });
  6615. /**
  6616. * @memberof TextTrack
  6617. * @member {TextTrackCueList} cues
  6618. * The text track cue list for this TextTrack.
  6619. * @instance
  6620. */
  6621. Object.defineProperty(tt, 'cues', {
  6622. get: function get$$1() {
  6623. if (!this.loaded_) {
  6624. return null;
  6625. }
  6626. return cues;
  6627. },
  6628. set: function set$$1() {}
  6629. });
  6630. /**
  6631. * @memberof TextTrack
  6632. * @member {TextTrackCueList} activeCues
  6633. * The list text track cues that are currently active for this TextTrack.
  6634. * @instance
  6635. */
  6636. Object.defineProperty(tt, 'activeCues', {
  6637. get: function get$$1() {
  6638. if (!this.loaded_) {
  6639. return null;
  6640. }
  6641. // nothing to do
  6642. if (this.cues.length === 0) {
  6643. return activeCues;
  6644. }
  6645. var ct = this.tech_.currentTime();
  6646. var active = [];
  6647. for (var i = 0, l = this.cues.length; i < l; i++) {
  6648. var cue = this.cues[i];
  6649. if (cue.startTime <= ct && cue.endTime >= ct) {
  6650. active.push(cue);
  6651. } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
  6652. active.push(cue);
  6653. }
  6654. }
  6655. changed = false;
  6656. if (active.length !== this.activeCues_.length) {
  6657. changed = true;
  6658. } else {
  6659. for (var _i = 0; _i < active.length; _i++) {
  6660. if (this.activeCues_.indexOf(active[_i]) === -1) {
  6661. changed = true;
  6662. }
  6663. }
  6664. }
  6665. this.activeCues_ = active;
  6666. activeCues.setCues_(this.activeCues_);
  6667. return activeCues;
  6668. },
  6669. // /!\ Keep this setter empty (see the timeupdate handler above)
  6670. set: function set$$1() {}
  6671. });
  6672. if (settings.src) {
  6673. tt.src = settings.src;
  6674. loadTrack(settings.src, tt);
  6675. } else {
  6676. tt.loaded_ = true;
  6677. }
  6678. return _ret = tt, possibleConstructorReturn(_this, _ret);
  6679. }
  6680. /**
  6681. * Add a cue to the internal list of cues.
  6682. *
  6683. * @param {TextTrack~Cue} cue
  6684. * The cue to add to our internal list
  6685. */
  6686. TextTrack.prototype.addCue = function addCue(originalCue) {
  6687. var cue = originalCue;
  6688. if (window_1.vttjs && !(originalCue instanceof window_1.vttjs.VTTCue)) {
  6689. cue = new window_1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
  6690. for (var prop in originalCue) {
  6691. if (!(prop in cue)) {
  6692. cue[prop] = originalCue[prop];
  6693. }
  6694. }
  6695. // make sure that `id` is copied over
  6696. cue.id = originalCue.id;
  6697. cue.originalCue_ = originalCue;
  6698. }
  6699. var tracks = this.tech_.textTracks();
  6700. for (var i = 0; i < tracks.length; i++) {
  6701. if (tracks[i] !== this) {
  6702. tracks[i].removeCue(cue);
  6703. }
  6704. }
  6705. this.cues_.push(cue);
  6706. this.cues.setCues_(this.cues_);
  6707. };
  6708. /**
  6709. * Remove a cue from our internal list
  6710. *
  6711. * @param {TextTrack~Cue} removeCue
  6712. * The cue to remove from our internal list
  6713. */
  6714. TextTrack.prototype.removeCue = function removeCue(_removeCue) {
  6715. var i = this.cues_.length;
  6716. while (i--) {
  6717. var cue = this.cues_[i];
  6718. if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
  6719. this.cues_.splice(i, 1);
  6720. this.cues.setCues_(this.cues_);
  6721. break;
  6722. }
  6723. }
  6724. };
  6725. return TextTrack;
  6726. }(Track);
  6727. /**
  6728. * cuechange - One or more cues in the track have become active or stopped being active.
  6729. */
  6730. TextTrack.prototype.allowedEvents_ = {
  6731. cuechange: 'cuechange'
  6732. };
  6733. /**
  6734. * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
  6735. * only one `AudioTrack` in the list will be enabled at a time.
  6736. *
  6737. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
  6738. * @extends Track
  6739. */
  6740. var AudioTrack = function (_Track) {
  6741. inherits(AudioTrack, _Track);
  6742. /**
  6743. * Create an instance of this class.
  6744. *
  6745. * @param {Object} [options={}]
  6746. * Object of option names and values
  6747. *
  6748. * @param {AudioTrack~Kind} [options.kind='']
  6749. * A valid audio track kind
  6750. *
  6751. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6752. * A unique id for this AudioTrack.
  6753. *
  6754. * @param {string} [options.label='']
  6755. * The menu label for this track.
  6756. *
  6757. * @param {string} [options.language='']
  6758. * A valid two character language code.
  6759. *
  6760. * @param {boolean} [options.enabled]
  6761. * If this track is the one that is currently playing. If this track is part of
  6762. * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
  6763. */
  6764. function AudioTrack() {
  6765. var _this, _ret;
  6766. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6767. classCallCheck(this, AudioTrack);
  6768. var settings = mergeOptions(options, {
  6769. kind: AudioTrackKind[options.kind] || ''
  6770. });
  6771. // on IE8 this will be a document element
  6772. // for every other browser this will be a normal object
  6773. var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  6774. var enabled = false;
  6775. if (IS_IE8) {
  6776. for (var prop in AudioTrack.prototype) {
  6777. if (prop !== 'constructor') {
  6778. track[prop] = AudioTrack.prototype[prop];
  6779. }
  6780. }
  6781. }
  6782. /**
  6783. * @memberof AudioTrack
  6784. * @member {boolean} enabled
  6785. * If this `AudioTrack` is enabled or not. When setting this will
  6786. * fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
  6787. * @instance
  6788. *
  6789. * @fires VideoTrack#selectedchange
  6790. */
  6791. Object.defineProperty(track, 'enabled', {
  6792. get: function get$$1() {
  6793. return enabled;
  6794. },
  6795. set: function set$$1(newEnabled) {
  6796. // an invalid or unchanged value
  6797. if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
  6798. return;
  6799. }
  6800. enabled = newEnabled;
  6801. /**
  6802. * An event that fires when enabled changes on this track. This allows
  6803. * the AudioTrackList that holds this track to act accordingly.
  6804. *
  6805. * > Note: This is not part of the spec! Native tracks will do
  6806. * this internally without an event.
  6807. *
  6808. * @event AudioTrack#enabledchange
  6809. * @type {EventTarget~Event}
  6810. */
  6811. this.trigger('enabledchange');
  6812. }
  6813. });
  6814. // if the user sets this track to selected then
  6815. // set selected to that true value otherwise
  6816. // we keep it false
  6817. if (settings.enabled) {
  6818. track.enabled = settings.enabled;
  6819. }
  6820. track.loaded_ = true;
  6821. return _ret = track, possibleConstructorReturn(_this, _ret);
  6822. }
  6823. return AudioTrack;
  6824. }(Track);
  6825. /**
  6826. * A representation of a single `VideoTrack`.
  6827. *
  6828. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
  6829. * @extends Track
  6830. */
  6831. var VideoTrack = function (_Track) {
  6832. inherits(VideoTrack, _Track);
  6833. /**
  6834. * Create an instance of this class.
  6835. *
  6836. * @param {Object} [options={}]
  6837. * Object of option names and values
  6838. *
  6839. * @param {string} [options.kind='']
  6840. * A valid {@link VideoTrack~Kind}
  6841. *
  6842. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6843. * A unique id for this AudioTrack.
  6844. *
  6845. * @param {string} [options.label='']
  6846. * The menu label for this track.
  6847. *
  6848. * @param {string} [options.language='']
  6849. * A valid two character language code.
  6850. *
  6851. * @param {boolean} [options.selected]
  6852. * If this track is the one that is currently playing.
  6853. */
  6854. function VideoTrack() {
  6855. var _this, _ret;
  6856. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6857. classCallCheck(this, VideoTrack);
  6858. var settings = mergeOptions(options, {
  6859. kind: VideoTrackKind[options.kind] || ''
  6860. });
  6861. // on IE8 this will be a document element
  6862. // for every other browser this will be a normal object
  6863. var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  6864. var selected = false;
  6865. if (IS_IE8) {
  6866. for (var prop in VideoTrack.prototype) {
  6867. if (prop !== 'constructor') {
  6868. track[prop] = VideoTrack.prototype[prop];
  6869. }
  6870. }
  6871. }
  6872. /**
  6873. * @memberof VideoTrack
  6874. * @member {boolean} selected
  6875. * If this `VideoTrack` is selected or not. When setting this will
  6876. * fire {@link VideoTrack#selectedchange} if the state of selected changed.
  6877. * @instance
  6878. *
  6879. * @fires VideoTrack#selectedchange
  6880. */
  6881. Object.defineProperty(track, 'selected', {
  6882. get: function get$$1() {
  6883. return selected;
  6884. },
  6885. set: function set$$1(newSelected) {
  6886. // an invalid or unchanged value
  6887. if (typeof newSelected !== 'boolean' || newSelected === selected) {
  6888. return;
  6889. }
  6890. selected = newSelected;
  6891. /**
  6892. * An event that fires when selected changes on this track. This allows
  6893. * the VideoTrackList that holds this track to act accordingly.
  6894. *
  6895. * > Note: This is not part of the spec! Native tracks will do
  6896. * this internally without an event.
  6897. *
  6898. * @event VideoTrack#selectedchange
  6899. * @type {EventTarget~Event}
  6900. */
  6901. this.trigger('selectedchange');
  6902. }
  6903. });
  6904. // if the user sets this track to selected then
  6905. // set selected to that true value otherwise
  6906. // we keep it false
  6907. if (settings.selected) {
  6908. track.selected = settings.selected;
  6909. }
  6910. return _ret = track, possibleConstructorReturn(_this, _ret);
  6911. }
  6912. return VideoTrack;
  6913. }(Track);
  6914. /**
  6915. * @file html-track-element.js
  6916. */
  6917. /**
  6918. * @memberof HTMLTrackElement
  6919. * @typedef {HTMLTrackElement~ReadyState}
  6920. * @enum {number}
  6921. */
  6922. var NONE = 0;
  6923. var LOADING = 1;
  6924. var LOADED = 2;
  6925. var ERROR = 3;
  6926. /**
  6927. * A single track represented in the DOM.
  6928. *
  6929. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
  6930. * @extends EventTarget
  6931. */
  6932. var HTMLTrackElement = function (_EventTarget) {
  6933. inherits(HTMLTrackElement, _EventTarget);
  6934. /**
  6935. * Create an instance of this class.
  6936. *
  6937. * @param {Object} options={}
  6938. * Object of option names and values
  6939. *
  6940. * @param {Tech} options.tech
  6941. * A reference to the tech that owns this HTMLTrackElement.
  6942. *
  6943. * @param {TextTrack~Kind} [options.kind='subtitles']
  6944. * A valid text track kind.
  6945. *
  6946. * @param {TextTrack~Mode} [options.mode='disabled']
  6947. * A valid text track mode.
  6948. *
  6949. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6950. * A unique id for this TextTrack.
  6951. *
  6952. * @param {string} [options.label='']
  6953. * The menu label for this track.
  6954. *
  6955. * @param {string} [options.language='']
  6956. * A valid two character language code.
  6957. *
  6958. * @param {string} [options.srclang='']
  6959. * A valid two character language code. An alternative, but deprioritized
  6960. * vesion of `options.language`
  6961. *
  6962. * @param {string} [options.src]
  6963. * A url to TextTrack cues.
  6964. *
  6965. * @param {boolean} [options.default]
  6966. * If this track should default to on or off.
  6967. */
  6968. function HTMLTrackElement() {
  6969. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6970. classCallCheck(this, HTMLTrackElement);
  6971. var _this = possibleConstructorReturn(this, _EventTarget.call(this));
  6972. var readyState = void 0;
  6973. var trackElement = _this; // eslint-disable-line
  6974. if (IS_IE8) {
  6975. trackElement = document_1.createElement('custom');
  6976. for (var prop in HTMLTrackElement.prototype) {
  6977. if (prop !== 'constructor') {
  6978. trackElement[prop] = HTMLTrackElement.prototype[prop];
  6979. }
  6980. }
  6981. }
  6982. var track = new TextTrack(options);
  6983. trackElement.kind = track.kind;
  6984. trackElement.src = track.src;
  6985. trackElement.srclang = track.language;
  6986. trackElement.label = track.label;
  6987. trackElement['default'] = track['default'];
  6988. /**
  6989. * @memberof HTMLTrackElement
  6990. * @member {HTMLTrackElement~ReadyState} readyState
  6991. * The current ready state of the track element.
  6992. * @instance
  6993. */
  6994. Object.defineProperty(trackElement, 'readyState', {
  6995. get: function get$$1() {
  6996. return readyState;
  6997. }
  6998. });
  6999. /**
  7000. * @memberof HTMLTrackElement
  7001. * @member {TextTrack} track
  7002. * The underlying TextTrack object.
  7003. * @instance
  7004. *
  7005. */
  7006. Object.defineProperty(trackElement, 'track', {
  7007. get: function get$$1() {
  7008. return track;
  7009. }
  7010. });
  7011. readyState = NONE;
  7012. /**
  7013. * @listens TextTrack#loadeddata
  7014. * @fires HTMLTrackElement#load
  7015. */
  7016. track.addEventListener('loadeddata', function () {
  7017. readyState = LOADED;
  7018. trackElement.trigger({
  7019. type: 'load',
  7020. target: trackElement
  7021. });
  7022. });
  7023. if (IS_IE8) {
  7024. var _ret;
  7025. return _ret = trackElement, possibleConstructorReturn(_this, _ret);
  7026. }
  7027. return _this;
  7028. }
  7029. return HTMLTrackElement;
  7030. }(EventTarget);
  7031. HTMLTrackElement.prototype.allowedEvents_ = {
  7032. load: 'load'
  7033. };
  7034. HTMLTrackElement.NONE = NONE;
  7035. HTMLTrackElement.LOADING = LOADING;
  7036. HTMLTrackElement.LOADED = LOADED;
  7037. HTMLTrackElement.ERROR = ERROR;
  7038. /*
  7039. * This file contains all track properties that are used in
  7040. * player.js, tech.js, html5.js and possibly other techs in the future.
  7041. */
  7042. var NORMAL = {
  7043. audio: {
  7044. ListClass: AudioTrackList,
  7045. TrackClass: AudioTrack,
  7046. capitalName: 'Audio'
  7047. },
  7048. video: {
  7049. ListClass: VideoTrackList,
  7050. TrackClass: VideoTrack,
  7051. capitalName: 'Video'
  7052. },
  7053. text: {
  7054. ListClass: TextTrackList,
  7055. TrackClass: TextTrack,
  7056. capitalName: 'Text'
  7057. }
  7058. };
  7059. Object.keys(NORMAL).forEach(function (type) {
  7060. NORMAL[type].getterName = type + 'Tracks';
  7061. NORMAL[type].privateName = type + 'Tracks_';
  7062. });
  7063. var REMOTE = {
  7064. remoteText: {
  7065. ListClass: TextTrackList,
  7066. TrackClass: TextTrack,
  7067. capitalName: 'RemoteText',
  7068. getterName: 'remoteTextTracks',
  7069. privateName: 'remoteTextTracks_'
  7070. },
  7071. remoteTextEl: {
  7072. ListClass: HtmlTrackElementList,
  7073. TrackClass: HTMLTrackElement,
  7074. capitalName: 'RemoteTextTrackEls',
  7075. getterName: 'remoteTextTrackEls',
  7076. privateName: 'remoteTextTrackEls_'
  7077. }
  7078. };
  7079. var ALL = mergeOptions(NORMAL, REMOTE);
  7080. REMOTE.names = Object.keys(REMOTE);
  7081. NORMAL.names = Object.keys(NORMAL);
  7082. ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
  7083. /**
  7084. * Copyright 2013 vtt.js Contributors
  7085. *
  7086. * Licensed under the Apache License, Version 2.0 (the "License");
  7087. * you may not use this file except in compliance with the License.
  7088. * You may obtain a copy of the License at
  7089. *
  7090. * http://www.apache.org/licenses/LICENSE-2.0
  7091. *
  7092. * Unless required by applicable law or agreed to in writing, software
  7093. * distributed under the License is distributed on an "AS IS" BASIS,
  7094. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  7095. * See the License for the specific language governing permissions and
  7096. * limitations under the License.
  7097. */
  7098. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  7099. /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
  7100. var _objCreate = Object.create || (function() {
  7101. function F() {}
  7102. return function(o) {
  7103. if (arguments.length !== 1) {
  7104. throw new Error('Object.create shim only accepts one parameter.');
  7105. }
  7106. F.prototype = o;
  7107. return new F();
  7108. };
  7109. })();
  7110. // Creates a new ParserError object from an errorData object. The errorData
  7111. // object should have default code and message properties. The default message
  7112. // property can be overriden by passing in a message parameter.
  7113. // See ParsingError.Errors below for acceptable errors.
  7114. function ParsingError(errorData, message) {
  7115. this.name = "ParsingError";
  7116. this.code = errorData.code;
  7117. this.message = message || errorData.message;
  7118. }
  7119. ParsingError.prototype = _objCreate(Error.prototype);
  7120. ParsingError.prototype.constructor = ParsingError;
  7121. // ParsingError metadata for acceptable ParsingErrors.
  7122. ParsingError.Errors = {
  7123. BadSignature: {
  7124. code: 0,
  7125. message: "Malformed WebVTT signature."
  7126. },
  7127. BadTimeStamp: {
  7128. code: 1,
  7129. message: "Malformed time stamp."
  7130. }
  7131. };
  7132. // Try to parse input as a time stamp.
  7133. function parseTimeStamp(input) {
  7134. function computeSeconds(h, m, s, f) {
  7135. return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
  7136. }
  7137. var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
  7138. if (!m) {
  7139. return null;
  7140. }
  7141. if (m[3]) {
  7142. // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
  7143. return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
  7144. } else if (m[1] > 59) {
  7145. // Timestamp takes the form of [hours]:[minutes].[milliseconds]
  7146. // First position is hours as it's over 59.
  7147. return computeSeconds(m[1], m[2], 0, m[4]);
  7148. } else {
  7149. // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
  7150. return computeSeconds(0, m[1], m[2], m[4]);
  7151. }
  7152. }
  7153. // A settings object holds key/value pairs and will ignore anything but the first
  7154. // assignment to a specific key.
  7155. function Settings() {
  7156. this.values = _objCreate(null);
  7157. }
  7158. Settings.prototype = {
  7159. // Only accept the first assignment to any key.
  7160. set: function(k, v) {
  7161. if (!this.get(k) && v !== "") {
  7162. this.values[k] = v;
  7163. }
  7164. },
  7165. // Return the value for a key, or a default value.
  7166. // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
  7167. // a number of possible default values as properties where 'defaultKey' is
  7168. // the key of the property that will be chosen; otherwise it's assumed to be
  7169. // a single value.
  7170. get: function(k, dflt, defaultKey) {
  7171. if (defaultKey) {
  7172. return this.has(k) ? this.values[k] : dflt[defaultKey];
  7173. }
  7174. return this.has(k) ? this.values[k] : dflt;
  7175. },
  7176. // Check whether we have a value for a key.
  7177. has: function(k) {
  7178. return k in this.values;
  7179. },
  7180. // Accept a setting if its one of the given alternatives.
  7181. alt: function(k, v, a) {
  7182. for (var n = 0; n < a.length; ++n) {
  7183. if (v === a[n]) {
  7184. this.set(k, v);
  7185. break;
  7186. }
  7187. }
  7188. },
  7189. // Accept a setting if its a valid (signed) integer.
  7190. integer: function(k, v) {
  7191. if (/^-?\d+$/.test(v)) { // integer
  7192. this.set(k, parseInt(v, 10));
  7193. }
  7194. },
  7195. // Accept a setting if its a valid percentage.
  7196. percent: function(k, v) {
  7197. var m;
  7198. if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
  7199. v = parseFloat(v);
  7200. if (v >= 0 && v <= 100) {
  7201. this.set(k, v);
  7202. return true;
  7203. }
  7204. }
  7205. return false;
  7206. }
  7207. };
  7208. // Helper function to parse input into groups separated by 'groupDelim', and
  7209. // interprete each group as a key/value pair separated by 'keyValueDelim'.
  7210. function parseOptions(input, callback, keyValueDelim, groupDelim) {
  7211. var groups = groupDelim ? input.split(groupDelim) : [input];
  7212. for (var i in groups) {
  7213. if (typeof groups[i] !== "string") {
  7214. continue;
  7215. }
  7216. var kv = groups[i].split(keyValueDelim);
  7217. if (kv.length !== 2) {
  7218. continue;
  7219. }
  7220. var k = kv[0];
  7221. var v = kv[1];
  7222. callback(k, v);
  7223. }
  7224. }
  7225. function parseCue(input, cue, regionList) {
  7226. // Remember the original input if we need to throw an error.
  7227. var oInput = input;
  7228. // 4.1 WebVTT timestamp
  7229. function consumeTimeStamp() {
  7230. var ts = parseTimeStamp(input);
  7231. if (ts === null) {
  7232. throw new ParsingError(ParsingError.Errors.BadTimeStamp,
  7233. "Malformed timestamp: " + oInput);
  7234. }
  7235. // Remove time stamp from input.
  7236. input = input.replace(/^[^\sa-zA-Z-]+/, "");
  7237. return ts;
  7238. }
  7239. // 4.4.2 WebVTT cue settings
  7240. function consumeCueSettings(input, cue) {
  7241. var settings = new Settings();
  7242. parseOptions(input, function (k, v) {
  7243. switch (k) {
  7244. case "region":
  7245. // Find the last region we parsed with the same region id.
  7246. for (var i = regionList.length - 1; i >= 0; i--) {
  7247. if (regionList[i].id === v) {
  7248. settings.set(k, regionList[i].region);
  7249. break;
  7250. }
  7251. }
  7252. break;
  7253. case "vertical":
  7254. settings.alt(k, v, ["rl", "lr"]);
  7255. break;
  7256. case "line":
  7257. var vals = v.split(","),
  7258. vals0 = vals[0];
  7259. settings.integer(k, vals0);
  7260. settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
  7261. settings.alt(k, vals0, ["auto"]);
  7262. if (vals.length === 2) {
  7263. settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
  7264. }
  7265. break;
  7266. case "position":
  7267. vals = v.split(",");
  7268. settings.percent(k, vals[0]);
  7269. if (vals.length === 2) {
  7270. settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
  7271. }
  7272. break;
  7273. case "size":
  7274. settings.percent(k, v);
  7275. break;
  7276. case "align":
  7277. settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
  7278. break;
  7279. }
  7280. }, /:/, /\s/);
  7281. // Apply default values for any missing fields.
  7282. cue.region = settings.get("region", null);
  7283. cue.vertical = settings.get("vertical", "");
  7284. cue.line = settings.get("line", "auto");
  7285. cue.lineAlign = settings.get("lineAlign", "start");
  7286. cue.snapToLines = settings.get("snapToLines", true);
  7287. cue.size = settings.get("size", 100);
  7288. cue.align = settings.get("align", "middle");
  7289. cue.position = settings.get("position", {
  7290. start: 0,
  7291. left: 0,
  7292. middle: 50,
  7293. end: 100,
  7294. right: 100
  7295. }, cue.align);
  7296. cue.positionAlign = settings.get("positionAlign", {
  7297. start: "start",
  7298. left: "start",
  7299. middle: "middle",
  7300. end: "end",
  7301. right: "end"
  7302. }, cue.align);
  7303. }
  7304. function skipWhitespace() {
  7305. input = input.replace(/^\s+/, "");
  7306. }
  7307. // 4.1 WebVTT cue timings.
  7308. skipWhitespace();
  7309. cue.startTime = consumeTimeStamp(); // (1) collect cue start time
  7310. skipWhitespace();
  7311. if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->"
  7312. throw new ParsingError(ParsingError.Errors.BadTimeStamp,
  7313. "Malformed time stamp (time stamps must be separated by '-->'): " +
  7314. oInput);
  7315. }
  7316. input = input.substr(3);
  7317. skipWhitespace();
  7318. cue.endTime = consumeTimeStamp(); // (5) collect cue end time
  7319. // 4.1 WebVTT cue settings list.
  7320. skipWhitespace();
  7321. consumeCueSettings(input, cue);
  7322. }
  7323. var ESCAPE = {
  7324. "&amp;": "&",
  7325. "&lt;": "<",
  7326. "&gt;": ">",
  7327. "&lrm;": "\u200e",
  7328. "&rlm;": "\u200f",
  7329. "&nbsp;": "\u00a0"
  7330. };
  7331. var TAG_NAME = {
  7332. c: "span",
  7333. i: "i",
  7334. b: "b",
  7335. u: "u",
  7336. ruby: "ruby",
  7337. rt: "rt",
  7338. v: "span",
  7339. lang: "span"
  7340. };
  7341. var TAG_ANNOTATION = {
  7342. v: "title",
  7343. lang: "lang"
  7344. };
  7345. var NEEDS_PARENT = {
  7346. rt: "ruby"
  7347. };
  7348. // Parse content into a document fragment.
  7349. function parseContent(window, input) {
  7350. function nextToken() {
  7351. // Check for end-of-string.
  7352. if (!input) {
  7353. return null;
  7354. }
  7355. // Consume 'n' characters from the input.
  7356. function consume(result) {
  7357. input = input.substr(result.length);
  7358. return result;
  7359. }
  7360. var m = input.match(/^([^<]*)(<[^>]*>?)?/);
  7361. // If there is some text before the next tag, return it, otherwise return
  7362. // the tag.
  7363. return consume(m[1] ? m[1] : m[2]);
  7364. }
  7365. // Unescape a string 's'.
  7366. function unescape1(e) {
  7367. return ESCAPE[e];
  7368. }
  7369. function unescape(s) {
  7370. while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
  7371. s = s.replace(m[0], unescape1);
  7372. }
  7373. return s;
  7374. }
  7375. function shouldAdd(current, element) {
  7376. return !NEEDS_PARENT[element.localName] ||
  7377. NEEDS_PARENT[element.localName] === current.localName;
  7378. }
  7379. // Create an element for this tag.
  7380. function createElement(type, annotation) {
  7381. var tagName = TAG_NAME[type];
  7382. if (!tagName) {
  7383. return null;
  7384. }
  7385. var element = window.document.createElement(tagName);
  7386. element.localName = tagName;
  7387. var name = TAG_ANNOTATION[type];
  7388. if (name && annotation) {
  7389. element[name] = annotation.trim();
  7390. }
  7391. return element;
  7392. }
  7393. var rootDiv = window.document.createElement("div"),
  7394. current = rootDiv,
  7395. t,
  7396. tagStack = [];
  7397. while ((t = nextToken()) !== null) {
  7398. if (t[0] === '<') {
  7399. if (t[1] === "/") {
  7400. // If the closing tag matches, move back up to the parent node.
  7401. if (tagStack.length &&
  7402. tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
  7403. tagStack.pop();
  7404. current = current.parentNode;
  7405. }
  7406. // Otherwise just ignore the end tag.
  7407. continue;
  7408. }
  7409. var ts = parseTimeStamp(t.substr(1, t.length - 2));
  7410. var node;
  7411. if (ts) {
  7412. // Timestamps are lead nodes as well.
  7413. node = window.document.createProcessingInstruction("timestamp", ts);
  7414. current.appendChild(node);
  7415. continue;
  7416. }
  7417. var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
  7418. // If we can't parse the tag, skip to the next tag.
  7419. if (!m) {
  7420. continue;
  7421. }
  7422. // Try to construct an element, and ignore the tag if we couldn't.
  7423. node = createElement(m[1], m[3]);
  7424. if (!node) {
  7425. continue;
  7426. }
  7427. // Determine if the tag should be added based on the context of where it
  7428. // is placed in the cuetext.
  7429. if (!shouldAdd(current, node)) {
  7430. continue;
  7431. }
  7432. // Set the class list (as a list of classes, separated by space).
  7433. if (m[2]) {
  7434. node.className = m[2].substr(1).replace('.', ' ');
  7435. }
  7436. // Append the node to the current node, and enter the scope of the new
  7437. // node.
  7438. tagStack.push(m[1]);
  7439. current.appendChild(node);
  7440. current = node;
  7441. continue;
  7442. }
  7443. // Text nodes are leaf nodes.
  7444. current.appendChild(window.document.createTextNode(unescape(t)));
  7445. }
  7446. return rootDiv;
  7447. }
  7448. // This is a list of all the Unicode characters that have a strong
  7449. // right-to-left category. What this means is that these characters are
  7450. // written right-to-left for sure. It was generated by pulling all the strong
  7451. // right-to-left characters out of the Unicode data table. That table can
  7452. // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
  7453. var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6],
  7454. [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d],
  7455. [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6],
  7456. [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5],
  7457. [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815],
  7458. [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858],
  7459. [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f],
  7460. [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c],
  7461. [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1],
  7462. [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc],
  7463. [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808],
  7464. [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855],
  7465. [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f],
  7466. [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13],
  7467. [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58],
  7468. [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72],
  7469. [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f],
  7470. [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32],
  7471. [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42],
  7472. [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f],
  7473. [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59],
  7474. [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62],
  7475. [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77],
  7476. [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b],
  7477. [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
  7478. function isStrongRTLChar(charCode) {
  7479. for (var i = 0; i < strongRTLRanges.length; i++) {
  7480. var currentRange = strongRTLRanges[i];
  7481. if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
  7482. return true;
  7483. }
  7484. }
  7485. return false;
  7486. }
  7487. function determineBidi(cueDiv) {
  7488. var nodeStack = [],
  7489. text = "",
  7490. charCode;
  7491. if (!cueDiv || !cueDiv.childNodes) {
  7492. return "ltr";
  7493. }
  7494. function pushNodes(nodeStack, node) {
  7495. for (var i = node.childNodes.length - 1; i >= 0; i--) {
  7496. nodeStack.push(node.childNodes[i]);
  7497. }
  7498. }
  7499. function nextTextNode(nodeStack) {
  7500. if (!nodeStack || !nodeStack.length) {
  7501. return null;
  7502. }
  7503. var node = nodeStack.pop(),
  7504. text = node.textContent || node.innerText;
  7505. if (text) {
  7506. // TODO: This should match all unicode type B characters (paragraph
  7507. // separator characters). See issue #115.
  7508. var m = text.match(/^.*(\n|\r)/);
  7509. if (m) {
  7510. nodeStack.length = 0;
  7511. return m[0];
  7512. }
  7513. return text;
  7514. }
  7515. if (node.tagName === "ruby") {
  7516. return nextTextNode(nodeStack);
  7517. }
  7518. if (node.childNodes) {
  7519. pushNodes(nodeStack, node);
  7520. return nextTextNode(nodeStack);
  7521. }
  7522. }
  7523. pushNodes(nodeStack, cueDiv);
  7524. while ((text = nextTextNode(nodeStack))) {
  7525. for (var i = 0; i < text.length; i++) {
  7526. charCode = text.charCodeAt(i);
  7527. if (isStrongRTLChar(charCode)) {
  7528. return "rtl";
  7529. }
  7530. }
  7531. }
  7532. return "ltr";
  7533. }
  7534. function computeLinePos(cue) {
  7535. if (typeof cue.line === "number" &&
  7536. (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
  7537. return cue.line;
  7538. }
  7539. if (!cue.track || !cue.track.textTrackList ||
  7540. !cue.track.textTrackList.mediaElement) {
  7541. return -1;
  7542. }
  7543. var track = cue.track,
  7544. trackList = track.textTrackList,
  7545. count = 0;
  7546. for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
  7547. if (trackList[i].mode === "showing") {
  7548. count++;
  7549. }
  7550. }
  7551. return ++count * -1;
  7552. }
  7553. function StyleBox() {
  7554. }
  7555. // Apply styles to a div. If there is no div passed then it defaults to the
  7556. // div on 'this'.
  7557. StyleBox.prototype.applyStyles = function(styles, div) {
  7558. div = div || this.div;
  7559. for (var prop in styles) {
  7560. if (styles.hasOwnProperty(prop)) {
  7561. div.style[prop] = styles[prop];
  7562. }
  7563. }
  7564. };
  7565. StyleBox.prototype.formatStyle = function(val, unit) {
  7566. return val === 0 ? 0 : val + unit;
  7567. };
  7568. // Constructs the computed display state of the cue (a div). Places the div
  7569. // into the overlay which should be a block level element (usually a div).
  7570. function CueStyleBox(window, cue, styleOptions) {
  7571. var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
  7572. var color = "rgba(255, 255, 255, 1)";
  7573. var backgroundColor = "rgba(0, 0, 0, 0.8)";
  7574. if (isIE8) {
  7575. color = "rgb(255, 255, 255)";
  7576. backgroundColor = "rgb(0, 0, 0)";
  7577. }
  7578. StyleBox.call(this);
  7579. this.cue = cue;
  7580. // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
  7581. // have inline positioning and will function as the cue background box.
  7582. this.cueDiv = parseContent(window, cue.text);
  7583. var styles = {
  7584. color: color,
  7585. backgroundColor: backgroundColor,
  7586. position: "relative",
  7587. left: 0,
  7588. right: 0,
  7589. top: 0,
  7590. bottom: 0,
  7591. display: "inline"
  7592. };
  7593. if (!isIE8) {
  7594. styles.writingMode = cue.vertical === "" ? "horizontal-tb"
  7595. : cue.vertical === "lr" ? "vertical-lr"
  7596. : "vertical-rl";
  7597. styles.unicodeBidi = "plaintext";
  7598. }
  7599. this.applyStyles(styles, this.cueDiv);
  7600. // Create an absolutely positioned div that will be used to position the cue
  7601. // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
  7602. // mirrors of them except "middle" which is "center" in CSS.
  7603. this.div = window.document.createElement("div");
  7604. styles = {
  7605. textAlign: cue.align === "middle" ? "center" : cue.align,
  7606. font: styleOptions.font,
  7607. whiteSpace: "pre-line",
  7608. position: "absolute"
  7609. };
  7610. if (!isIE8) {
  7611. styles.direction = determineBidi(this.cueDiv);
  7612. styles.writingMode = cue.vertical === "" ? "horizontal-tb"
  7613. : cue.vertical === "lr" ? "vertical-lr"
  7614. : "vertical-rl".
  7615. stylesunicodeBidi = "plaintext";
  7616. }
  7617. this.applyStyles(styles);
  7618. this.div.appendChild(this.cueDiv);
  7619. // Calculate the distance from the reference edge of the viewport to the text
  7620. // position of the cue box. The reference edge will be resolved later when
  7621. // the box orientation styles are applied.
  7622. var textPos = 0;
  7623. switch (cue.positionAlign) {
  7624. case "start":
  7625. textPos = cue.position;
  7626. break;
  7627. case "middle":
  7628. textPos = cue.position - (cue.size / 2);
  7629. break;
  7630. case "end":
  7631. textPos = cue.position - cue.size;
  7632. break;
  7633. }
  7634. // Horizontal box orientation; textPos is the distance from the left edge of the
  7635. // area to the left edge of the box and cue.size is the distance extending to
  7636. // the right from there.
  7637. if (cue.vertical === "") {
  7638. this.applyStyles({
  7639. left: this.formatStyle(textPos, "%"),
  7640. width: this.formatStyle(cue.size, "%")
  7641. });
  7642. // Vertical box orientation; textPos is the distance from the top edge of the
  7643. // area to the top edge of the box and cue.size is the height extending
  7644. // downwards from there.
  7645. } else {
  7646. this.applyStyles({
  7647. top: this.formatStyle(textPos, "%"),
  7648. height: this.formatStyle(cue.size, "%")
  7649. });
  7650. }
  7651. this.move = function(box) {
  7652. this.applyStyles({
  7653. top: this.formatStyle(box.top, "px"),
  7654. bottom: this.formatStyle(box.bottom, "px"),
  7655. left: this.formatStyle(box.left, "px"),
  7656. right: this.formatStyle(box.right, "px"),
  7657. height: this.formatStyle(box.height, "px"),
  7658. width: this.formatStyle(box.width, "px")
  7659. });
  7660. };
  7661. }
  7662. CueStyleBox.prototype = _objCreate(StyleBox.prototype);
  7663. CueStyleBox.prototype.constructor = CueStyleBox;
  7664. // Represents the co-ordinates of an Element in a way that we can easily
  7665. // compute things with such as if it overlaps or intersects with another Element.
  7666. // Can initialize it with either a StyleBox or another BoxPosition.
  7667. function BoxPosition(obj) {
  7668. var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
  7669. // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
  7670. // was passed in and we need to copy the results of 'getBoundingClientRect'
  7671. // as the object returned is readonly. All co-ordinate values are in reference
  7672. // to the viewport origin (top left).
  7673. var lh, height, width, top;
  7674. if (obj.div) {
  7675. height = obj.div.offsetHeight;
  7676. width = obj.div.offsetWidth;
  7677. top = obj.div.offsetTop;
  7678. var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
  7679. rects.getClientRects && rects.getClientRects();
  7680. obj = obj.div.getBoundingClientRect();
  7681. // In certain cases the outter div will be slightly larger then the sum of
  7682. // the inner div's lines. This could be due to bold text, etc, on some platforms.
  7683. // In this case we should get the average line height and use that. This will
  7684. // result in the desired behaviour.
  7685. lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
  7686. : 0;
  7687. }
  7688. this.left = obj.left;
  7689. this.right = obj.right;
  7690. this.top = obj.top || top;
  7691. this.height = obj.height || height;
  7692. this.bottom = obj.bottom || (top + (obj.height || height));
  7693. this.width = obj.width || width;
  7694. this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
  7695. if (isIE8 && !this.lineHeight) {
  7696. this.lineHeight = 13;
  7697. }
  7698. }
  7699. // Move the box along a particular axis. Optionally pass in an amount to move
  7700. // the box. If no amount is passed then the default is the line height of the
  7701. // box.
  7702. BoxPosition.prototype.move = function(axis, toMove) {
  7703. toMove = toMove !== undefined ? toMove : this.lineHeight;
  7704. switch (axis) {
  7705. case "+x":
  7706. this.left += toMove;
  7707. this.right += toMove;
  7708. break;
  7709. case "-x":
  7710. this.left -= toMove;
  7711. this.right -= toMove;
  7712. break;
  7713. case "+y":
  7714. this.top += toMove;
  7715. this.bottom += toMove;
  7716. break;
  7717. case "-y":
  7718. this.top -= toMove;
  7719. this.bottom -= toMove;
  7720. break;
  7721. }
  7722. };
  7723. // Check if this box overlaps another box, b2.
  7724. BoxPosition.prototype.overlaps = function(b2) {
  7725. return this.left < b2.right &&
  7726. this.right > b2.left &&
  7727. this.top < b2.bottom &&
  7728. this.bottom > b2.top;
  7729. };
  7730. // Check if this box overlaps any other boxes in boxes.
  7731. BoxPosition.prototype.overlapsAny = function(boxes) {
  7732. for (var i = 0; i < boxes.length; i++) {
  7733. if (this.overlaps(boxes[i])) {
  7734. return true;
  7735. }
  7736. }
  7737. return false;
  7738. };
  7739. // Check if this box is within another box.
  7740. BoxPosition.prototype.within = function(container) {
  7741. return this.top >= container.top &&
  7742. this.bottom <= container.bottom &&
  7743. this.left >= container.left &&
  7744. this.right <= container.right;
  7745. };
  7746. // Check if this box is entirely within the container or it is overlapping
  7747. // on the edge opposite of the axis direction passed. For example, if "+x" is
  7748. // passed and the box is overlapping on the left edge of the container, then
  7749. // return true.
  7750. BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
  7751. switch (axis) {
  7752. case "+x":
  7753. return this.left < container.left;
  7754. case "-x":
  7755. return this.right > container.right;
  7756. case "+y":
  7757. return this.top < container.top;
  7758. case "-y":
  7759. return this.bottom > container.bottom;
  7760. }
  7761. };
  7762. // Find the percentage of the area that this box is overlapping with another
  7763. // box.
  7764. BoxPosition.prototype.intersectPercentage = function(b2) {
  7765. var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
  7766. y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
  7767. intersectArea = x * y;
  7768. return intersectArea / (this.height * this.width);
  7769. };
  7770. // Convert the positions from this box to CSS compatible positions using
  7771. // the reference container's positions. This has to be done because this
  7772. // box's positions are in reference to the viewport origin, whereas, CSS
  7773. // values are in referecne to their respective edges.
  7774. BoxPosition.prototype.toCSSCompatValues = function(reference) {
  7775. return {
  7776. top: this.top - reference.top,
  7777. bottom: reference.bottom - this.bottom,
  7778. left: this.left - reference.left,
  7779. right: reference.right - this.right,
  7780. height: this.height,
  7781. width: this.width
  7782. };
  7783. };
  7784. // Get an object that represents the box's position without anything extra.
  7785. // Can pass a StyleBox, HTMLElement, or another BoxPositon.
  7786. BoxPosition.getSimpleBoxPosition = function(obj) {
  7787. var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
  7788. var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
  7789. var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
  7790. obj = obj.div ? obj.div.getBoundingClientRect() :
  7791. obj.tagName ? obj.getBoundingClientRect() : obj;
  7792. var ret = {
  7793. left: obj.left,
  7794. right: obj.right,
  7795. top: obj.top || top,
  7796. height: obj.height || height,
  7797. bottom: obj.bottom || (top + (obj.height || height)),
  7798. width: obj.width || width
  7799. };
  7800. return ret;
  7801. };
  7802. // Move a StyleBox to its specified, or next best, position. The containerBox
  7803. // is the box that contains the StyleBox, such as a div. boxPositions are
  7804. // a list of other boxes that the styleBox can't overlap with.
  7805. function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
  7806. // Find the best position for a cue box, b, on the video. The axis parameter
  7807. // is a list of axis, the order of which, it will move the box along. For example:
  7808. // Passing ["+x", "-x"] will move the box first along the x axis in the positive
  7809. // direction. If it doesn't find a good position for it there it will then move
  7810. // it along the x axis in the negative direction.
  7811. function findBestPosition(b, axis) {
  7812. var bestPosition,
  7813. specifiedPosition = new BoxPosition(b),
  7814. percentage = 1; // Highest possible so the first thing we get is better.
  7815. for (var i = 0; i < axis.length; i++) {
  7816. while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
  7817. (b.within(containerBox) && b.overlapsAny(boxPositions))) {
  7818. b.move(axis[i]);
  7819. }
  7820. // We found a spot where we aren't overlapping anything. This is our
  7821. // best position.
  7822. if (b.within(containerBox)) {
  7823. return b;
  7824. }
  7825. var p = b.intersectPercentage(containerBox);
  7826. // If we're outside the container box less then we were on our last try
  7827. // then remember this position as the best position.
  7828. if (percentage > p) {
  7829. bestPosition = new BoxPosition(b);
  7830. percentage = p;
  7831. }
  7832. // Reset the box position to the specified position.
  7833. b = new BoxPosition(specifiedPosition);
  7834. }
  7835. return bestPosition || specifiedPosition;
  7836. }
  7837. var boxPosition = new BoxPosition(styleBox),
  7838. cue = styleBox.cue,
  7839. linePos = computeLinePos(cue),
  7840. axis = [];
  7841. // If we have a line number to align the cue to.
  7842. if (cue.snapToLines) {
  7843. var size;
  7844. switch (cue.vertical) {
  7845. case "":
  7846. axis = [ "+y", "-y" ];
  7847. size = "height";
  7848. break;
  7849. case "rl":
  7850. axis = [ "+x", "-x" ];
  7851. size = "width";
  7852. break;
  7853. case "lr":
  7854. axis = [ "-x", "+x" ];
  7855. size = "width";
  7856. break;
  7857. }
  7858. var step = boxPosition.lineHeight,
  7859. position = step * Math.round(linePos),
  7860. maxPosition = containerBox[size] + step,
  7861. initialAxis = axis[0];
  7862. // If the specified intial position is greater then the max position then
  7863. // clamp the box to the amount of steps it would take for the box to
  7864. // reach the max position.
  7865. if (Math.abs(position) > maxPosition) {
  7866. position = position < 0 ? -1 : 1;
  7867. position *= Math.ceil(maxPosition / step) * step;
  7868. }
  7869. // If computed line position returns negative then line numbers are
  7870. // relative to the bottom of the video instead of the top. Therefore, we
  7871. // need to increase our initial position by the length or width of the
  7872. // video, depending on the writing direction, and reverse our axis directions.
  7873. if (linePos < 0) {
  7874. position += cue.vertical === "" ? containerBox.height : containerBox.width;
  7875. axis = axis.reverse();
  7876. }
  7877. // Move the box to the specified position. This may not be its best
  7878. // position.
  7879. boxPosition.move(initialAxis, position);
  7880. } else {
  7881. // If we have a percentage line value for the cue.
  7882. var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
  7883. switch (cue.lineAlign) {
  7884. case "middle":
  7885. linePos -= (calculatedPercentage / 2);
  7886. break;
  7887. case "end":
  7888. linePos -= calculatedPercentage;
  7889. break;
  7890. }
  7891. // Apply initial line position to the cue box.
  7892. switch (cue.vertical) {
  7893. case "":
  7894. styleBox.applyStyles({
  7895. top: styleBox.formatStyle(linePos, "%")
  7896. });
  7897. break;
  7898. case "rl":
  7899. styleBox.applyStyles({
  7900. left: styleBox.formatStyle(linePos, "%")
  7901. });
  7902. break;
  7903. case "lr":
  7904. styleBox.applyStyles({
  7905. right: styleBox.formatStyle(linePos, "%")
  7906. });
  7907. break;
  7908. }
  7909. axis = [ "+y", "-x", "+x", "-y" ];
  7910. // Get the box position again after we've applied the specified positioning
  7911. // to it.
  7912. boxPosition = new BoxPosition(styleBox);
  7913. }
  7914. var bestPosition = findBestPosition(boxPosition, axis);
  7915. styleBox.move(bestPosition.toCSSCompatValues(containerBox));
  7916. }
  7917. function WebVTT$1() {
  7918. // Nothing
  7919. }
  7920. // Helper to allow strings to be decoded instead of the default binary utf8 data.
  7921. WebVTT$1.StringDecoder = function() {
  7922. return {
  7923. decode: function(data) {
  7924. if (!data) {
  7925. return "";
  7926. }
  7927. if (typeof data !== "string") {
  7928. throw new Error("Error - expected string data.");
  7929. }
  7930. return decodeURIComponent(encodeURIComponent(data));
  7931. }
  7932. };
  7933. };
  7934. WebVTT$1.convertCueToDOMTree = function(window, cuetext) {
  7935. if (!window || !cuetext) {
  7936. return null;
  7937. }
  7938. return parseContent(window, cuetext);
  7939. };
  7940. var FONT_SIZE_PERCENT = 0.05;
  7941. var FONT_STYLE = "sans-serif";
  7942. var CUE_BACKGROUND_PADDING = "1.5%";
  7943. // Runs the processing model over the cues and regions passed to it.
  7944. // @param overlay A block level element (usually a div) that the computed cues
  7945. // and regions will be placed into.
  7946. WebVTT$1.processCues = function(window, cues, overlay) {
  7947. if (!window || !cues || !overlay) {
  7948. return null;
  7949. }
  7950. // Remove all previous children.
  7951. while (overlay.firstChild) {
  7952. overlay.removeChild(overlay.firstChild);
  7953. }
  7954. var paddedOverlay = window.document.createElement("div");
  7955. paddedOverlay.style.position = "absolute";
  7956. paddedOverlay.style.left = "0";
  7957. paddedOverlay.style.right = "0";
  7958. paddedOverlay.style.top = "0";
  7959. paddedOverlay.style.bottom = "0";
  7960. paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
  7961. overlay.appendChild(paddedOverlay);
  7962. // Determine if we need to compute the display states of the cues. This could
  7963. // be the case if a cue's state has been changed since the last computation or
  7964. // if it has not been computed yet.
  7965. function shouldCompute(cues) {
  7966. for (var i = 0; i < cues.length; i++) {
  7967. if (cues[i].hasBeenReset || !cues[i].displayState) {
  7968. return true;
  7969. }
  7970. }
  7971. return false;
  7972. }
  7973. // We don't need to recompute the cues' display states. Just reuse them.
  7974. if (!shouldCompute(cues)) {
  7975. for (var i = 0; i < cues.length; i++) {
  7976. paddedOverlay.appendChild(cues[i].displayState);
  7977. }
  7978. return;
  7979. }
  7980. var boxPositions = [],
  7981. containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
  7982. fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
  7983. var styleOptions = {
  7984. font: fontSize + "px " + FONT_STYLE
  7985. };
  7986. (function() {
  7987. var styleBox, cue;
  7988. for (var i = 0; i < cues.length; i++) {
  7989. cue = cues[i];
  7990. // Compute the intial position and styles of the cue div.
  7991. styleBox = new CueStyleBox(window, cue, styleOptions);
  7992. paddedOverlay.appendChild(styleBox.div);
  7993. // Move the cue div to it's correct line position.
  7994. moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
  7995. // Remember the computed div so that we don't have to recompute it later
  7996. // if we don't have too.
  7997. cue.displayState = styleBox.div;
  7998. boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
  7999. }
  8000. })();
  8001. };
  8002. WebVTT$1.Parser = function(window, vttjs, decoder) {
  8003. if (!decoder) {
  8004. decoder = vttjs;
  8005. vttjs = {};
  8006. }
  8007. if (!vttjs) {
  8008. vttjs = {};
  8009. }
  8010. this.window = window;
  8011. this.vttjs = vttjs;
  8012. this.state = "INITIAL";
  8013. this.buffer = "";
  8014. this.decoder = decoder || new TextDecoder("utf8");
  8015. this.regionList = [];
  8016. };
  8017. WebVTT$1.Parser.prototype = {
  8018. // If the error is a ParsingError then report it to the consumer if
  8019. // possible. If it's not a ParsingError then throw it like normal.
  8020. reportOrThrowError: function(e) {
  8021. if (e instanceof ParsingError) {
  8022. this.onparsingerror && this.onparsingerror(e);
  8023. } else {
  8024. throw e;
  8025. }
  8026. },
  8027. parse: function (data) {
  8028. var self = this;
  8029. // If there is no data then we won't decode it, but will just try to parse
  8030. // whatever is in buffer already. This may occur in circumstances, for
  8031. // example when flush() is called.
  8032. if (data) {
  8033. // Try to decode the data that we received.
  8034. self.buffer += self.decoder.decode(data, {stream: true});
  8035. }
  8036. function collectNextLine() {
  8037. var buffer = self.buffer;
  8038. var pos = 0;
  8039. while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
  8040. ++pos;
  8041. }
  8042. var line = buffer.substr(0, pos);
  8043. // Advance the buffer early in case we fail below.
  8044. if (buffer[pos] === '\r') {
  8045. ++pos;
  8046. }
  8047. if (buffer[pos] === '\n') {
  8048. ++pos;
  8049. }
  8050. self.buffer = buffer.substr(pos);
  8051. return line;
  8052. }
  8053. // 3.4 WebVTT region and WebVTT region settings syntax
  8054. function parseRegion(input) {
  8055. var settings = new Settings();
  8056. parseOptions(input, function (k, v) {
  8057. switch (k) {
  8058. case "id":
  8059. settings.set(k, v);
  8060. break;
  8061. case "width":
  8062. settings.percent(k, v);
  8063. break;
  8064. case "lines":
  8065. settings.integer(k, v);
  8066. break;
  8067. case "regionanchor":
  8068. case "viewportanchor":
  8069. var xy = v.split(',');
  8070. if (xy.length !== 2) {
  8071. break;
  8072. }
  8073. // We have to make sure both x and y parse, so use a temporary
  8074. // settings object here.
  8075. var anchor = new Settings();
  8076. anchor.percent("x", xy[0]);
  8077. anchor.percent("y", xy[1]);
  8078. if (!anchor.has("x") || !anchor.has("y")) {
  8079. break;
  8080. }
  8081. settings.set(k + "X", anchor.get("x"));
  8082. settings.set(k + "Y", anchor.get("y"));
  8083. break;
  8084. case "scroll":
  8085. settings.alt(k, v, ["up"]);
  8086. break;
  8087. }
  8088. }, /=/, /\s/);
  8089. // Create the region, using default values for any values that were not
  8090. // specified.
  8091. if (settings.has("id")) {
  8092. var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
  8093. region.width = settings.get("width", 100);
  8094. region.lines = settings.get("lines", 3);
  8095. region.regionAnchorX = settings.get("regionanchorX", 0);
  8096. region.regionAnchorY = settings.get("regionanchorY", 100);
  8097. region.viewportAnchorX = settings.get("viewportanchorX", 0);
  8098. region.viewportAnchorY = settings.get("viewportanchorY", 100);
  8099. region.scroll = settings.get("scroll", "");
  8100. // Register the region.
  8101. self.onregion && self.onregion(region);
  8102. // Remember the VTTRegion for later in case we parse any VTTCues that
  8103. // reference it.
  8104. self.regionList.push({
  8105. id: settings.get("id"),
  8106. region: region
  8107. });
  8108. }
  8109. }
  8110. // draft-pantos-http-live-streaming-20
  8111. // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
  8112. // 3.5 WebVTT
  8113. function parseTimestampMap(input) {
  8114. var settings = new Settings();
  8115. parseOptions(input, function(k, v) {
  8116. switch(k) {
  8117. case "MPEGT":
  8118. settings.integer(k + 'S', v);
  8119. break;
  8120. case "LOCA":
  8121. settings.set(k + 'L', parseTimeStamp(v));
  8122. break;
  8123. }
  8124. }, /[^\d]:/, /,/);
  8125. self.ontimestampmap && self.ontimestampmap({
  8126. "MPEGTS": settings.get("MPEGTS"),
  8127. "LOCAL": settings.get("LOCAL")
  8128. });
  8129. }
  8130. // 3.2 WebVTT metadata header syntax
  8131. function parseHeader(input) {
  8132. if (input.match(/X-TIMESTAMP-MAP/)) {
  8133. // This line contains HLS X-TIMESTAMP-MAP metadata
  8134. parseOptions(input, function(k, v) {
  8135. switch(k) {
  8136. case "X-TIMESTAMP-MAP":
  8137. parseTimestampMap(v);
  8138. break;
  8139. }
  8140. }, /=/);
  8141. } else {
  8142. parseOptions(input, function (k, v) {
  8143. switch (k) {
  8144. case "Region":
  8145. // 3.3 WebVTT region metadata header syntax
  8146. parseRegion(v);
  8147. break;
  8148. }
  8149. }, /:/);
  8150. }
  8151. }
  8152. // 5.1 WebVTT file parsing.
  8153. try {
  8154. var line;
  8155. if (self.state === "INITIAL") {
  8156. // We can't start parsing until we have the first line.
  8157. if (!/\r\n|\n/.test(self.buffer)) {
  8158. return this;
  8159. }
  8160. line = collectNextLine();
  8161. var m = line.match(/^WEBVTT([ \t].*)?$/);
  8162. if (!m || !m[0]) {
  8163. throw new ParsingError(ParsingError.Errors.BadSignature);
  8164. }
  8165. self.state = "HEADER";
  8166. }
  8167. var alreadyCollectedLine = false;
  8168. while (self.buffer) {
  8169. // We can't parse a line until we have the full line.
  8170. if (!/\r\n|\n/.test(self.buffer)) {
  8171. return this;
  8172. }
  8173. if (!alreadyCollectedLine) {
  8174. line = collectNextLine();
  8175. } else {
  8176. alreadyCollectedLine = false;
  8177. }
  8178. switch (self.state) {
  8179. case "HEADER":
  8180. // 13-18 - Allow a header (metadata) under the WEBVTT line.
  8181. if (/:/.test(line)) {
  8182. parseHeader(line);
  8183. } else if (!line) {
  8184. // An empty line terminates the header and starts the body (cues).
  8185. self.state = "ID";
  8186. }
  8187. continue;
  8188. case "NOTE":
  8189. // Ignore NOTE blocks.
  8190. if (!line) {
  8191. self.state = "ID";
  8192. }
  8193. continue;
  8194. case "ID":
  8195. // Check for the start of NOTE blocks.
  8196. if (/^NOTE($|[ \t])/.test(line)) {
  8197. self.state = "NOTE";
  8198. break;
  8199. }
  8200. // 19-29 - Allow any number of line terminators, then initialize new cue values.
  8201. if (!line) {
  8202. continue;
  8203. }
  8204. self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
  8205. self.state = "CUE";
  8206. // 30-39 - Check if self line contains an optional identifier or timing data.
  8207. if (line.indexOf("-->") === -1) {
  8208. self.cue.id = line;
  8209. continue;
  8210. }
  8211. // Process line as start of a cue.
  8212. /*falls through*/
  8213. case "CUE":
  8214. // 40 - Collect cue timings and settings.
  8215. try {
  8216. parseCue(line, self.cue, self.regionList);
  8217. } catch (e) {
  8218. self.reportOrThrowError(e);
  8219. // In case of an error ignore rest of the cue.
  8220. self.cue = null;
  8221. self.state = "BADCUE";
  8222. continue;
  8223. }
  8224. self.state = "CUETEXT";
  8225. continue;
  8226. case "CUETEXT":
  8227. var hasSubstring = line.indexOf("-->") !== -1;
  8228. // 34 - If we have an empty line then report the cue.
  8229. // 35 - If we have the special substring '-->' then report the cue,
  8230. // but do not collect the line as we need to process the current
  8231. // one as a new cue.
  8232. if (!line || hasSubstring && (alreadyCollectedLine = true)) {
  8233. // We are done parsing self cue.
  8234. self.oncue && self.oncue(self.cue);
  8235. self.cue = null;
  8236. self.state = "ID";
  8237. continue;
  8238. }
  8239. if (self.cue.text) {
  8240. self.cue.text += "\n";
  8241. }
  8242. self.cue.text += line;
  8243. continue;
  8244. case "BADCUE": // BADCUE
  8245. // 54-62 - Collect and discard the remaining cue.
  8246. if (!line) {
  8247. self.state = "ID";
  8248. }
  8249. continue;
  8250. }
  8251. }
  8252. } catch (e) {
  8253. self.reportOrThrowError(e);
  8254. // If we are currently parsing a cue, report what we have.
  8255. if (self.state === "CUETEXT" && self.cue && self.oncue) {
  8256. self.oncue(self.cue);
  8257. }
  8258. self.cue = null;
  8259. // Enter BADWEBVTT state if header was not parsed correctly otherwise
  8260. // another exception occurred so enter BADCUE state.
  8261. self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
  8262. }
  8263. return this;
  8264. },
  8265. flush: function () {
  8266. var self = this;
  8267. try {
  8268. // Finish decoding the stream.
  8269. self.buffer += self.decoder.decode();
  8270. // Synthesize the end of the current cue or region.
  8271. if (self.cue || self.state === "HEADER") {
  8272. self.buffer += "\n\n";
  8273. self.parse();
  8274. }
  8275. // If we've flushed, parsed, and we're still on the INITIAL state then
  8276. // that means we don't have enough of the stream to parse the first
  8277. // line.
  8278. if (self.state === "INITIAL") {
  8279. throw new ParsingError(ParsingError.Errors.BadSignature);
  8280. }
  8281. } catch(e) {
  8282. self.reportOrThrowError(e);
  8283. }
  8284. self.onflush && self.onflush();
  8285. return this;
  8286. }
  8287. };
  8288. var vtt$1 = WebVTT$1;
  8289. /**
  8290. * Copyright 2013 vtt.js Contributors
  8291. *
  8292. * Licensed under the Apache License, Version 2.0 (the "License");
  8293. * you may not use this file except in compliance with the License.
  8294. * You may obtain a copy of the License at
  8295. *
  8296. * http://www.apache.org/licenses/LICENSE-2.0
  8297. *
  8298. * Unless required by applicable law or agreed to in writing, software
  8299. * distributed under the License is distributed on an "AS IS" BASIS,
  8300. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  8301. * See the License for the specific language governing permissions and
  8302. * limitations under the License.
  8303. */
  8304. var autoKeyword = "auto";
  8305. var directionSetting = {
  8306. "": true,
  8307. "lr": true,
  8308. "rl": true
  8309. };
  8310. var alignSetting = {
  8311. "start": true,
  8312. "middle": true,
  8313. "end": true,
  8314. "left": true,
  8315. "right": true
  8316. };
  8317. function findDirectionSetting(value) {
  8318. if (typeof value !== "string") {
  8319. return false;
  8320. }
  8321. var dir = directionSetting[value.toLowerCase()];
  8322. return dir ? value.toLowerCase() : false;
  8323. }
  8324. function findAlignSetting(value) {
  8325. if (typeof value !== "string") {
  8326. return false;
  8327. }
  8328. var align = alignSetting[value.toLowerCase()];
  8329. return align ? value.toLowerCase() : false;
  8330. }
  8331. function extend$1(obj) {
  8332. var i = 1;
  8333. for (; i < arguments.length; i++) {
  8334. var cobj = arguments[i];
  8335. for (var p in cobj) {
  8336. obj[p] = cobj[p];
  8337. }
  8338. }
  8339. return obj;
  8340. }
  8341. function VTTCue(startTime, endTime, text) {
  8342. var cue = this;
  8343. var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
  8344. var baseObj = {};
  8345. if (isIE8) {
  8346. cue = document.createElement('custom');
  8347. } else {
  8348. baseObj.enumerable = true;
  8349. }
  8350. /**
  8351. * Shim implementation specific properties. These properties are not in
  8352. * the spec.
  8353. */
  8354. // Lets us know when the VTTCue's data has changed in such a way that we need
  8355. // to recompute its display state. This lets us compute its display state
  8356. // lazily.
  8357. cue.hasBeenReset = false;
  8358. /**
  8359. * VTTCue and TextTrackCue properties
  8360. * http://dev.w3.org/html5/webvtt/#vttcue-interface
  8361. */
  8362. var _id = "";
  8363. var _pauseOnExit = false;
  8364. var _startTime = startTime;
  8365. var _endTime = endTime;
  8366. var _text = text;
  8367. var _region = null;
  8368. var _vertical = "";
  8369. var _snapToLines = true;
  8370. var _line = "auto";
  8371. var _lineAlign = "start";
  8372. var _position = 50;
  8373. var _positionAlign = "middle";
  8374. var _size = 50;
  8375. var _align = "middle";
  8376. Object.defineProperty(cue,
  8377. "id", extend$1({}, baseObj, {
  8378. get: function() {
  8379. return _id;
  8380. },
  8381. set: function(value) {
  8382. _id = "" + value;
  8383. }
  8384. }));
  8385. Object.defineProperty(cue,
  8386. "pauseOnExit", extend$1({}, baseObj, {
  8387. get: function() {
  8388. return _pauseOnExit;
  8389. },
  8390. set: function(value) {
  8391. _pauseOnExit = !!value;
  8392. }
  8393. }));
  8394. Object.defineProperty(cue,
  8395. "startTime", extend$1({}, baseObj, {
  8396. get: function() {
  8397. return _startTime;
  8398. },
  8399. set: function(value) {
  8400. if (typeof value !== "number") {
  8401. throw new TypeError("Start time must be set to a number.");
  8402. }
  8403. _startTime = value;
  8404. this.hasBeenReset = true;
  8405. }
  8406. }));
  8407. Object.defineProperty(cue,
  8408. "endTime", extend$1({}, baseObj, {
  8409. get: function() {
  8410. return _endTime;
  8411. },
  8412. set: function(value) {
  8413. if (typeof value !== "number") {
  8414. throw new TypeError("End time must be set to a number.");
  8415. }
  8416. _endTime = value;
  8417. this.hasBeenReset = true;
  8418. }
  8419. }));
  8420. Object.defineProperty(cue,
  8421. "text", extend$1({}, baseObj, {
  8422. get: function() {
  8423. return _text;
  8424. },
  8425. set: function(value) {
  8426. _text = "" + value;
  8427. this.hasBeenReset = true;
  8428. }
  8429. }));
  8430. Object.defineProperty(cue,
  8431. "region", extend$1({}, baseObj, {
  8432. get: function() {
  8433. return _region;
  8434. },
  8435. set: function(value) {
  8436. _region = value;
  8437. this.hasBeenReset = true;
  8438. }
  8439. }));
  8440. Object.defineProperty(cue,
  8441. "vertical", extend$1({}, baseObj, {
  8442. get: function() {
  8443. return _vertical;
  8444. },
  8445. set: function(value) {
  8446. var setting = findDirectionSetting(value);
  8447. // Have to check for false because the setting an be an empty string.
  8448. if (setting === false) {
  8449. throw new SyntaxError("An invalid or illegal string was specified.");
  8450. }
  8451. _vertical = setting;
  8452. this.hasBeenReset = true;
  8453. }
  8454. }));
  8455. Object.defineProperty(cue,
  8456. "snapToLines", extend$1({}, baseObj, {
  8457. get: function() {
  8458. return _snapToLines;
  8459. },
  8460. set: function(value) {
  8461. _snapToLines = !!value;
  8462. this.hasBeenReset = true;
  8463. }
  8464. }));
  8465. Object.defineProperty(cue,
  8466. "line", extend$1({}, baseObj, {
  8467. get: function() {
  8468. return _line;
  8469. },
  8470. set: function(value) {
  8471. if (typeof value !== "number" && value !== autoKeyword) {
  8472. throw new SyntaxError("An invalid number or illegal string was specified.");
  8473. }
  8474. _line = value;
  8475. this.hasBeenReset = true;
  8476. }
  8477. }));
  8478. Object.defineProperty(cue,
  8479. "lineAlign", extend$1({}, baseObj, {
  8480. get: function() {
  8481. return _lineAlign;
  8482. },
  8483. set: function(value) {
  8484. var setting = findAlignSetting(value);
  8485. if (!setting) {
  8486. throw new SyntaxError("An invalid or illegal string was specified.");
  8487. }
  8488. _lineAlign = setting;
  8489. this.hasBeenReset = true;
  8490. }
  8491. }));
  8492. Object.defineProperty(cue,
  8493. "position", extend$1({}, baseObj, {
  8494. get: function() {
  8495. return _position;
  8496. },
  8497. set: function(value) {
  8498. if (value < 0 || value > 100) {
  8499. throw new Error("Position must be between 0 and 100.");
  8500. }
  8501. _position = value;
  8502. this.hasBeenReset = true;
  8503. }
  8504. }));
  8505. Object.defineProperty(cue,
  8506. "positionAlign", extend$1({}, baseObj, {
  8507. get: function() {
  8508. return _positionAlign;
  8509. },
  8510. set: function(value) {
  8511. var setting = findAlignSetting(value);
  8512. if (!setting) {
  8513. throw new SyntaxError("An invalid or illegal string was specified.");
  8514. }
  8515. _positionAlign = setting;
  8516. this.hasBeenReset = true;
  8517. }
  8518. }));
  8519. Object.defineProperty(cue,
  8520. "size", extend$1({}, baseObj, {
  8521. get: function() {
  8522. return _size;
  8523. },
  8524. set: function(value) {
  8525. if (value < 0 || value > 100) {
  8526. throw new Error("Size must be between 0 and 100.");
  8527. }
  8528. _size = value;
  8529. this.hasBeenReset = true;
  8530. }
  8531. }));
  8532. Object.defineProperty(cue,
  8533. "align", extend$1({}, baseObj, {
  8534. get: function() {
  8535. return _align;
  8536. },
  8537. set: function(value) {
  8538. var setting = findAlignSetting(value);
  8539. if (!setting) {
  8540. throw new SyntaxError("An invalid or illegal string was specified.");
  8541. }
  8542. _align = setting;
  8543. this.hasBeenReset = true;
  8544. }
  8545. }));
  8546. /**
  8547. * Other <track> spec defined properties
  8548. */
  8549. // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
  8550. cue.displayState = undefined;
  8551. if (isIE8) {
  8552. return cue;
  8553. }
  8554. }
  8555. /**
  8556. * VTTCue methods
  8557. */
  8558. VTTCue.prototype.getCueAsHTML = function() {
  8559. // Assume WebVTT.convertCueToDOMTree is on the global.
  8560. return WebVTT.convertCueToDOMTree(window, this.text);
  8561. };
  8562. var vttcue = VTTCue;
  8563. /**
  8564. * Copyright 2013 vtt.js Contributors
  8565. *
  8566. * Licensed under the Apache License, Version 2.0 (the "License");
  8567. * you may not use this file except in compliance with the License.
  8568. * You may obtain a copy of the License at
  8569. *
  8570. * http://www.apache.org/licenses/LICENSE-2.0
  8571. *
  8572. * Unless required by applicable law or agreed to in writing, software
  8573. * distributed under the License is distributed on an "AS IS" BASIS,
  8574. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  8575. * See the License for the specific language governing permissions and
  8576. * limitations under the License.
  8577. */
  8578. var scrollSetting = {
  8579. "": true,
  8580. "up": true
  8581. };
  8582. function findScrollSetting(value) {
  8583. if (typeof value !== "string") {
  8584. return false;
  8585. }
  8586. var scroll = scrollSetting[value.toLowerCase()];
  8587. return scroll ? value.toLowerCase() : false;
  8588. }
  8589. function isValidPercentValue(value) {
  8590. return typeof value === "number" && (value >= 0 && value <= 100);
  8591. }
  8592. // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
  8593. function VTTRegion() {
  8594. var _width = 100;
  8595. var _lines = 3;
  8596. var _regionAnchorX = 0;
  8597. var _regionAnchorY = 100;
  8598. var _viewportAnchorX = 0;
  8599. var _viewportAnchorY = 100;
  8600. var _scroll = "";
  8601. Object.defineProperties(this, {
  8602. "width": {
  8603. enumerable: true,
  8604. get: function() {
  8605. return _width;
  8606. },
  8607. set: function(value) {
  8608. if (!isValidPercentValue(value)) {
  8609. throw new Error("Width must be between 0 and 100.");
  8610. }
  8611. _width = value;
  8612. }
  8613. },
  8614. "lines": {
  8615. enumerable: true,
  8616. get: function() {
  8617. return _lines;
  8618. },
  8619. set: function(value) {
  8620. if (typeof value !== "number") {
  8621. throw new TypeError("Lines must be set to a number.");
  8622. }
  8623. _lines = value;
  8624. }
  8625. },
  8626. "regionAnchorY": {
  8627. enumerable: true,
  8628. get: function() {
  8629. return _regionAnchorY;
  8630. },
  8631. set: function(value) {
  8632. if (!isValidPercentValue(value)) {
  8633. throw new Error("RegionAnchorX must be between 0 and 100.");
  8634. }
  8635. _regionAnchorY = value;
  8636. }
  8637. },
  8638. "regionAnchorX": {
  8639. enumerable: true,
  8640. get: function() {
  8641. return _regionAnchorX;
  8642. },
  8643. set: function(value) {
  8644. if(!isValidPercentValue(value)) {
  8645. throw new Error("RegionAnchorY must be between 0 and 100.");
  8646. }
  8647. _regionAnchorX = value;
  8648. }
  8649. },
  8650. "viewportAnchorY": {
  8651. enumerable: true,
  8652. get: function() {
  8653. return _viewportAnchorY;
  8654. },
  8655. set: function(value) {
  8656. if (!isValidPercentValue(value)) {
  8657. throw new Error("ViewportAnchorY must be between 0 and 100.");
  8658. }
  8659. _viewportAnchorY = value;
  8660. }
  8661. },
  8662. "viewportAnchorX": {
  8663. enumerable: true,
  8664. get: function() {
  8665. return _viewportAnchorX;
  8666. },
  8667. set: function(value) {
  8668. if (!isValidPercentValue(value)) {
  8669. throw new Error("ViewportAnchorX must be between 0 and 100.");
  8670. }
  8671. _viewportAnchorX = value;
  8672. }
  8673. },
  8674. "scroll": {
  8675. enumerable: true,
  8676. get: function() {
  8677. return _scroll;
  8678. },
  8679. set: function(value) {
  8680. var setting = findScrollSetting(value);
  8681. // Have to check for false as an empty string is a legal value.
  8682. if (setting === false) {
  8683. throw new SyntaxError("An invalid or illegal string was specified.");
  8684. }
  8685. _scroll = setting;
  8686. }
  8687. }
  8688. });
  8689. }
  8690. var vttregion = VTTRegion;
  8691. var browserIndex = createCommonjsModule(function (module) {
  8692. /**
  8693. * Copyright 2013 vtt.js Contributors
  8694. *
  8695. * Licensed under the Apache License, Version 2.0 (the "License");
  8696. * you may not use this file except in compliance with the License.
  8697. * You may obtain a copy of the License at
  8698. *
  8699. * http://www.apache.org/licenses/LICENSE-2.0
  8700. *
  8701. * Unless required by applicable law or agreed to in writing, software
  8702. * distributed under the License is distributed on an "AS IS" BASIS,
  8703. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  8704. * See the License for the specific language governing permissions and
  8705. * limitations under the License.
  8706. */
  8707. // Default exports for Node. Export the extended versions of VTTCue and
  8708. // VTTRegion in Node since we likely want the capability to convert back and
  8709. // forth between JSON. If we don't then it's not that big of a deal since we're
  8710. // off browser.
  8711. var vttjs = module.exports = {
  8712. WebVTT: vtt$1,
  8713. VTTCue: vttcue,
  8714. VTTRegion: vttregion
  8715. };
  8716. window_1.vttjs = vttjs;
  8717. window_1.WebVTT = vttjs.WebVTT;
  8718. var cueShim = vttjs.VTTCue;
  8719. var regionShim = vttjs.VTTRegion;
  8720. var nativeVTTCue = window_1.VTTCue;
  8721. var nativeVTTRegion = window_1.VTTRegion;
  8722. vttjs.shim = function() {
  8723. window_1.VTTCue = cueShim;
  8724. window_1.VTTRegion = regionShim;
  8725. };
  8726. vttjs.restore = function() {
  8727. window_1.VTTCue = nativeVTTCue;
  8728. window_1.VTTRegion = nativeVTTRegion;
  8729. };
  8730. if (!window_1.VTTCue) {
  8731. vttjs.shim();
  8732. }
  8733. });
  8734. /**
  8735. * @file tech.js
  8736. */
  8737. /**
  8738. * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
  8739. * that just contains the src url alone.
  8740. * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
  8741. * `var SourceString = 'http://example.com/some-video.mp4';`
  8742. *
  8743. * @typedef {Object|string} Tech~SourceObject
  8744. *
  8745. * @property {string} src
  8746. * The url to the source
  8747. *
  8748. * @property {string} type
  8749. * The mime type of the source
  8750. */
  8751. /**
  8752. * A function used by {@link Tech} to create a new {@link TextTrack}.
  8753. *
  8754. * @private
  8755. *
  8756. * @param {Tech} self
  8757. * An instance of the Tech class.
  8758. *
  8759. * @param {string} kind
  8760. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  8761. *
  8762. * @param {string} [label]
  8763. * Label to identify the text track
  8764. *
  8765. * @param {string} [language]
  8766. * Two letter language abbreviation
  8767. *
  8768. * @param {Object} [options={}]
  8769. * An object with additional text track options
  8770. *
  8771. * @return {TextTrack}
  8772. * The text track that was created.
  8773. */
  8774. function createTrackHelper(self, kind, label, language) {
  8775. var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
  8776. var tracks = self.textTracks();
  8777. options.kind = kind;
  8778. if (label) {
  8779. options.label = label;
  8780. }
  8781. if (language) {
  8782. options.language = language;
  8783. }
  8784. options.tech = self;
  8785. var track = new ALL.text.TrackClass(options);
  8786. tracks.addTrack(track);
  8787. return track;
  8788. }
  8789. /**
  8790. * This is the base class for media playback technology controllers, such as
  8791. * {@link Flash} and {@link HTML5}
  8792. *
  8793. * @extends Component
  8794. */
  8795. var Tech = function (_Component) {
  8796. inherits(Tech, _Component);
  8797. /**
  8798. * Create an instance of this Tech.
  8799. *
  8800. * @param {Object} [options]
  8801. * The key/value store of player options.
  8802. *
  8803. * @param {Component~ReadyCallback} ready
  8804. * Callback function to call when the `HTML5` Tech is ready.
  8805. */
  8806. function Tech() {
  8807. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  8808. var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
  8809. classCallCheck(this, Tech);
  8810. // we don't want the tech to report user activity automatically.
  8811. // This is done manually in addControlsListeners
  8812. options.reportTouchActivity = false;
  8813. // keep track of whether the current source has played at all to
  8814. // implement a very limited played()
  8815. var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
  8816. _this.hasStarted_ = false;
  8817. _this.on('playing', function () {
  8818. this.hasStarted_ = true;
  8819. });
  8820. _this.on('loadstart', function () {
  8821. this.hasStarted_ = false;
  8822. });
  8823. ALL.names.forEach(function (name) {
  8824. var props = ALL[name];
  8825. if (options && options[props.getterName]) {
  8826. _this[props.privateName] = options[props.getterName];
  8827. }
  8828. });
  8829. // Manually track progress in cases where the browser/flash player doesn't report it.
  8830. if (!_this.featuresProgressEvents) {
  8831. _this.manualProgressOn();
  8832. }
  8833. // Manually track timeupdates in cases where the browser/flash player doesn't report it.
  8834. if (!_this.featuresTimeupdateEvents) {
  8835. _this.manualTimeUpdatesOn();
  8836. }
  8837. ['Text', 'Audio', 'Video'].forEach(function (track) {
  8838. if (options['native' + track + 'Tracks'] === false) {
  8839. _this['featuresNative' + track + 'Tracks'] = false;
  8840. }
  8841. });
  8842. if (options.nativeCaptions === false || options.nativeTextTracks === false) {
  8843. _this.featuresNativeTextTracks = false;
  8844. } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
  8845. _this.featuresNativeTextTracks = true;
  8846. }
  8847. if (!_this.featuresNativeTextTracks) {
  8848. _this.emulateTextTracks();
  8849. }
  8850. _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
  8851. _this.initTrackListeners();
  8852. // Turn on component tap events only if not using native controls
  8853. if (!options.nativeControlsForTouch) {
  8854. _this.emitTapEvents();
  8855. }
  8856. if (_this.constructor) {
  8857. _this.name_ = _this.constructor.name || 'Unknown Tech';
  8858. }
  8859. return _this;
  8860. }
  8861. /**
  8862. * A special function to trigger source set in a way that will allow player
  8863. * to re-trigger if the player or tech are not ready yet.
  8864. *
  8865. * @fires Tech#sourceset
  8866. * @param {string} src The source string at the time of the source changing.
  8867. */
  8868. Tech.prototype.triggerSourceset = function triggerSourceset(src) {
  8869. var _this2 = this;
  8870. if (!this.isReady_) {
  8871. // on initial ready we have to trigger source set
  8872. // 1ms after ready so that player can watch for it.
  8873. this.one('ready', function () {
  8874. return _this2.setTimeout(function () {
  8875. return _this2.triggerSourceset(src);
  8876. }, 1);
  8877. });
  8878. }
  8879. /**
  8880. * Fired when the source is set on the tech causing the media element
  8881. * to reload.
  8882. *
  8883. * @see {@link Player#event:sourceset}
  8884. * @event Tech#sourceset
  8885. * @type {EventTarget~Event}
  8886. */
  8887. this.trigger({
  8888. src: src,
  8889. type: 'sourceset'
  8890. });
  8891. };
  8892. /* Fallbacks for unsupported event types
  8893. ================================================================================ */
  8894. /**
  8895. * Polyfill the `progress` event for browsers that don't support it natively.
  8896. *
  8897. * @see {@link Tech#trackProgress}
  8898. */
  8899. Tech.prototype.manualProgressOn = function manualProgressOn() {
  8900. this.on('durationchange', this.onDurationChange);
  8901. this.manualProgress = true;
  8902. // Trigger progress watching when a source begins loading
  8903. this.one('ready', this.trackProgress);
  8904. };
  8905. /**
  8906. * Turn off the polyfill for `progress` events that was created in
  8907. * {@link Tech#manualProgressOn}
  8908. */
  8909. Tech.prototype.manualProgressOff = function manualProgressOff() {
  8910. this.manualProgress = false;
  8911. this.stopTrackingProgress();
  8912. this.off('durationchange', this.onDurationChange);
  8913. };
  8914. /**
  8915. * This is used to trigger a `progress` event when the buffered percent changes. It
  8916. * sets an interval function that will be called every 500 milliseconds to check if the
  8917. * buffer end percent has changed.
  8918. *
  8919. * > This function is called by {@link Tech#manualProgressOn}
  8920. *
  8921. * @param {EventTarget~Event} event
  8922. * The `ready` event that caused this to run.
  8923. *
  8924. * @listens Tech#ready
  8925. * @fires Tech#progress
  8926. */
  8927. Tech.prototype.trackProgress = function trackProgress(event) {
  8928. this.stopTrackingProgress();
  8929. this.progressInterval = this.setInterval(bind(this, function () {
  8930. // Don't trigger unless buffered amount is greater than last time
  8931. var numBufferedPercent = this.bufferedPercent();
  8932. if (this.bufferedPercent_ !== numBufferedPercent) {
  8933. /**
  8934. * See {@link Player#progress}
  8935. *
  8936. * @event Tech#progress
  8937. * @type {EventTarget~Event}
  8938. */
  8939. this.trigger('progress');
  8940. }
  8941. this.bufferedPercent_ = numBufferedPercent;
  8942. if (numBufferedPercent === 1) {
  8943. this.stopTrackingProgress();
  8944. }
  8945. }), 500);
  8946. };
  8947. /**
  8948. * Update our internal duration on a `durationchange` event by calling
  8949. * {@link Tech#duration}.
  8950. *
  8951. * @param {EventTarget~Event} event
  8952. * The `durationchange` event that caused this to run.
  8953. *
  8954. * @listens Tech#durationchange
  8955. */
  8956. Tech.prototype.onDurationChange = function onDurationChange(event) {
  8957. this.duration_ = this.duration();
  8958. };
  8959. /**
  8960. * Get and create a `TimeRange` object for buffering.
  8961. *
  8962. * @return {TimeRange}
  8963. * The time range object that was created.
  8964. */
  8965. Tech.prototype.buffered = function buffered() {
  8966. return createTimeRanges(0, 0);
  8967. };
  8968. /**
  8969. * Get the percentage of the current video that is currently buffered.
  8970. *
  8971. * @return {number}
  8972. * A number from 0 to 1 that represents the decimal percentage of the
  8973. * video that is buffered.
  8974. *
  8975. */
  8976. Tech.prototype.bufferedPercent = function bufferedPercent$$1() {
  8977. return bufferedPercent(this.buffered(), this.duration_);
  8978. };
  8979. /**
  8980. * Turn off the polyfill for `progress` events that was created in
  8981. * {@link Tech#manualProgressOn}
  8982. * Stop manually tracking progress events by clearing the interval that was set in
  8983. * {@link Tech#trackProgress}.
  8984. */
  8985. Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
  8986. this.clearInterval(this.progressInterval);
  8987. };
  8988. /**
  8989. * Polyfill the `timeupdate` event for browsers that don't support it.
  8990. *
  8991. * @see {@link Tech#trackCurrentTime}
  8992. */
  8993. Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
  8994. this.manualTimeUpdates = true;
  8995. this.on('play', this.trackCurrentTime);
  8996. this.on('pause', this.stopTrackingCurrentTime);
  8997. };
  8998. /**
  8999. * Turn off the polyfill for `timeupdate` events that was created in
  9000. * {@link Tech#manualTimeUpdatesOn}
  9001. */
  9002. Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
  9003. this.manualTimeUpdates = false;
  9004. this.stopTrackingCurrentTime();
  9005. this.off('play', this.trackCurrentTime);
  9006. this.off('pause', this.stopTrackingCurrentTime);
  9007. };
  9008. /**
  9009. * Sets up an interval function to track current time and trigger `timeupdate` every
  9010. * 250 milliseconds.
  9011. *
  9012. * @listens Tech#play
  9013. * @triggers Tech#timeupdate
  9014. */
  9015. Tech.prototype.trackCurrentTime = function trackCurrentTime() {
  9016. if (this.currentTimeInterval) {
  9017. this.stopTrackingCurrentTime();
  9018. }
  9019. this.currentTimeInterval = this.setInterval(function () {
  9020. /**
  9021. * Triggered at an interval of 250ms to indicated that time is passing in the video.
  9022. *
  9023. * @event Tech#timeupdate
  9024. * @type {EventTarget~Event}
  9025. */
  9026. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  9027. // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
  9028. }, 250);
  9029. };
  9030. /**
  9031. * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
  9032. * `timeupdate` event is no longer triggered.
  9033. *
  9034. * @listens {Tech#pause}
  9035. */
  9036. Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
  9037. this.clearInterval(this.currentTimeInterval);
  9038. // #1002 - if the video ends right before the next timeupdate would happen,
  9039. // the progress bar won't make it all the way to the end
  9040. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  9041. };
  9042. /**
  9043. * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
  9044. * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
  9045. *
  9046. * @fires Component#dispose
  9047. */
  9048. Tech.prototype.dispose = function dispose() {
  9049. // clear out all tracks because we can't reuse them between techs
  9050. this.clearTracks(NORMAL.names);
  9051. // Turn off any manual progress or timeupdate tracking
  9052. if (this.manualProgress) {
  9053. this.manualProgressOff();
  9054. }
  9055. if (this.manualTimeUpdates) {
  9056. this.manualTimeUpdatesOff();
  9057. }
  9058. _Component.prototype.dispose.call(this);
  9059. };
  9060. /**
  9061. * Clear out a single `TrackList` or an array of `TrackLists` given their names.
  9062. *
  9063. * > Note: Techs without source handlers should call this between sources for `video`
  9064. * & `audio` tracks. You don't want to use them between tracks!
  9065. *
  9066. * @param {string[]|string} types
  9067. * TrackList names to clear, valid names are `video`, `audio`, and
  9068. * `text`.
  9069. */
  9070. Tech.prototype.clearTracks = function clearTracks(types) {
  9071. var _this3 = this;
  9072. types = [].concat(types);
  9073. // clear out all tracks because we can't reuse them between techs
  9074. types.forEach(function (type) {
  9075. var list = _this3[type + 'Tracks']() || [];
  9076. var i = list.length;
  9077. while (i--) {
  9078. var track = list[i];
  9079. if (type === 'text') {
  9080. _this3.removeRemoteTextTrack(track);
  9081. }
  9082. list.removeTrack(track);
  9083. }
  9084. });
  9085. };
  9086. /**
  9087. * Remove any TextTracks added via addRemoteTextTrack that are
  9088. * flagged for automatic garbage collection
  9089. */
  9090. Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
  9091. var list = this.autoRemoteTextTracks_ || [];
  9092. var i = list.length;
  9093. while (i--) {
  9094. var track = list[i];
  9095. this.removeRemoteTextTrack(track);
  9096. }
  9097. };
  9098. /**
  9099. * Reset the tech, which will removes all sources and reset the internal readyState.
  9100. *
  9101. * @abstract
  9102. */
  9103. Tech.prototype.reset = function reset() {};
  9104. /**
  9105. * Get or set an error on the Tech.
  9106. *
  9107. * @param {MediaError} [err]
  9108. * Error to set on the Tech
  9109. *
  9110. * @return {MediaError|null}
  9111. * The current error object on the tech, or null if there isn't one.
  9112. */
  9113. Tech.prototype.error = function error(err) {
  9114. if (err !== undefined) {
  9115. this.error_ = new MediaError(err);
  9116. this.trigger('error');
  9117. }
  9118. return this.error_;
  9119. };
  9120. /**
  9121. * Returns the `TimeRange`s that have been played through for the current source.
  9122. *
  9123. * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
  9124. * It only checks wether the source has played at all or not.
  9125. *
  9126. * @return {TimeRange}
  9127. * - A single time range if this video has played
  9128. * - An empty set of ranges if not.
  9129. */
  9130. Tech.prototype.played = function played() {
  9131. if (this.hasStarted_) {
  9132. return createTimeRanges(0, 0);
  9133. }
  9134. return createTimeRanges();
  9135. };
  9136. /**
  9137. * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
  9138. * previously called.
  9139. *
  9140. * @fires Tech#timeupdate
  9141. */
  9142. Tech.prototype.setCurrentTime = function setCurrentTime() {
  9143. // improve the accuracy of manual timeupdates
  9144. if (this.manualTimeUpdates) {
  9145. /**
  9146. * A manual `timeupdate` event.
  9147. *
  9148. * @event Tech#timeupdate
  9149. * @type {EventTarget~Event}
  9150. */
  9151. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  9152. }
  9153. };
  9154. /**
  9155. * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
  9156. * {@link TextTrackList} events.
  9157. *
  9158. * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`.
  9159. *
  9160. * @fires Tech#audiotrackchange
  9161. * @fires Tech#videotrackchange
  9162. * @fires Tech#texttrackchange
  9163. */
  9164. Tech.prototype.initTrackListeners = function initTrackListeners() {
  9165. var _this4 = this;
  9166. /**
  9167. * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
  9168. *
  9169. * @event Tech#audiotrackchange
  9170. * @type {EventTarget~Event}
  9171. */
  9172. /**
  9173. * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
  9174. *
  9175. * @event Tech#videotrackchange
  9176. * @type {EventTarget~Event}
  9177. */
  9178. /**
  9179. * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
  9180. *
  9181. * @event Tech#texttrackchange
  9182. * @type {EventTarget~Event}
  9183. */
  9184. NORMAL.names.forEach(function (name) {
  9185. var props = NORMAL[name];
  9186. var trackListChanges = function trackListChanges() {
  9187. _this4.trigger(name + 'trackchange');
  9188. };
  9189. var tracks = _this4[props.getterName]();
  9190. tracks.addEventListener('removetrack', trackListChanges);
  9191. tracks.addEventListener('addtrack', trackListChanges);
  9192. _this4.on('dispose', function () {
  9193. tracks.removeEventListener('removetrack', trackListChanges);
  9194. tracks.removeEventListener('addtrack', trackListChanges);
  9195. });
  9196. });
  9197. };
  9198. /**
  9199. * Emulate TextTracks using vtt.js if necessary
  9200. *
  9201. * @fires Tech#vttjsloaded
  9202. * @fires Tech#vttjserror
  9203. */
  9204. Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
  9205. var _this5 = this;
  9206. if (window_1.WebVTT) {
  9207. return;
  9208. }
  9209. // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
  9210. // signals that the Tech is ready at which point Tech.el_ is part of the DOM
  9211. // before inserting the WebVTT script
  9212. if (document_1.body.contains(this.el())) {
  9213. // load via require if available and vtt.js script location was not passed in
  9214. // as an option. novtt builds will turn the above require call into an empty object
  9215. // which will cause this if check to always fail.
  9216. if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
  9217. this.trigger('vttjsloaded');
  9218. return;
  9219. }
  9220. // load vtt.js via the script location option or the cdn of no location was
  9221. // passed in
  9222. var script = document_1.createElement('script');
  9223. script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
  9224. script.onload = function () {
  9225. /**
  9226. * Fired when vtt.js is loaded.
  9227. *
  9228. * @event Tech#vttjsloaded
  9229. * @type {EventTarget~Event}
  9230. */
  9231. _this5.trigger('vttjsloaded');
  9232. };
  9233. script.onerror = function () {
  9234. /**
  9235. * Fired when vtt.js was not loaded due to an error
  9236. *
  9237. * @event Tech#vttjsloaded
  9238. * @type {EventTarget~Event}
  9239. */
  9240. _this5.trigger('vttjserror');
  9241. };
  9242. this.on('dispose', function () {
  9243. script.onload = null;
  9244. script.onerror = null;
  9245. });
  9246. // but have not loaded yet and we set it to true before the inject so that
  9247. // we don't overwrite the injected window.WebVTT if it loads right away
  9248. window_1.WebVTT = true;
  9249. this.el().parentNode.appendChild(script);
  9250. } else {
  9251. this.ready(this.addWebVttScript_);
  9252. }
  9253. };
  9254. /**
  9255. * Emulate texttracks
  9256. *
  9257. */
  9258. Tech.prototype.emulateTextTracks = function emulateTextTracks() {
  9259. var _this6 = this;
  9260. var tracks = this.textTracks();
  9261. var remoteTracks = this.remoteTextTracks();
  9262. var handleAddTrack = function handleAddTrack(e) {
  9263. return tracks.addTrack(e.track);
  9264. };
  9265. var handleRemoveTrack = function handleRemoveTrack(e) {
  9266. return tracks.removeTrack(e.track);
  9267. };
  9268. remoteTracks.on('addtrack', handleAddTrack);
  9269. remoteTracks.on('removetrack', handleRemoveTrack);
  9270. this.addWebVttScript_();
  9271. var updateDisplay = function updateDisplay() {
  9272. return _this6.trigger('texttrackchange');
  9273. };
  9274. var textTracksChanges = function textTracksChanges() {
  9275. updateDisplay();
  9276. for (var i = 0; i < tracks.length; i++) {
  9277. var track = tracks[i];
  9278. track.removeEventListener('cuechange', updateDisplay);
  9279. if (track.mode === 'showing') {
  9280. track.addEventListener('cuechange', updateDisplay);
  9281. }
  9282. }
  9283. };
  9284. textTracksChanges();
  9285. tracks.addEventListener('change', textTracksChanges);
  9286. tracks.addEventListener('addtrack', textTracksChanges);
  9287. tracks.addEventListener('removetrack', textTracksChanges);
  9288. this.on('dispose', function () {
  9289. remoteTracks.off('addtrack', handleAddTrack);
  9290. remoteTracks.off('removetrack', handleRemoveTrack);
  9291. tracks.removeEventListener('change', textTracksChanges);
  9292. tracks.removeEventListener('addtrack', textTracksChanges);
  9293. tracks.removeEventListener('removetrack', textTracksChanges);
  9294. for (var i = 0; i < tracks.length; i++) {
  9295. var track = tracks[i];
  9296. track.removeEventListener('cuechange', updateDisplay);
  9297. }
  9298. });
  9299. };
  9300. /**
  9301. * Create and returns a remote {@link TextTrack} object.
  9302. *
  9303. * @param {string} kind
  9304. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  9305. *
  9306. * @param {string} [label]
  9307. * Label to identify the text track
  9308. *
  9309. * @param {string} [language]
  9310. * Two letter language abbreviation
  9311. *
  9312. * @return {TextTrack}
  9313. * The TextTrack that gets created.
  9314. */
  9315. Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  9316. if (!kind) {
  9317. throw new Error('TextTrack kind is required but was not provided');
  9318. }
  9319. return createTrackHelper(this, kind, label, language);
  9320. };
  9321. /**
  9322. * Create an emulated TextTrack for use by addRemoteTextTrack
  9323. *
  9324. * This is intended to be overridden by classes that inherit from
  9325. * Tech in order to create native or custom TextTracks.
  9326. *
  9327. * @param {Object} options
  9328. * The object should contain the options to initialize the TextTrack with.
  9329. *
  9330. * @param {string} [options.kind]
  9331. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
  9332. *
  9333. * @param {string} [options.label].
  9334. * Label to identify the text track
  9335. *
  9336. * @param {string} [options.language]
  9337. * Two letter language abbreviation.
  9338. *
  9339. * @return {HTMLTrackElement}
  9340. * The track element that gets created.
  9341. */
  9342. Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
  9343. var track = mergeOptions(options, {
  9344. tech: this
  9345. });
  9346. return new REMOTE.remoteTextEl.TrackClass(track);
  9347. };
  9348. /**
  9349. * Creates a remote text track object and returns an html track element.
  9350. *
  9351. * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
  9352. *
  9353. * @param {Object} options
  9354. * See {@link Tech#createRemoteTextTrack} for more detailed properties.
  9355. *
  9356. * @param {boolean} [manualCleanup=true]
  9357. * - When false: the TextTrack will be automatically removed from the video
  9358. * element whenever the source changes
  9359. * - When True: The TextTrack will have to be cleaned up manually
  9360. *
  9361. * @return {HTMLTrackElement}
  9362. * An Html Track Element.
  9363. *
  9364. * @deprecated The default functionality for this function will be equivalent
  9365. * to "manualCleanup=false" in the future. The manualCleanup parameter will
  9366. * also be removed.
  9367. */
  9368. Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
  9369. var _this7 = this;
  9370. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  9371. var manualCleanup = arguments[1];
  9372. var htmlTrackElement = this.createRemoteTextTrack(options);
  9373. if (manualCleanup !== true && manualCleanup !== false) {
  9374. // deprecation warning
  9375. log.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
  9376. manualCleanup = true;
  9377. }
  9378. // store HTMLTrackElement and TextTrack to remote list
  9379. this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
  9380. this.remoteTextTracks().addTrack(htmlTrackElement.track);
  9381. if (manualCleanup !== true) {
  9382. // create the TextTrackList if it doesn't exist
  9383. this.ready(function () {
  9384. return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
  9385. });
  9386. }
  9387. return htmlTrackElement;
  9388. };
  9389. /**
  9390. * Remove a remote text track from the remote `TextTrackList`.
  9391. *
  9392. * @param {TextTrack} track
  9393. * `TextTrack` to remove from the `TextTrackList`
  9394. */
  9395. Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
  9396. var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
  9397. // remove HTMLTrackElement and TextTrack from remote list
  9398. this.remoteTextTrackEls().removeTrackElement_(trackElement);
  9399. this.remoteTextTracks().removeTrack(track);
  9400. this.autoRemoteTextTracks_.removeTrack(track);
  9401. };
  9402. /**
  9403. * Gets available media playback quality metrics as specified by the W3C's Media
  9404. * Playback Quality API.
  9405. *
  9406. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  9407. *
  9408. * @return {Object}
  9409. * An object with supported media playback quality metrics
  9410. *
  9411. * @abstract
  9412. */
  9413. Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  9414. return {};
  9415. };
  9416. /**
  9417. * A method to set a poster from a `Tech`.
  9418. *
  9419. * @abstract
  9420. */
  9421. Tech.prototype.setPoster = function setPoster() {};
  9422. /**
  9423. * A method to check for the presence of the 'playsinine' <video> attribute.
  9424. *
  9425. * @abstract
  9426. */
  9427. Tech.prototype.playsinline = function playsinline() {};
  9428. /**
  9429. * A method to set or unset the 'playsinine' <video> attribute.
  9430. *
  9431. * @abstract
  9432. */
  9433. Tech.prototype.setPlaysinline = function setPlaysinline() {};
  9434. /*
  9435. * Check if the tech can support the given mime-type.
  9436. *
  9437. * The base tech does not support any type, but source handlers might
  9438. * overwrite this.
  9439. *
  9440. * @param {string} type
  9441. * The mimetype to check for support
  9442. *
  9443. * @return {string}
  9444. * 'probably', 'maybe', or empty string
  9445. *
  9446. * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
  9447. *
  9448. * @abstract
  9449. */
  9450. Tech.prototype.canPlayType = function canPlayType() {
  9451. return '';
  9452. };
  9453. /**
  9454. * Check if the type is supported by this tech.
  9455. *
  9456. * The base tech does not support any type, but source handlers might
  9457. * overwrite this.
  9458. *
  9459. * @param {string} type
  9460. * The media type to check
  9461. * @return {string} Returns the native video element's response
  9462. */
  9463. Tech.canPlayType = function canPlayType() {
  9464. return '';
  9465. };
  9466. /**
  9467. * Check if the tech can support the given source
  9468. * @param {Object} srcObj
  9469. * The source object
  9470. * @param {Object} options
  9471. * The options passed to the tech
  9472. * @return {string} 'probably', 'maybe', or '' (empty string)
  9473. */
  9474. Tech.canPlaySource = function canPlaySource(srcObj, options) {
  9475. return Tech.canPlayType(srcObj.type);
  9476. };
  9477. /*
  9478. * Return whether the argument is a Tech or not.
  9479. * Can be passed either a Class like `Html5` or a instance like `player.tech_`
  9480. *
  9481. * @param {Object} component
  9482. * The item to check
  9483. *
  9484. * @return {boolean}
  9485. * Whether it is a tech or not
  9486. * - True if it is a tech
  9487. * - False if it is not
  9488. */
  9489. Tech.isTech = function isTech(component) {
  9490. return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
  9491. };
  9492. /**
  9493. * Registers a `Tech` into a shared list for videojs.
  9494. *
  9495. * @param {string} name
  9496. * Name of the `Tech` to register.
  9497. *
  9498. * @param {Object} tech
  9499. * The `Tech` class to register.
  9500. */
  9501. Tech.registerTech = function registerTech(name, tech) {
  9502. if (!Tech.techs_) {
  9503. Tech.techs_ = {};
  9504. }
  9505. if (!Tech.isTech(tech)) {
  9506. throw new Error('Tech ' + name + ' must be a Tech');
  9507. }
  9508. if (!Tech.canPlayType) {
  9509. throw new Error('Techs must have a static canPlayType method on them');
  9510. }
  9511. if (!Tech.canPlaySource) {
  9512. throw new Error('Techs must have a static canPlaySource method on them');
  9513. }
  9514. name = toTitleCase(name);
  9515. Tech.techs_[name] = tech;
  9516. if (name !== 'Tech') {
  9517. // camel case the techName for use in techOrder
  9518. Tech.defaultTechOrder_.push(name);
  9519. }
  9520. return tech;
  9521. };
  9522. /**
  9523. * Get a `Tech` from the shared list by name.
  9524. *
  9525. * @param {string} name
  9526. * `camelCase` or `TitleCase` name of the Tech to get
  9527. *
  9528. * @return {Tech|undefined}
  9529. * The `Tech` or undefined if there was no tech with the name requsted.
  9530. */
  9531. Tech.getTech = function getTech(name) {
  9532. if (!name) {
  9533. return;
  9534. }
  9535. name = toTitleCase(name);
  9536. if (Tech.techs_ && Tech.techs_[name]) {
  9537. return Tech.techs_[name];
  9538. }
  9539. if (window_1 && window_1.videojs && window_1.videojs[name]) {
  9540. log.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
  9541. return window_1.videojs[name];
  9542. }
  9543. };
  9544. return Tech;
  9545. }(Component);
  9546. /**
  9547. * Get the {@link VideoTrackList}
  9548. *
  9549. * @returns {VideoTrackList}
  9550. * @method Tech.prototype.videoTracks
  9551. */
  9552. /**
  9553. * Get the {@link AudioTrackList}
  9554. *
  9555. * @returns {AudioTrackList}
  9556. * @method Tech.prototype.audioTracks
  9557. */
  9558. /**
  9559. * Get the {@link TextTrackList}
  9560. *
  9561. * @returns {TextTrackList}
  9562. * @method Tech.prototype.textTracks
  9563. */
  9564. /**
  9565. * Get the remote element {@link TextTrackList}
  9566. *
  9567. * @returns {TextTrackList}
  9568. * @method Tech.prototype.remoteTextTracks
  9569. */
  9570. /**
  9571. * Get the remote element {@link HtmlTrackElementList}
  9572. *
  9573. * @returns {HtmlTrackElementList}
  9574. * @method Tech.prototype.remoteTextTrackEls
  9575. */
  9576. ALL.names.forEach(function (name) {
  9577. var props = ALL[name];
  9578. Tech.prototype[props.getterName] = function () {
  9579. this[props.privateName] = this[props.privateName] || new props.ListClass();
  9580. return this[props.privateName];
  9581. };
  9582. });
  9583. /**
  9584. * List of associated text tracks
  9585. *
  9586. * @type {TextTrackList}
  9587. * @private
  9588. * @property Tech#textTracks_
  9589. */
  9590. /**
  9591. * List of associated audio tracks.
  9592. *
  9593. * @type {AudioTrackList}
  9594. * @private
  9595. * @property Tech#audioTracks_
  9596. */
  9597. /**
  9598. * List of associated video tracks.
  9599. *
  9600. * @type {VideoTrackList}
  9601. * @private
  9602. * @property Tech#videoTracks_
  9603. */
  9604. /**
  9605. * Boolean indicating wether the `Tech` supports volume control.
  9606. *
  9607. * @type {boolean}
  9608. * @default
  9609. */
  9610. Tech.prototype.featuresVolumeControl = true;
  9611. /**
  9612. * Boolean indicating whether the `Tech` supports muting volume.
  9613. *
  9614. * @type {bolean}
  9615. * @default
  9616. */
  9617. Tech.prototype.featuresMuteControl = true;
  9618. /**
  9619. * Boolean indicating whether the `Tech` supports fullscreen resize control.
  9620. * Resizing plugins using request fullscreen reloads the plugin
  9621. *
  9622. * @type {boolean}
  9623. * @default
  9624. */
  9625. Tech.prototype.featuresFullscreenResize = false;
  9626. /**
  9627. * Boolean indicating wether the `Tech` supports changing the speed at which the video
  9628. * plays. Examples:
  9629. * - Set player to play 2x (twice) as fast
  9630. * - Set player to play 0.5x (half) as fast
  9631. *
  9632. * @type {boolean}
  9633. * @default
  9634. */
  9635. Tech.prototype.featuresPlaybackRate = false;
  9636. /**
  9637. * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
  9638. * not triggered by video-js-swf. This will be used to determine if
  9639. * {@link Tech#manualProgressOn} should be called.
  9640. *
  9641. * @type {boolean}
  9642. * @default
  9643. */
  9644. Tech.prototype.featuresProgressEvents = false;
  9645. /**
  9646. * Boolean indicating wether the `Tech` supports the `sourceset` event.
  9647. *
  9648. * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
  9649. * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
  9650. * a new source.
  9651. *
  9652. * @type {boolean}
  9653. * @default
  9654. */
  9655. Tech.prototype.featuresSourceset = false;
  9656. /**
  9657. * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
  9658. * not triggered by video-js-swf. This will be used to determine if
  9659. * {@link Tech#manualTimeUpdates} should be called.
  9660. *
  9661. * @type {boolean}
  9662. * @default
  9663. */
  9664. Tech.prototype.featuresTimeupdateEvents = false;
  9665. /**
  9666. * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
  9667. * This will help us integrate with native `TextTrack`s if the browser supports them.
  9668. *
  9669. * @type {boolean}
  9670. * @default
  9671. */
  9672. Tech.prototype.featuresNativeTextTracks = false;
  9673. /**
  9674. * A functional mixin for techs that want to use the Source Handler pattern.
  9675. * Source handlers are scripts for handling specific formats.
  9676. * The source handler pattern is used for adaptive formats (HLS, DASH) that
  9677. * manually load video data and feed it into a Source Buffer (Media Source Extensions)
  9678. * Example: `Tech.withSourceHandlers.call(MyTech);`
  9679. *
  9680. * @param {Tech} _Tech
  9681. * The tech to add source handler functions to.
  9682. *
  9683. * @mixes Tech~SourceHandlerAdditions
  9684. */
  9685. Tech.withSourceHandlers = function (_Tech) {
  9686. /**
  9687. * Register a source handler
  9688. *
  9689. * @param {Function} handler
  9690. * The source handler class
  9691. *
  9692. * @param {number} [index]
  9693. * Register it at the following index
  9694. */
  9695. _Tech.registerSourceHandler = function (handler, index) {
  9696. var handlers = _Tech.sourceHandlers;
  9697. if (!handlers) {
  9698. handlers = _Tech.sourceHandlers = [];
  9699. }
  9700. if (index === undefined) {
  9701. // add to the end of the list
  9702. index = handlers.length;
  9703. }
  9704. handlers.splice(index, 0, handler);
  9705. };
  9706. /**
  9707. * Check if the tech can support the given type. Also checks the
  9708. * Techs sourceHandlers.
  9709. *
  9710. * @param {string} type
  9711. * The mimetype to check.
  9712. *
  9713. * @return {string}
  9714. * 'probably', 'maybe', or '' (empty string)
  9715. */
  9716. _Tech.canPlayType = function (type) {
  9717. var handlers = _Tech.sourceHandlers || [];
  9718. var can = void 0;
  9719. for (var i = 0; i < handlers.length; i++) {
  9720. can = handlers[i].canPlayType(type);
  9721. if (can) {
  9722. return can;
  9723. }
  9724. }
  9725. return '';
  9726. };
  9727. /**
  9728. * Returns the first source handler that supports the source.
  9729. *
  9730. * TODO: Answer question: should 'probably' be prioritized over 'maybe'
  9731. *
  9732. * @param {Tech~SourceObject} source
  9733. * The source object
  9734. *
  9735. * @param {Object} options
  9736. * The options passed to the tech
  9737. *
  9738. * @return {SourceHandler|null}
  9739. * The first source handler that supports the source or null if
  9740. * no SourceHandler supports the source
  9741. */
  9742. _Tech.selectSourceHandler = function (source, options) {
  9743. var handlers = _Tech.sourceHandlers || [];
  9744. var can = void 0;
  9745. for (var i = 0; i < handlers.length; i++) {
  9746. can = handlers[i].canHandleSource(source, options);
  9747. if (can) {
  9748. return handlers[i];
  9749. }
  9750. }
  9751. return null;
  9752. };
  9753. /**
  9754. * Check if the tech can support the given source.
  9755. *
  9756. * @param {Tech~SourceObject} srcObj
  9757. * The source object
  9758. *
  9759. * @param {Object} options
  9760. * The options passed to the tech
  9761. *
  9762. * @return {string}
  9763. * 'probably', 'maybe', or '' (empty string)
  9764. */
  9765. _Tech.canPlaySource = function (srcObj, options) {
  9766. var sh = _Tech.selectSourceHandler(srcObj, options);
  9767. if (sh) {
  9768. return sh.canHandleSource(srcObj, options);
  9769. }
  9770. return '';
  9771. };
  9772. /**
  9773. * When using a source handler, prefer its implementation of
  9774. * any function normally provided by the tech.
  9775. */
  9776. var deferrable = ['seekable', 'seeking', 'duration'];
  9777. /**
  9778. * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
  9779. * function if it exists, with a fallback to the Techs seekable function.
  9780. *
  9781. * @method _Tech.seekable
  9782. */
  9783. /**
  9784. * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
  9785. * function if it exists, otherwise it will fallback to the techs duration function.
  9786. *
  9787. * @method _Tech.duration
  9788. */
  9789. deferrable.forEach(function (fnName) {
  9790. var originalFn = this[fnName];
  9791. if (typeof originalFn !== 'function') {
  9792. return;
  9793. }
  9794. this[fnName] = function () {
  9795. if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
  9796. return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
  9797. }
  9798. return originalFn.apply(this, arguments);
  9799. };
  9800. }, _Tech.prototype);
  9801. /**
  9802. * Create a function for setting the source using a source object
  9803. * and source handlers.
  9804. * Should never be called unless a source handler was found.
  9805. *
  9806. * @param {Tech~SourceObject} source
  9807. * A source object with src and type keys
  9808. */
  9809. _Tech.prototype.setSource = function (source) {
  9810. var sh = _Tech.selectSourceHandler(source, this.options_);
  9811. if (!sh) {
  9812. // Fall back to a native source hander when unsupported sources are
  9813. // deliberately set
  9814. if (_Tech.nativeSourceHandler) {
  9815. sh = _Tech.nativeSourceHandler;
  9816. } else {
  9817. log.error('No source hander found for the current source.');
  9818. }
  9819. }
  9820. // Dispose any existing source handler
  9821. this.disposeSourceHandler();
  9822. this.off('dispose', this.disposeSourceHandler);
  9823. if (sh !== _Tech.nativeSourceHandler) {
  9824. this.currentSource_ = source;
  9825. }
  9826. this.sourceHandler_ = sh.handleSource(source, this, this.options_);
  9827. this.on('dispose', this.disposeSourceHandler);
  9828. };
  9829. /**
  9830. * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
  9831. *
  9832. * @listens Tech#dispose
  9833. */
  9834. _Tech.prototype.disposeSourceHandler = function () {
  9835. // if we have a source and get another one
  9836. // then we are loading something new
  9837. // than clear all of our current tracks
  9838. if (this.currentSource_) {
  9839. this.clearTracks(['audio', 'video']);
  9840. this.currentSource_ = null;
  9841. }
  9842. // always clean up auto-text tracks
  9843. this.cleanupAutoTextTracks();
  9844. if (this.sourceHandler_) {
  9845. if (this.sourceHandler_.dispose) {
  9846. this.sourceHandler_.dispose();
  9847. }
  9848. this.sourceHandler_ = null;
  9849. }
  9850. };
  9851. };
  9852. // The base Tech class needs to be registered as a Component. It is the only
  9853. // Tech that can be registered as a Component.
  9854. Component.registerComponent('Tech', Tech);
  9855. Tech.registerTech('Tech', Tech);
  9856. /**
  9857. * A list of techs that should be added to techOrder on Players
  9858. *
  9859. * @private
  9860. */
  9861. Tech.defaultTechOrder_ = [];
  9862. var middlewares = {};
  9863. var middlewareInstances = {};
  9864. var TERMINATOR = {};
  9865. function use(type, middleware) {
  9866. middlewares[type] = middlewares[type] || [];
  9867. middlewares[type].push(middleware);
  9868. }
  9869. function setSource(player, src, next) {
  9870. player.setTimeout(function () {
  9871. return setSourceHelper(src, middlewares[src.type], next, player);
  9872. }, 1);
  9873. }
  9874. function setTech(middleware, tech) {
  9875. middleware.forEach(function (mw) {
  9876. return mw.setTech && mw.setTech(tech);
  9877. });
  9878. }
  9879. /**
  9880. * Calls a getter on the tech first, through each middleware
  9881. * from right to left to the player.
  9882. */
  9883. function get$1(middleware, tech, method) {
  9884. return middleware.reduceRight(middlewareIterator(method), tech[method]());
  9885. }
  9886. /**
  9887. * Takes the argument given to the player and calls the setter method on each
  9888. * middlware from left to right to the tech.
  9889. */
  9890. function set$1(middleware, tech, method, arg) {
  9891. return tech[method](middleware.reduce(middlewareIterator(method), arg));
  9892. }
  9893. /**
  9894. * Takes the argument given to the player and calls the `call` version of the method
  9895. * on each middleware from left to right.
  9896. * Then, call the passed in method on the tech and return the result unchanged
  9897. * back to the player, through middleware, this time from right to left.
  9898. */
  9899. function mediate(middleware, tech, method) {
  9900. var arg = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  9901. var callMethod = 'call' + toTitleCase(method);
  9902. var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
  9903. var terminated = middlewareValue === TERMINATOR;
  9904. var returnValue = terminated ? null : tech[method](middlewareValue);
  9905. executeRight(middleware, method, returnValue, terminated);
  9906. return returnValue;
  9907. }
  9908. var allowedGetters = {
  9909. buffered: 1,
  9910. currentTime: 1,
  9911. duration: 1,
  9912. seekable: 1,
  9913. played: 1,
  9914. paused: 1
  9915. };
  9916. var allowedSetters = {
  9917. setCurrentTime: 1
  9918. };
  9919. var allowedMediators = {
  9920. play: 1,
  9921. pause: 1
  9922. };
  9923. function middlewareIterator(method) {
  9924. return function (value, mw) {
  9925. // if the previous middleware terminated, pass along the termination
  9926. if (value === TERMINATOR) {
  9927. return TERMINATOR;
  9928. }
  9929. if (mw[method]) {
  9930. return mw[method](value);
  9931. }
  9932. return value;
  9933. };
  9934. }
  9935. function executeRight(mws, method, value, terminated) {
  9936. for (var i = mws.length - 1; i >= 0; i--) {
  9937. var mw = mws[i];
  9938. if (mw[method]) {
  9939. mw[method](terminated, value);
  9940. }
  9941. }
  9942. }
  9943. function clearCacheForPlayer(player) {
  9944. middlewareInstances[player.id()] = null;
  9945. }
  9946. /**
  9947. * {
  9948. * [playerId]: [[mwFactory, mwInstance], ...]
  9949. * }
  9950. */
  9951. function getOrCreateFactory(player, mwFactory) {
  9952. var mws = middlewareInstances[player.id()];
  9953. var mw = null;
  9954. if (mws === undefined || mws === null) {
  9955. mw = mwFactory(player);
  9956. middlewareInstances[player.id()] = [[mwFactory, mw]];
  9957. return mw;
  9958. }
  9959. for (var i = 0; i < mws.length; i++) {
  9960. var _mws$i = mws[i],
  9961. mwf = _mws$i[0],
  9962. mwi = _mws$i[1];
  9963. if (mwf !== mwFactory) {
  9964. continue;
  9965. }
  9966. mw = mwi;
  9967. }
  9968. if (mw === null) {
  9969. mw = mwFactory(player);
  9970. mws.push([mwFactory, mw]);
  9971. }
  9972. return mw;
  9973. }
  9974. function setSourceHelper() {
  9975. var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  9976. var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  9977. var next = arguments[2];
  9978. var player = arguments[3];
  9979. var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
  9980. var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
  9981. var mwFactory = middleware[0],
  9982. mwrest = middleware.slice(1);
  9983. // if mwFactory is a string, then we're at a fork in the road
  9984. if (typeof mwFactory === 'string') {
  9985. setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);
  9986. // if we have an mwFactory, call it with the player to get the mw,
  9987. // then call the mw's setSource method
  9988. } else if (mwFactory) {
  9989. var mw = getOrCreateFactory(player, mwFactory);
  9990. // if setSource isn't present, implicitly select this middleware
  9991. if (!mw.setSource) {
  9992. acc.push(mw);
  9993. return setSourceHelper(src, mwrest, next, player, acc, lastRun);
  9994. }
  9995. mw.setSource(assign({}, src), function (err, _src) {
  9996. // something happened, try the next middleware on the current level
  9997. // make sure to use the old src
  9998. if (err) {
  9999. return setSourceHelper(src, mwrest, next, player, acc, lastRun);
  10000. }
  10001. // we've succeeded, now we need to go deeper
  10002. acc.push(mw);
  10003. // if it's the same type, continue down the current chain
  10004. // otherwise, we want to go down the new chain
  10005. setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
  10006. });
  10007. } else if (mwrest.length) {
  10008. setSourceHelper(src, mwrest, next, player, acc, lastRun);
  10009. } else if (lastRun) {
  10010. next(src, acc);
  10011. } else {
  10012. setSourceHelper(src, middlewares['*'], next, player, acc, true);
  10013. }
  10014. }
  10015. /**
  10016. * Mimetypes
  10017. *
  10018. * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
  10019. * @typedef Mimetypes~Kind
  10020. * @enum
  10021. */
  10022. var MimetypesKind = {
  10023. opus: 'video/ogg',
  10024. ogv: 'video/ogg',
  10025. mp4: 'video/mp4',
  10026. mov: 'video/mp4',
  10027. m4v: 'video/mp4',
  10028. mkv: 'video/x-matroska',
  10029. mp3: 'audio/mpeg',
  10030. aac: 'audio/aac',
  10031. oga: 'audio/ogg',
  10032. m3u8: 'application/x-mpegURL'
  10033. };
  10034. /**
  10035. * Get the mimetype of a given src url if possible
  10036. *
  10037. * @param {string} src
  10038. * The url to the src
  10039. *
  10040. * @return {string}
  10041. * return the mimetype if it was known or empty string otherwise
  10042. */
  10043. var getMimetype = function getMimetype() {
  10044. var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  10045. var ext = getFileExtension(src);
  10046. var mimetype = MimetypesKind[ext.toLowerCase()];
  10047. return mimetype || '';
  10048. };
  10049. /**
  10050. * Find the mime type of a given source string if possible. Uses the player
  10051. * source cache.
  10052. *
  10053. * @param {Player} player
  10054. * The player object
  10055. *
  10056. * @param {string} src
  10057. * The source string
  10058. *
  10059. * @return {string}
  10060. * The type that was found
  10061. */
  10062. var findMimetype = function findMimetype(player, src) {
  10063. if (!src) {
  10064. return '';
  10065. }
  10066. // 1. check for the type in the `source` cache
  10067. if (player.cache_.source.src === src && player.cache_.source.type) {
  10068. return player.cache_.source.type;
  10069. }
  10070. // 2. see if we have this source in our `currentSources` cache
  10071. var matchingSources = player.cache_.sources.filter(function (s) {
  10072. return s.src === src;
  10073. });
  10074. if (matchingSources.length) {
  10075. return matchingSources[0].type;
  10076. }
  10077. // 3. look for the src url in source elements and use the type there
  10078. var sources = player.$$('source');
  10079. for (var i = 0; i < sources.length; i++) {
  10080. var s = sources[i];
  10081. if (s.type && s.src && s.src === src) {
  10082. return s.type;
  10083. }
  10084. }
  10085. // 4. finally fallback to our list of mime types based on src url extension
  10086. return getMimetype(src);
  10087. };
  10088. /**
  10089. * @module filter-source
  10090. */
  10091. /**
  10092. * Filter out single bad source objects or multiple source objects in an
  10093. * array. Also flattens nested source object arrays into a 1 dimensional
  10094. * array of source objects.
  10095. *
  10096. * @param {Tech~SourceObject|Tech~SourceObject[]} src
  10097. * The src object to filter
  10098. *
  10099. * @return {Tech~SourceObject[]}
  10100. * An array of sourceobjects containing only valid sources
  10101. *
  10102. * @private
  10103. */
  10104. var filterSource = function filterSource(src) {
  10105. // traverse array
  10106. if (Array.isArray(src)) {
  10107. var newsrc = [];
  10108. src.forEach(function (srcobj) {
  10109. srcobj = filterSource(srcobj);
  10110. if (Array.isArray(srcobj)) {
  10111. newsrc = newsrc.concat(srcobj);
  10112. } else if (isObject(srcobj)) {
  10113. newsrc.push(srcobj);
  10114. }
  10115. });
  10116. src = newsrc;
  10117. } else if (typeof src === 'string' && src.trim()) {
  10118. // convert string into object
  10119. src = [fixSource({ src: src })];
  10120. } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
  10121. // src is already valid
  10122. src = [fixSource(src)];
  10123. } else {
  10124. // invalid source, turn it into an empty array
  10125. src = [];
  10126. }
  10127. return src;
  10128. };
  10129. /**
  10130. * Checks src mimetype, adding it when possible
  10131. *
  10132. * @param {Tech~SourceObject} src
  10133. * The src object to check
  10134. * @return {Tech~SourceObject}
  10135. * src Object with known type
  10136. */
  10137. function fixSource(src) {
  10138. var mimetype = getMimetype(src.src);
  10139. if (!src.type && mimetype) {
  10140. src.type = mimetype;
  10141. }
  10142. return src;
  10143. }
  10144. /**
  10145. * @file loader.js
  10146. */
  10147. /**
  10148. * The `MediaLoader` is the `Component` that decides which playback technology to load
  10149. * when a player is initialized.
  10150. *
  10151. * @extends Component
  10152. */
  10153. var MediaLoader = function (_Component) {
  10154. inherits(MediaLoader, _Component);
  10155. /**
  10156. * Create an instance of this class.
  10157. *
  10158. * @param {Player} player
  10159. * The `Player` that this class should attach to.
  10160. *
  10161. * @param {Object} [options]
  10162. * The key/value stroe of player options.
  10163. *
  10164. * @param {Component~ReadyCallback} [ready]
  10165. * The function that is run when this component is ready.
  10166. */
  10167. function MediaLoader(player, options, ready) {
  10168. classCallCheck(this, MediaLoader);
  10169. // MediaLoader has no element
  10170. var options_ = mergeOptions({ createEl: false }, options);
  10171. // If there are no sources when the player is initialized,
  10172. // load the first supported playback technology.
  10173. var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready));
  10174. if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
  10175. for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
  10176. var techName = toTitleCase(j[i]);
  10177. var tech = Tech.getTech(techName);
  10178. // Support old behavior of techs being registered as components.
  10179. // Remove once that deprecated behavior is removed.
  10180. if (!techName) {
  10181. tech = Component.getComponent(techName);
  10182. }
  10183. // Check if the browser supports this technology
  10184. if (tech && tech.isSupported()) {
  10185. player.loadTech_(techName);
  10186. break;
  10187. }
  10188. }
  10189. } else {
  10190. // Loop through playback technologies (HTML5, Flash) and check for support.
  10191. // Then load the best source.
  10192. // A few assumptions here:
  10193. // All playback technologies respect preload false.
  10194. player.src(options.playerOptions.sources);
  10195. }
  10196. return _this;
  10197. }
  10198. return MediaLoader;
  10199. }(Component);
  10200. Component.registerComponent('MediaLoader', MediaLoader);
  10201. /**
  10202. * @file button.js
  10203. */
  10204. /**
  10205. * Clickable Component which is clickable or keyboard actionable,
  10206. * but is not a native HTML button.
  10207. *
  10208. * @extends Component
  10209. */
  10210. var ClickableComponent = function (_Component) {
  10211. inherits(ClickableComponent, _Component);
  10212. /**
  10213. * Creates an instance of this class.
  10214. *
  10215. * @param {Player} player
  10216. * The `Player` that this class should be attached to.
  10217. *
  10218. * @param {Object} [options]
  10219. * The key/value store of player options.
  10220. */
  10221. function ClickableComponent(player, options) {
  10222. classCallCheck(this, ClickableComponent);
  10223. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  10224. _this.emitTapEvents();
  10225. _this.enable();
  10226. return _this;
  10227. }
  10228. /**
  10229. * Create the `Component`s DOM element.
  10230. *
  10231. * @param {string} [tag=div]
  10232. * The element's node type.
  10233. *
  10234. * @param {Object} [props={}]
  10235. * An object of properties that should be set on the element.
  10236. *
  10237. * @param {Object} [attributes={}]
  10238. * An object of attributes that should be set on the element.
  10239. *
  10240. * @return {Element}
  10241. * The element that gets created.
  10242. */
  10243. ClickableComponent.prototype.createEl = function createEl$$1() {
  10244. var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  10245. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  10246. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  10247. props = assign({
  10248. innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
  10249. className: this.buildCSSClass(),
  10250. tabIndex: 0
  10251. }, props);
  10252. if (tag === 'button') {
  10253. log.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
  10254. }
  10255. // Add ARIA attributes for clickable element which is not a native HTML button
  10256. attributes = assign({
  10257. role: 'button'
  10258. }, attributes);
  10259. this.tabIndex_ = props.tabIndex;
  10260. var el = _Component.prototype.createEl.call(this, tag, props, attributes);
  10261. this.createControlTextEl(el);
  10262. return el;
  10263. };
  10264. ClickableComponent.prototype.dispose = function dispose() {
  10265. // remove controlTextEl_ on dipose
  10266. this.controlTextEl_ = null;
  10267. _Component.prototype.dispose.call(this);
  10268. };
  10269. /**
  10270. * Create a control text element on this `Component`
  10271. *
  10272. * @param {Element} [el]
  10273. * Parent element for the control text.
  10274. *
  10275. * @return {Element}
  10276. * The control text element that gets created.
  10277. */
  10278. ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
  10279. this.controlTextEl_ = createEl('span', {
  10280. className: 'vjs-control-text'
  10281. }, {
  10282. // let the screen reader user know that the text of the element may change
  10283. 'aria-live': 'polite'
  10284. });
  10285. if (el) {
  10286. el.appendChild(this.controlTextEl_);
  10287. }
  10288. this.controlText(this.controlText_, el);
  10289. return this.controlTextEl_;
  10290. };
  10291. /**
  10292. * Get or set the localize text to use for the controls on the `Component`.
  10293. *
  10294. * @param {string} [text]
  10295. * Control text for element.
  10296. *
  10297. * @param {Element} [el=this.el()]
  10298. * Element to set the title on.
  10299. *
  10300. * @return {string}
  10301. * - The control text when getting
  10302. */
  10303. ClickableComponent.prototype.controlText = function controlText(text) {
  10304. var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
  10305. if (text === undefined) {
  10306. return this.controlText_ || 'Need Text';
  10307. }
  10308. var localizedText = this.localize(text);
  10309. this.controlText_ = text;
  10310. textContent(this.controlTextEl_, localizedText);
  10311. if (!this.nonIconControl) {
  10312. // Set title attribute if only an icon is shown
  10313. el.setAttribute('title', localizedText);
  10314. }
  10315. };
  10316. /**
  10317. * Builds the default DOM `className`.
  10318. *
  10319. * @return {string}
  10320. * The DOM `className` for this object.
  10321. */
  10322. ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
  10323. return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
  10324. };
  10325. /**
  10326. * Enable this `Component`s element.
  10327. */
  10328. ClickableComponent.prototype.enable = function enable() {
  10329. if (!this.enabled_) {
  10330. this.enabled_ = true;
  10331. this.removeClass('vjs-disabled');
  10332. this.el_.setAttribute('aria-disabled', 'false');
  10333. if (typeof this.tabIndex_ !== 'undefined') {
  10334. this.el_.setAttribute('tabIndex', this.tabIndex_);
  10335. }
  10336. this.on(['tap', 'click'], this.handleClick);
  10337. this.on('focus', this.handleFocus);
  10338. this.on('blur', this.handleBlur);
  10339. }
  10340. };
  10341. /**
  10342. * Disable this `Component`s element.
  10343. */
  10344. ClickableComponent.prototype.disable = function disable() {
  10345. this.enabled_ = false;
  10346. this.addClass('vjs-disabled');
  10347. this.el_.setAttribute('aria-disabled', 'true');
  10348. if (typeof this.tabIndex_ !== 'undefined') {
  10349. this.el_.removeAttribute('tabIndex');
  10350. }
  10351. this.off(['tap', 'click'], this.handleClick);
  10352. this.off('focus', this.handleFocus);
  10353. this.off('blur', this.handleBlur);
  10354. };
  10355. /**
  10356. * This gets called when a `ClickableComponent` gets:
  10357. * - Clicked (via the `click` event, listening starts in the constructor)
  10358. * - Tapped (via the `tap` event, listening starts in the constructor)
  10359. * - The following things happen in order:
  10360. * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
  10361. * `ClickableComponent`.
  10362. * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
  10363. * {@link ClickableComponent#handleKeyPress}.
  10364. * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
  10365. * the space or enter key.
  10366. * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
  10367. * event as a parameter.
  10368. *
  10369. * @param {EventTarget~Event} event
  10370. * The `keydown`, `tap`, or `click` event that caused this function to be
  10371. * called.
  10372. *
  10373. * @listens tap
  10374. * @listens click
  10375. * @abstract
  10376. */
  10377. ClickableComponent.prototype.handleClick = function handleClick(event) {};
  10378. /**
  10379. * This gets called when a `ClickableComponent` gains focus via a `focus` event.
  10380. * Turns on listening for `keydown` events. When they happen it
  10381. * calls `this.handleKeyPress`.
  10382. *
  10383. * @param {EventTarget~Event} event
  10384. * The `focus` event that caused this function to be called.
  10385. *
  10386. * @listens focus
  10387. */
  10388. ClickableComponent.prototype.handleFocus = function handleFocus(event) {
  10389. on(document_1, 'keydown', bind(this, this.handleKeyPress));
  10390. };
  10391. /**
  10392. * Called when this ClickableComponent has focus and a key gets pressed down. By
  10393. * default it will call `this.handleClick` when the key is space or enter.
  10394. *
  10395. * @param {EventTarget~Event} event
  10396. * The `keydown` event that caused this function to be called.
  10397. *
  10398. * @listens keydown
  10399. */
  10400. ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
  10401. // Support Space (32) or Enter (13) key operation to fire a click event
  10402. if (event.which === 32 || event.which === 13) {
  10403. event.preventDefault();
  10404. this.trigger('click');
  10405. } else if (_Component.prototype.handleKeyPress) {
  10406. // Pass keypress handling up for unsupported keys
  10407. _Component.prototype.handleKeyPress.call(this, event);
  10408. }
  10409. };
  10410. /**
  10411. * Called when a `ClickableComponent` loses focus. Turns off the listener for
  10412. * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
  10413. *
  10414. * @param {EventTarget~Event} event
  10415. * The `blur` event that caused this function to be called.
  10416. *
  10417. * @listens blur
  10418. */
  10419. ClickableComponent.prototype.handleBlur = function handleBlur(event) {
  10420. off(document_1, 'keydown', bind(this, this.handleKeyPress));
  10421. };
  10422. return ClickableComponent;
  10423. }(Component);
  10424. Component.registerComponent('ClickableComponent', ClickableComponent);
  10425. /**
  10426. * @file poster-image.js
  10427. */
  10428. /**
  10429. * A `ClickableComponent` that handles showing the poster image for the player.
  10430. *
  10431. * @extends ClickableComponent
  10432. */
  10433. var PosterImage = function (_ClickableComponent) {
  10434. inherits(PosterImage, _ClickableComponent);
  10435. /**
  10436. * Create an instance of this class.
  10437. *
  10438. * @param {Player} player
  10439. * The `Player` that this class should attach to.
  10440. *
  10441. * @param {Object} [options]
  10442. * The key/value store of player options.
  10443. */
  10444. function PosterImage(player, options) {
  10445. classCallCheck(this, PosterImage);
  10446. var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  10447. _this.update();
  10448. player.on('posterchange', bind(_this, _this.update));
  10449. return _this;
  10450. }
  10451. /**
  10452. * Clean up and dispose of the `PosterImage`.
  10453. */
  10454. PosterImage.prototype.dispose = function dispose() {
  10455. this.player().off('posterchange', this.update);
  10456. _ClickableComponent.prototype.dispose.call(this);
  10457. };
  10458. /**
  10459. * Create the `PosterImage`s DOM element.
  10460. *
  10461. * @return {Element}
  10462. * The element that gets created.
  10463. */
  10464. PosterImage.prototype.createEl = function createEl$$1() {
  10465. var el = createEl('div', {
  10466. className: 'vjs-poster',
  10467. // Don't want poster to be tabbable.
  10468. tabIndex: -1
  10469. });
  10470. // To ensure the poster image resizes while maintaining its original aspect
  10471. // ratio, use a div with `background-size` when available. For browsers that
  10472. // do not support `background-size` (e.g. IE8), fall back on using a regular
  10473. // img element.
  10474. if (!BACKGROUND_SIZE_SUPPORTED) {
  10475. this.fallbackImg_ = createEl('img');
  10476. el.appendChild(this.fallbackImg_);
  10477. }
  10478. return el;
  10479. };
  10480. /**
  10481. * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
  10482. *
  10483. * @listens Player#posterchange
  10484. *
  10485. * @param {EventTarget~Event} [event]
  10486. * The `Player#posterchange` event that triggered this function.
  10487. */
  10488. PosterImage.prototype.update = function update(event) {
  10489. var url = this.player().poster();
  10490. this.setSrc(url);
  10491. // If there's no poster source we should display:none on this component
  10492. // so it's not still clickable or right-clickable
  10493. if (url) {
  10494. this.show();
  10495. } else {
  10496. this.hide();
  10497. }
  10498. };
  10499. /**
  10500. * Set the source of the `PosterImage` depending on the display method.
  10501. *
  10502. * @param {string} url
  10503. * The URL to the source for the `PosterImage`.
  10504. */
  10505. PosterImage.prototype.setSrc = function setSrc(url) {
  10506. if (this.fallbackImg_) {
  10507. this.fallbackImg_.src = url;
  10508. } else {
  10509. var backgroundImage = '';
  10510. // Any falsey values should stay as an empty string, otherwise
  10511. // this will throw an extra error
  10512. if (url) {
  10513. backgroundImage = 'url("' + url + '")';
  10514. }
  10515. this.el_.style.backgroundImage = backgroundImage;
  10516. }
  10517. };
  10518. /**
  10519. * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
  10520. * {@link ClickableComponent#handleClick} for instances where this will be triggered.
  10521. *
  10522. * @listens tap
  10523. * @listens click
  10524. * @listens keydown
  10525. *
  10526. * @param {EventTarget~Event} event
  10527. + The `click`, `tap` or `keydown` event that caused this function to be called.
  10528. */
  10529. PosterImage.prototype.handleClick = function handleClick(event) {
  10530. // We don't want a click to trigger playback when controls are disabled
  10531. if (!this.player_.controls()) {
  10532. return;
  10533. }
  10534. if (this.player_.paused()) {
  10535. silencePromise(this.player_.play());
  10536. } else {
  10537. this.player_.pause();
  10538. }
  10539. };
  10540. return PosterImage;
  10541. }(ClickableComponent);
  10542. Component.registerComponent('PosterImage', PosterImage);
  10543. /**
  10544. * @file text-track-display.js
  10545. */
  10546. var darkGray = '#222';
  10547. var lightGray = '#ccc';
  10548. var fontMap = {
  10549. monospace: 'monospace',
  10550. sansSerif: 'sans-serif',
  10551. serif: 'serif',
  10552. monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
  10553. monospaceSerif: '"Courier New", monospace',
  10554. proportionalSansSerif: 'sans-serif',
  10555. proportionalSerif: 'serif',
  10556. casual: '"Comic Sans MS", Impact, fantasy',
  10557. script: '"Monotype Corsiva", cursive',
  10558. smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
  10559. };
  10560. /**
  10561. * Construct an rgba color from a given hex color code.
  10562. *
  10563. * @param {number} color
  10564. * Hex number for color, like #f0e or #f604e2.
  10565. *
  10566. * @param {number} opacity
  10567. * Value for opacity, 0.0 - 1.0.
  10568. *
  10569. * @return {string}
  10570. * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
  10571. */
  10572. function constructColor(color, opacity) {
  10573. var hex = void 0;
  10574. if (color.length === 4) {
  10575. // color looks like "#f0e"
  10576. hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
  10577. } else if (color.length === 7) {
  10578. // color looks like "#f604e2"
  10579. hex = color.slice(1);
  10580. } else {
  10581. throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');
  10582. }
  10583. return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';
  10584. }
  10585. /**
  10586. * Try to update the style of a DOM element. Some style changes will throw an error,
  10587. * particularly in IE8. Those should be noops.
  10588. *
  10589. * @param {Element} el
  10590. * The DOM element to be styled.
  10591. *
  10592. * @param {string} style
  10593. * The CSS property on the element that should be styled.
  10594. *
  10595. * @param {string} rule
  10596. * The style rule that should be applied to the property.
  10597. *
  10598. * @private
  10599. */
  10600. function tryUpdateStyle(el, style, rule) {
  10601. try {
  10602. el.style[style] = rule;
  10603. } catch (e) {
  10604. // Satisfies linter.
  10605. return;
  10606. }
  10607. }
  10608. /**
  10609. * The component for displaying text track cues.
  10610. *
  10611. * @extends Component
  10612. */
  10613. var TextTrackDisplay = function (_Component) {
  10614. inherits(TextTrackDisplay, _Component);
  10615. /**
  10616. * Creates an instance of this class.
  10617. *
  10618. * @param {Player} player
  10619. * The `Player` that this class should be attached to.
  10620. *
  10621. * @param {Object} [options]
  10622. * The key/value store of player options.
  10623. *
  10624. * @param {Component~ReadyCallback} [ready]
  10625. * The function to call when `TextTrackDisplay` is ready.
  10626. */
  10627. function TextTrackDisplay(player, options, ready) {
  10628. classCallCheck(this, TextTrackDisplay);
  10629. var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready));
  10630. var updateDisplayHandler = bind(_this, _this.updateDisplay);
  10631. player.on('loadstart', bind(_this, _this.toggleDisplay));
  10632. player.on('texttrackchange', updateDisplayHandler);
  10633. player.on('loadstart', bind(_this, _this.preselectTrack));
  10634. // This used to be called during player init, but was causing an error
  10635. // if a track should show by default and the display hadn't loaded yet.
  10636. // Should probably be moved to an external track loader when we support
  10637. // tracks that don't need a display.
  10638. player.ready(bind(_this, function () {
  10639. if (player.tech_ && player.tech_.featuresNativeTextTracks) {
  10640. this.hide();
  10641. return;
  10642. }
  10643. player.on('fullscreenchange', updateDisplayHandler);
  10644. player.on('playerresize', updateDisplayHandler);
  10645. if (window_1.addEventListener) {
  10646. window_1.addEventListener('orientationchange', updateDisplayHandler);
  10647. }
  10648. player.on('dispose', function () {
  10649. return window_1.removeEventListener('orientationchange', updateDisplayHandler);
  10650. });
  10651. var tracks = this.options_.playerOptions.tracks || [];
  10652. for (var i = 0; i < tracks.length; i++) {
  10653. this.player_.addRemoteTextTrack(tracks[i], true);
  10654. }
  10655. this.preselectTrack();
  10656. }));
  10657. return _this;
  10658. }
  10659. /**
  10660. * Preselect a track following this precedence:
  10661. * - matches the previously selected {@link TextTrack}'s language and kind
  10662. * - matches the previously selected {@link TextTrack}'s language only
  10663. * - is the first default captions track
  10664. * - is the first default descriptions track
  10665. *
  10666. * @listens Player#loadstart
  10667. */
  10668. TextTrackDisplay.prototype.preselectTrack = function preselectTrack() {
  10669. var modes = { captions: 1, subtitles: 1 };
  10670. var trackList = this.player_.textTracks();
  10671. var userPref = this.player_.cache_.selectedLanguage;
  10672. var firstDesc = void 0;
  10673. var firstCaptions = void 0;
  10674. var preferredTrack = void 0;
  10675. for (var i = 0; i < trackList.length; i++) {
  10676. var track = trackList[i];
  10677. if (userPref && userPref.enabled && userPref.language === track.language) {
  10678. // Always choose the track that matches both language and kind
  10679. if (track.kind === userPref.kind) {
  10680. preferredTrack = track;
  10681. // or choose the first track that matches language
  10682. } else if (!preferredTrack) {
  10683. preferredTrack = track;
  10684. }
  10685. // clear everything if offTextTrackMenuItem was clicked
  10686. } else if (userPref && !userPref.enabled) {
  10687. preferredTrack = null;
  10688. firstDesc = null;
  10689. firstCaptions = null;
  10690. } else if (track['default']) {
  10691. if (track.kind === 'descriptions' && !firstDesc) {
  10692. firstDesc = track;
  10693. } else if (track.kind in modes && !firstCaptions) {
  10694. firstCaptions = track;
  10695. }
  10696. }
  10697. }
  10698. // The preferredTrack matches the user preference and takes
  10699. // precendence over all the other tracks.
  10700. // So, display the preferredTrack before the first default track
  10701. // and the subtitles/captions track before the descriptions track
  10702. if (preferredTrack) {
  10703. preferredTrack.mode = 'showing';
  10704. } else if (firstCaptions) {
  10705. firstCaptions.mode = 'showing';
  10706. } else if (firstDesc) {
  10707. firstDesc.mode = 'showing';
  10708. }
  10709. };
  10710. /**
  10711. * Turn display of {@link TextTrack}'s from the current state into the other state.
  10712. * There are only two states:
  10713. * - 'shown'
  10714. * - 'hidden'
  10715. *
  10716. * @listens Player#loadstart
  10717. */
  10718. TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
  10719. if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
  10720. this.hide();
  10721. } else {
  10722. this.show();
  10723. }
  10724. };
  10725. /**
  10726. * Create the {@link Component}'s DOM element.
  10727. *
  10728. * @return {Element}
  10729. * The element that was created.
  10730. */
  10731. TextTrackDisplay.prototype.createEl = function createEl() {
  10732. return _Component.prototype.createEl.call(this, 'div', {
  10733. className: 'vjs-text-track-display'
  10734. }, {
  10735. 'aria-live': 'off',
  10736. 'aria-atomic': 'true'
  10737. });
  10738. };
  10739. /**
  10740. * Clear all displayed {@link TextTrack}s.
  10741. */
  10742. TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
  10743. if (typeof window_1.WebVTT === 'function') {
  10744. window_1.WebVTT.processCues(window_1, [], this.el_);
  10745. }
  10746. };
  10747. /**
  10748. * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
  10749. * a {@link Player#fullscreenchange} is fired.
  10750. *
  10751. * @listens Player#texttrackchange
  10752. * @listens Player#fullscreenchange
  10753. */
  10754. TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
  10755. var tracks = this.player_.textTracks();
  10756. this.clearDisplay();
  10757. // Track display prioritization model: if multiple tracks are 'showing',
  10758. // display the first 'subtitles' or 'captions' track which is 'showing',
  10759. // otherwise display the first 'descriptions' track which is 'showing'
  10760. var descriptionsTrack = null;
  10761. var captionsSubtitlesTrack = null;
  10762. var i = tracks.length;
  10763. while (i--) {
  10764. var track = tracks[i];
  10765. if (track.mode === 'showing') {
  10766. if (track.kind === 'descriptions') {
  10767. descriptionsTrack = track;
  10768. } else {
  10769. captionsSubtitlesTrack = track;
  10770. }
  10771. }
  10772. }
  10773. if (captionsSubtitlesTrack) {
  10774. if (this.getAttribute('aria-live') !== 'off') {
  10775. this.setAttribute('aria-live', 'off');
  10776. }
  10777. this.updateForTrack(captionsSubtitlesTrack);
  10778. } else if (descriptionsTrack) {
  10779. if (this.getAttribute('aria-live') !== 'assertive') {
  10780. this.setAttribute('aria-live', 'assertive');
  10781. }
  10782. this.updateForTrack(descriptionsTrack);
  10783. }
  10784. };
  10785. /**
  10786. * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
  10787. *
  10788. * @param {TextTrack} track
  10789. * Text track object to be added to the list.
  10790. */
  10791. TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
  10792. if (typeof window_1.WebVTT !== 'function' || !track.activeCues) {
  10793. return;
  10794. }
  10795. var cues = [];
  10796. for (var _i = 0; _i < track.activeCues.length; _i++) {
  10797. cues.push(track.activeCues[_i]);
  10798. }
  10799. window_1.WebVTT.processCues(window_1, cues, this.el_);
  10800. if (!this.player_.textTrackSettings) {
  10801. return;
  10802. }
  10803. var overrides = this.player_.textTrackSettings.getValues();
  10804. var i = cues.length;
  10805. while (i--) {
  10806. var cue = cues[i];
  10807. if (!cue) {
  10808. continue;
  10809. }
  10810. var cueDiv = cue.displayState;
  10811. if (overrides.color) {
  10812. cueDiv.firstChild.style.color = overrides.color;
  10813. }
  10814. if (overrides.textOpacity) {
  10815. tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
  10816. }
  10817. if (overrides.backgroundColor) {
  10818. cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
  10819. }
  10820. if (overrides.backgroundOpacity) {
  10821. tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
  10822. }
  10823. if (overrides.windowColor) {
  10824. if (overrides.windowOpacity) {
  10825. tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
  10826. } else {
  10827. cueDiv.style.backgroundColor = overrides.windowColor;
  10828. }
  10829. }
  10830. if (overrides.edgeStyle) {
  10831. if (overrides.edgeStyle === 'dropshadow') {
  10832. cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
  10833. } else if (overrides.edgeStyle === 'raised') {
  10834. cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
  10835. } else if (overrides.edgeStyle === 'depressed') {
  10836. cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
  10837. } else if (overrides.edgeStyle === 'uniform') {
  10838. cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
  10839. }
  10840. }
  10841. if (overrides.fontPercent && overrides.fontPercent !== 1) {
  10842. var fontSize = window_1.parseFloat(cueDiv.style.fontSize);
  10843. cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
  10844. cueDiv.style.height = 'auto';
  10845. cueDiv.style.top = 'auto';
  10846. cueDiv.style.bottom = '2px';
  10847. }
  10848. if (overrides.fontFamily && overrides.fontFamily !== 'default') {
  10849. if (overrides.fontFamily === 'small-caps') {
  10850. cueDiv.firstChild.style.fontVariant = 'small-caps';
  10851. } else {
  10852. cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
  10853. }
  10854. }
  10855. }
  10856. };
  10857. return TextTrackDisplay;
  10858. }(Component);
  10859. Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
  10860. /**
  10861. * @file loading-spinner.js
  10862. */
  10863. /**
  10864. * A loading spinner for use during waiting/loading events.
  10865. *
  10866. * @extends Component
  10867. */
  10868. var LoadingSpinner = function (_Component) {
  10869. inherits(LoadingSpinner, _Component);
  10870. function LoadingSpinner() {
  10871. classCallCheck(this, LoadingSpinner);
  10872. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  10873. }
  10874. /**
  10875. * Create the `LoadingSpinner`s DOM element.
  10876. *
  10877. * @return {Element}
  10878. * The dom element that gets created.
  10879. */
  10880. LoadingSpinner.prototype.createEl = function createEl$$1() {
  10881. var isAudio = this.player_.isAudio();
  10882. var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
  10883. var controlText = createEl('span', {
  10884. className: 'vjs-control-text',
  10885. innerHTML: this.localize('{1} is loading.', [playerType])
  10886. });
  10887. var el = _Component.prototype.createEl.call(this, 'div', {
  10888. className: 'vjs-loading-spinner',
  10889. dir: 'ltr'
  10890. });
  10891. el.appendChild(controlText);
  10892. return el;
  10893. };
  10894. return LoadingSpinner;
  10895. }(Component);
  10896. Component.registerComponent('LoadingSpinner', LoadingSpinner);
  10897. /**
  10898. * @file button.js
  10899. */
  10900. /**
  10901. * Base class for all buttons.
  10902. *
  10903. * @extends ClickableComponent
  10904. */
  10905. var Button = function (_ClickableComponent) {
  10906. inherits(Button, _ClickableComponent);
  10907. function Button() {
  10908. classCallCheck(this, Button);
  10909. return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
  10910. }
  10911. /**
  10912. * Create the `Button`s DOM element.
  10913. *
  10914. * @param {string} [tag="button"]
  10915. * The element's node type. This argument is IGNORED: no matter what
  10916. * is passed, it will always create a `button` element.
  10917. *
  10918. * @param {Object} [props={}]
  10919. * An object of properties that should be set on the element.
  10920. *
  10921. * @param {Object} [attributes={}]
  10922. * An object of attributes that should be set on the element.
  10923. *
  10924. * @return {Element}
  10925. * The element that gets created.
  10926. */
  10927. Button.prototype.createEl = function createEl(tag) {
  10928. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  10929. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  10930. tag = 'button';
  10931. props = assign({
  10932. innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
  10933. className: this.buildCSSClass()
  10934. }, props);
  10935. // Add attributes for button element
  10936. attributes = assign({
  10937. // Necessary since the default button type is "submit"
  10938. type: 'button'
  10939. }, attributes);
  10940. var el = Component.prototype.createEl.call(this, tag, props, attributes);
  10941. this.createControlTextEl(el);
  10942. return el;
  10943. };
  10944. /**
  10945. * Add a child `Component` inside of this `Button`.
  10946. *
  10947. * @param {string|Component} child
  10948. * The name or instance of a child to add.
  10949. *
  10950. * @param {Object} [options={}]
  10951. * The key/value store of options that will get passed to children of
  10952. * the child.
  10953. *
  10954. * @return {Component}
  10955. * The `Component` that gets added as a child. When using a string the
  10956. * `Component` will get created by this process.
  10957. *
  10958. * @deprecated since version 5
  10959. */
  10960. Button.prototype.addChild = function addChild(child) {
  10961. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  10962. var className = this.constructor.name;
  10963. log.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
  10964. // Avoid the error message generated by ClickableComponent's addChild method
  10965. return Component.prototype.addChild.call(this, child, options);
  10966. };
  10967. /**
  10968. * Enable the `Button` element so that it can be activated or clicked. Use this with
  10969. * {@link Button#disable}.
  10970. */
  10971. Button.prototype.enable = function enable() {
  10972. _ClickableComponent.prototype.enable.call(this);
  10973. this.el_.removeAttribute('disabled');
  10974. };
  10975. /**
  10976. * Disable the `Button` element so that it cannot be activated or clicked. Use this with
  10977. * {@link Button#enable}.
  10978. */
  10979. Button.prototype.disable = function disable() {
  10980. _ClickableComponent.prototype.disable.call(this);
  10981. this.el_.setAttribute('disabled', 'disabled');
  10982. };
  10983. /**
  10984. * This gets called when a `Button` has focus and `keydown` is triggered via a key
  10985. * press.
  10986. *
  10987. * @param {EventTarget~Event} event
  10988. * The event that caused this function to get called.
  10989. *
  10990. * @listens keydown
  10991. */
  10992. Button.prototype.handleKeyPress = function handleKeyPress(event) {
  10993. // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
  10994. if (event.which === 32 || event.which === 13) {
  10995. return;
  10996. }
  10997. // Pass keypress handling up for unsupported keys
  10998. _ClickableComponent.prototype.handleKeyPress.call(this, event);
  10999. };
  11000. return Button;
  11001. }(ClickableComponent);
  11002. Component.registerComponent('Button', Button);
  11003. /**
  11004. * @file big-play-button.js
  11005. */
  11006. /**
  11007. * The initial play button that shows before the video has played. The hiding of the
  11008. * `BigPlayButton` get done via CSS and `Player` states.
  11009. *
  11010. * @extends Button
  11011. */
  11012. var BigPlayButton = function (_Button) {
  11013. inherits(BigPlayButton, _Button);
  11014. function BigPlayButton(player, options) {
  11015. classCallCheck(this, BigPlayButton);
  11016. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  11017. _this.mouseused_ = false;
  11018. _this.on('mousedown', _this.handleMouseDown);
  11019. return _this;
  11020. }
  11021. /**
  11022. * Builds the default DOM `className`.
  11023. *
  11024. * @return {string}
  11025. * The DOM `className` for this object. Always returns 'vjs-big-play-button'.
  11026. */
  11027. BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
  11028. return 'vjs-big-play-button';
  11029. };
  11030. /**
  11031. * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
  11032. * for more detailed information on what a click can be.
  11033. *
  11034. * @param {EventTarget~Event} event
  11035. * The `keydown`, `tap`, or `click` event that caused this function to be
  11036. * called.
  11037. *
  11038. * @listens tap
  11039. * @listens click
  11040. */
  11041. BigPlayButton.prototype.handleClick = function handleClick(event) {
  11042. var playPromise = this.player_.play();
  11043. // exit early if clicked via the mouse
  11044. if (this.mouseused_ && event.clientX && event.clientY) {
  11045. silencePromise(playPromise);
  11046. return;
  11047. }
  11048. var cb = this.player_.getChild('controlBar');
  11049. var playToggle = cb && cb.getChild('playToggle');
  11050. if (!playToggle) {
  11051. this.player_.focus();
  11052. return;
  11053. }
  11054. var playFocus = function playFocus() {
  11055. return playToggle.focus();
  11056. };
  11057. if (isPromise(playPromise)) {
  11058. playPromise.then(playFocus, function () {});
  11059. } else {
  11060. this.setTimeout(playFocus, 1);
  11061. }
  11062. };
  11063. BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
  11064. this.mouseused_ = false;
  11065. _Button.prototype.handleKeyPress.call(this, event);
  11066. };
  11067. BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
  11068. this.mouseused_ = true;
  11069. };
  11070. return BigPlayButton;
  11071. }(Button);
  11072. /**
  11073. * The text that should display over the `BigPlayButton`s controls. Added to for localization.
  11074. *
  11075. * @type {string}
  11076. * @private
  11077. */
  11078. BigPlayButton.prototype.controlText_ = 'Play Video';
  11079. Component.registerComponent('BigPlayButton', BigPlayButton);
  11080. /**
  11081. * @file close-button.js
  11082. */
  11083. /**
  11084. * The `CloseButton` is a `{@link Button}` that fires a `close` event when
  11085. * it gets clicked.
  11086. *
  11087. * @extends Button
  11088. */
  11089. var CloseButton = function (_Button) {
  11090. inherits(CloseButton, _Button);
  11091. /**
  11092. * Creates an instance of the this class.
  11093. *
  11094. * @param {Player} player
  11095. * The `Player` that this class should be attached to.
  11096. *
  11097. * @param {Object} [options]
  11098. * The key/value store of player options.
  11099. */
  11100. function CloseButton(player, options) {
  11101. classCallCheck(this, CloseButton);
  11102. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  11103. _this.controlText(options && options.controlText || _this.localize('Close'));
  11104. return _this;
  11105. }
  11106. /**
  11107. * Builds the default DOM `className`.
  11108. *
  11109. * @return {string}
  11110. * The DOM `className` for this object.
  11111. */
  11112. CloseButton.prototype.buildCSSClass = function buildCSSClass() {
  11113. return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
  11114. };
  11115. /**
  11116. * This gets called when a `CloseButton` gets clicked. See
  11117. * {@link ClickableComponent#handleClick} for more information on when this will be
  11118. * triggered
  11119. *
  11120. * @param {EventTarget~Event} event
  11121. * The `keydown`, `tap`, or `click` event that caused this function to be
  11122. * called.
  11123. *
  11124. * @listens tap
  11125. * @listens click
  11126. * @fires CloseButton#close
  11127. */
  11128. CloseButton.prototype.handleClick = function handleClick(event) {
  11129. /**
  11130. * Triggered when the a `CloseButton` is clicked.
  11131. *
  11132. * @event CloseButton#close
  11133. * @type {EventTarget~Event}
  11134. *
  11135. * @property {boolean} [bubbles=false]
  11136. * set to false so that the close event does not
  11137. * bubble up to parents if there is no listener
  11138. */
  11139. this.trigger({ type: 'close', bubbles: false });
  11140. };
  11141. return CloseButton;
  11142. }(Button);
  11143. Component.registerComponent('CloseButton', CloseButton);
  11144. /**
  11145. * @file play-toggle.js
  11146. */
  11147. /**
  11148. * Button to toggle between play and pause.
  11149. *
  11150. * @extends Button
  11151. */
  11152. var PlayToggle = function (_Button) {
  11153. inherits(PlayToggle, _Button);
  11154. /**
  11155. * Creates an instance of this class.
  11156. *
  11157. * @param {Player} player
  11158. * The `Player` that this class should be attached to.
  11159. *
  11160. * @param {Object} [options]
  11161. * The key/value store of player options.
  11162. */
  11163. function PlayToggle(player, options) {
  11164. classCallCheck(this, PlayToggle);
  11165. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  11166. _this.on(player, 'play', _this.handlePlay);
  11167. _this.on(player, 'pause', _this.handlePause);
  11168. _this.on(player, 'ended', _this.handleEnded);
  11169. return _this;
  11170. }
  11171. /**
  11172. * Builds the default DOM `className`.
  11173. *
  11174. * @return {string}
  11175. * The DOM `className` for this object.
  11176. */
  11177. PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
  11178. return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
  11179. };
  11180. /**
  11181. * This gets called when an `PlayToggle` is "clicked". See
  11182. * {@link ClickableComponent} for more detailed information on what a click can be.
  11183. *
  11184. * @param {EventTarget~Event} [event]
  11185. * The `keydown`, `tap`, or `click` event that caused this function to be
  11186. * called.
  11187. *
  11188. * @listens tap
  11189. * @listens click
  11190. */
  11191. PlayToggle.prototype.handleClick = function handleClick(event) {
  11192. if (this.player_.paused()) {
  11193. this.player_.play();
  11194. } else {
  11195. this.player_.pause();
  11196. }
  11197. };
  11198. /**
  11199. * This gets called once after the video has ended and the user seeks so that
  11200. * we can change the replay button back to a play button.
  11201. *
  11202. * @param {EventTarget~Event} [event]
  11203. * The event that caused this function to run.
  11204. *
  11205. * @listens Player#seeked
  11206. */
  11207. PlayToggle.prototype.handleSeeked = function handleSeeked(event) {
  11208. this.removeClass('vjs-ended');
  11209. if (this.player_.paused()) {
  11210. this.handlePause(event);
  11211. } else {
  11212. this.handlePlay(event);
  11213. }
  11214. };
  11215. /**
  11216. * Add the vjs-playing class to the element so it can change appearance.
  11217. *
  11218. * @param {EventTarget~Event} [event]
  11219. * The event that caused this function to run.
  11220. *
  11221. * @listens Player#play
  11222. */
  11223. PlayToggle.prototype.handlePlay = function handlePlay(event) {
  11224. this.removeClass('vjs-ended');
  11225. this.removeClass('vjs-paused');
  11226. this.addClass('vjs-playing');
  11227. // change the button text to "Pause"
  11228. this.controlText('Pause');
  11229. };
  11230. /**
  11231. * Add the vjs-paused class to the element so it can change appearance.
  11232. *
  11233. * @param {EventTarget~Event} [event]
  11234. * The event that caused this function to run.
  11235. *
  11236. * @listens Player#pause
  11237. */
  11238. PlayToggle.prototype.handlePause = function handlePause(event) {
  11239. this.removeClass('vjs-playing');
  11240. this.addClass('vjs-paused');
  11241. // change the button text to "Play"
  11242. this.controlText('Play');
  11243. };
  11244. /**
  11245. * Add the vjs-ended class to the element so it can change appearance
  11246. *
  11247. * @param {EventTarget~Event} [event]
  11248. * The event that caused this function to run.
  11249. *
  11250. * @listens Player#ended
  11251. */
  11252. PlayToggle.prototype.handleEnded = function handleEnded(event) {
  11253. this.removeClass('vjs-playing');
  11254. this.addClass('vjs-ended');
  11255. // change the button text to "Replay"
  11256. this.controlText('Replay');
  11257. // on the next seek remove the replay button
  11258. this.one(this.player_, 'seeked', this.handleSeeked);
  11259. };
  11260. return PlayToggle;
  11261. }(Button);
  11262. /**
  11263. * The text that should display over the `PlayToggle`s controls. Added for localization.
  11264. *
  11265. * @type {string}
  11266. * @private
  11267. */
  11268. PlayToggle.prototype.controlText_ = 'Play';
  11269. Component.registerComponent('PlayToggle', PlayToggle);
  11270. /**
  11271. * @file format-time.js
  11272. * @module format-time
  11273. */
  11274. /**
  11275. * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
  11276. * will force a number of leading zeros to cover the length of the guide.
  11277. *
  11278. * @param {number} seconds
  11279. * Number of seconds to be turned into a string
  11280. *
  11281. * @param {number} guide
  11282. * Number (in seconds) to model the string after
  11283. *
  11284. * @return {string}
  11285. * Time formatted as H:MM:SS or M:SS
  11286. */
  11287. var defaultImplementation = function defaultImplementation(seconds, guide) {
  11288. seconds = seconds < 0 ? 0 : seconds;
  11289. var s = Math.floor(seconds % 60);
  11290. var m = Math.floor(seconds / 60 % 60);
  11291. var h = Math.floor(seconds / 3600);
  11292. var gm = Math.floor(guide / 60 % 60);
  11293. var gh = Math.floor(guide / 3600);
  11294. // handle invalid times
  11295. if (isNaN(seconds) || seconds === Infinity) {
  11296. // '-' is false for all relational operators (e.g. <, >=) so this setting
  11297. // will add the minimum number of fields specified by the guide
  11298. h = m = s = '-';
  11299. }
  11300. // Check if we need to show hours
  11301. h = h > 0 || gh > 0 ? h + ':' : '';
  11302. // If hours are showing, we may need to add a leading zero.
  11303. // Always show at least one digit of minutes.
  11304. m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
  11305. // Check if leading zero is need for seconds
  11306. s = s < 10 ? '0' + s : s;
  11307. return h + m + s;
  11308. };
  11309. var implementation = defaultImplementation;
  11310. /**
  11311. * Replaces the default formatTime implementation with a custom implementation.
  11312. *
  11313. * @param {Function} customImplementation
  11314. * A function which will be used in place of the default formatTime implementation.
  11315. * Will receive the current time in seconds and the guide (in seconds) as arguments.
  11316. */
  11317. function setFormatTime(customImplementation) {
  11318. implementation = customImplementation;
  11319. }
  11320. /**
  11321. * Resets formatTime to the default implementation.
  11322. */
  11323. function resetFormatTime() {
  11324. implementation = defaultImplementation;
  11325. }
  11326. var formatTime = function (seconds) {
  11327. var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
  11328. return implementation(seconds, guide);
  11329. };
  11330. /**
  11331. * @file time-display.js
  11332. */
  11333. /**
  11334. * Displays the time left in the video
  11335. *
  11336. * @extends Component
  11337. */
  11338. var TimeDisplay = function (_Component) {
  11339. inherits(TimeDisplay, _Component);
  11340. /**
  11341. * Creates an instance of this class.
  11342. *
  11343. * @param {Player} player
  11344. * The `Player` that this class should be attached to.
  11345. *
  11346. * @param {Object} [options]
  11347. * The key/value store of player options.
  11348. */
  11349. function TimeDisplay(player, options) {
  11350. classCallCheck(this, TimeDisplay);
  11351. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11352. _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
  11353. _this.on(player, 'timeupdate', _this.throttledUpdateContent);
  11354. return _this;
  11355. }
  11356. /**
  11357. * Create the `Component`'s DOM element
  11358. *
  11359. * @return {Element}
  11360. * The element that was created.
  11361. */
  11362. TimeDisplay.prototype.createEl = function createEl$$1(plainName) {
  11363. var className = this.buildCSSClass();
  11364. var el = _Component.prototype.createEl.call(this, 'div', {
  11365. className: className + ' vjs-time-control vjs-control',
  11366. innerHTML: '<span class="vjs-control-text">' + this.localize(this.labelText_) + '\xA0</span>'
  11367. });
  11368. this.contentEl_ = createEl('span', {
  11369. className: className + '-display'
  11370. }, {
  11371. // tell screen readers not to automatically read the time as it changes
  11372. 'aria-live': 'off'
  11373. });
  11374. this.updateTextNode_();
  11375. el.appendChild(this.contentEl_);
  11376. return el;
  11377. };
  11378. TimeDisplay.prototype.dispose = function dispose() {
  11379. this.contentEl_ = null;
  11380. this.textNode_ = null;
  11381. _Component.prototype.dispose.call(this);
  11382. };
  11383. /**
  11384. * Updates the "remaining time" text node with new content using the
  11385. * contents of the `formattedTime_` property.
  11386. *
  11387. * @private
  11388. */
  11389. TimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
  11390. if (!this.contentEl_) {
  11391. return;
  11392. }
  11393. while (this.contentEl_.firstChild) {
  11394. this.contentEl_.removeChild(this.contentEl_.firstChild);
  11395. }
  11396. this.textNode_ = document_1.createTextNode(this.formattedTime_ || this.formatTime_(0));
  11397. this.contentEl_.appendChild(this.textNode_);
  11398. };
  11399. /**
  11400. * Generates a formatted time for this component to use in display.
  11401. *
  11402. * @param {number} time
  11403. * A numeric time, in seconds.
  11404. *
  11405. * @return {string}
  11406. * A formatted time
  11407. *
  11408. * @private
  11409. */
  11410. TimeDisplay.prototype.formatTime_ = function formatTime_(time) {
  11411. return formatTime(time);
  11412. };
  11413. /**
  11414. * Updates the time display text node if it has what was passed in changed
  11415. * the formatted time.
  11416. *
  11417. * @param {number} time
  11418. * The time to update to
  11419. *
  11420. * @private
  11421. */
  11422. TimeDisplay.prototype.updateFormattedTime_ = function updateFormattedTime_(time) {
  11423. var formattedTime = this.formatTime_(time);
  11424. if (formattedTime === this.formattedTime_) {
  11425. return;
  11426. }
  11427. this.formattedTime_ = formattedTime;
  11428. this.requestAnimationFrame(this.updateTextNode_);
  11429. };
  11430. /**
  11431. * To be filled out in the child class, should update the displayed time
  11432. * in accordance with the fact that the current time has changed.
  11433. *
  11434. * @param {EventTarget~Event} [event]
  11435. * The `timeupdate` event that caused this to run.
  11436. *
  11437. * @listens Player#timeupdate
  11438. */
  11439. TimeDisplay.prototype.updateContent = function updateContent(event) {};
  11440. return TimeDisplay;
  11441. }(Component);
  11442. /**
  11443. * The text that is added to the `TimeDisplay` for screen reader users.
  11444. *
  11445. * @type {string}
  11446. * @private
  11447. */
  11448. TimeDisplay.prototype.labelText_ = 'Time';
  11449. /**
  11450. * The text that should display over the `TimeDisplay`s controls. Added to for localization.
  11451. *
  11452. * @type {string}
  11453. * @private
  11454. *
  11455. * @deprecated in v7; controlText_ is not used in non-active display Components
  11456. */
  11457. TimeDisplay.prototype.controlText_ = 'Time';
  11458. Component.registerComponent('TimeDisplay', TimeDisplay);
  11459. /**
  11460. * @file current-time-display.js
  11461. */
  11462. /**
  11463. * Displays the current time
  11464. *
  11465. * @extends Component
  11466. */
  11467. var CurrentTimeDisplay = function (_TimeDisplay) {
  11468. inherits(CurrentTimeDisplay, _TimeDisplay);
  11469. /**
  11470. * Creates an instance of this class.
  11471. *
  11472. * @param {Player} player
  11473. * The `Player` that this class should be attached to.
  11474. *
  11475. * @param {Object} [options]
  11476. * The key/value store of player options.
  11477. */
  11478. function CurrentTimeDisplay(player, options) {
  11479. classCallCheck(this, CurrentTimeDisplay);
  11480. var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
  11481. _this.on(player, 'ended', _this.handleEnded);
  11482. return _this;
  11483. }
  11484. /**
  11485. * Builds the default DOM `className`.
  11486. *
  11487. * @return {string}
  11488. * The DOM `className` for this object.
  11489. */
  11490. CurrentTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
  11491. return 'vjs-current-time';
  11492. };
  11493. /**
  11494. * Update current time display
  11495. *
  11496. * @param {EventTarget~Event} [event]
  11497. * The `timeupdate` event that caused this function to run.
  11498. *
  11499. * @listens Player#timeupdate
  11500. */
  11501. CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
  11502. // Allows for smooth scrubbing, when player can't keep up.
  11503. var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  11504. this.updateFormattedTime_(time);
  11505. };
  11506. /**
  11507. * When the player fires ended there should be no time left. Sadly
  11508. * this is not always the case, lets make it seem like that is the case
  11509. * for users.
  11510. *
  11511. * @param {EventTarget~Event} [event]
  11512. * The `ended` event that caused this to run.
  11513. *
  11514. * @listens Player#ended
  11515. */
  11516. CurrentTimeDisplay.prototype.handleEnded = function handleEnded(event) {
  11517. if (!this.player_.duration()) {
  11518. return;
  11519. }
  11520. this.updateFormattedTime_(this.player_.duration());
  11521. };
  11522. return CurrentTimeDisplay;
  11523. }(TimeDisplay);
  11524. /**
  11525. * The text that is added to the `CurrentTimeDisplay` for screen reader users.
  11526. *
  11527. * @type {string}
  11528. * @private
  11529. */
  11530. CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
  11531. /**
  11532. * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
  11533. *
  11534. * @type {string}
  11535. * @private
  11536. *
  11537. * @deprecated in v7; controlText_ is not used in non-active display Components
  11538. */
  11539. CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
  11540. Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
  11541. /**
  11542. * @file duration-display.js
  11543. */
  11544. /**
  11545. * Displays the duration
  11546. *
  11547. * @extends Component
  11548. */
  11549. var DurationDisplay = function (_TimeDisplay) {
  11550. inherits(DurationDisplay, _TimeDisplay);
  11551. /**
  11552. * Creates an instance of this class.
  11553. *
  11554. * @param {Player} player
  11555. * The `Player` that this class should be attached to.
  11556. *
  11557. * @param {Object} [options]
  11558. * The key/value store of player options.
  11559. */
  11560. function DurationDisplay(player, options) {
  11561. classCallCheck(this, DurationDisplay);
  11562. // we do not want to/need to throttle duration changes,
  11563. // as they should always display the changed duration as
  11564. // it has changed
  11565. var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
  11566. _this.on(player, 'durationchange', _this.updateContent);
  11567. // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
  11568. // listeners could have broken dependent applications/libraries. These
  11569. // can likely be removed for 7.0.
  11570. _this.on(player, 'loadedmetadata', _this.throttledUpdateContent);
  11571. return _this;
  11572. }
  11573. /**
  11574. * Builds the default DOM `className`.
  11575. *
  11576. * @return {string}
  11577. * The DOM `className` for this object.
  11578. */
  11579. DurationDisplay.prototype.buildCSSClass = function buildCSSClass() {
  11580. return 'vjs-duration';
  11581. };
  11582. /**
  11583. * Update duration time display.
  11584. *
  11585. * @param {EventTarget~Event} [event]
  11586. * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
  11587. * this function to be called.
  11588. *
  11589. * @listens Player#durationchange
  11590. * @listens Player#timeupdate
  11591. * @listens Player#loadedmetadata
  11592. */
  11593. DurationDisplay.prototype.updateContent = function updateContent(event) {
  11594. var duration = this.player_.duration();
  11595. if (duration && this.duration_ !== duration) {
  11596. this.duration_ = duration;
  11597. this.updateFormattedTime_(duration);
  11598. }
  11599. };
  11600. return DurationDisplay;
  11601. }(TimeDisplay);
  11602. /**
  11603. * The text that is added to the `DurationDisplay` for screen reader users.
  11604. *
  11605. * @type {string}
  11606. * @private
  11607. */
  11608. DurationDisplay.prototype.labelText_ = 'Duration';
  11609. /**
  11610. * The text that should display over the `DurationDisplay`s controls. Added to for localization.
  11611. *
  11612. * @type {string}
  11613. * @private
  11614. *
  11615. * @deprecated in v7; controlText_ is not used in non-active display Components
  11616. */
  11617. DurationDisplay.prototype.controlText_ = 'Duration';
  11618. Component.registerComponent('DurationDisplay', DurationDisplay);
  11619. /**
  11620. * @file time-divider.js
  11621. */
  11622. /**
  11623. * The separator between the current time and duration.
  11624. * Can be hidden if it's not needed in the design.
  11625. *
  11626. * @extends Component
  11627. */
  11628. var TimeDivider = function (_Component) {
  11629. inherits(TimeDivider, _Component);
  11630. function TimeDivider() {
  11631. classCallCheck(this, TimeDivider);
  11632. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  11633. }
  11634. /**
  11635. * Create the component's DOM element
  11636. *
  11637. * @return {Element}
  11638. * The element that was created.
  11639. */
  11640. TimeDivider.prototype.createEl = function createEl() {
  11641. return _Component.prototype.createEl.call(this, 'div', {
  11642. className: 'vjs-time-control vjs-time-divider',
  11643. innerHTML: '<div><span>/</span></div>'
  11644. });
  11645. };
  11646. return TimeDivider;
  11647. }(Component);
  11648. Component.registerComponent('TimeDivider', TimeDivider);
  11649. /**
  11650. * @file remaining-time-display.js
  11651. */
  11652. /**
  11653. * Displays the time left in the video
  11654. *
  11655. * @extends Component
  11656. */
  11657. var RemainingTimeDisplay = function (_TimeDisplay) {
  11658. inherits(RemainingTimeDisplay, _TimeDisplay);
  11659. /**
  11660. * Creates an instance of this class.
  11661. *
  11662. * @param {Player} player
  11663. * The `Player` that this class should be attached to.
  11664. *
  11665. * @param {Object} [options]
  11666. * The key/value store of player options.
  11667. */
  11668. function RemainingTimeDisplay(player, options) {
  11669. classCallCheck(this, RemainingTimeDisplay);
  11670. var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
  11671. _this.on(player, 'durationchange', _this.throttledUpdateContent);
  11672. _this.on(player, 'ended', _this.handleEnded);
  11673. return _this;
  11674. }
  11675. /**
  11676. * Builds the default DOM `className`.
  11677. *
  11678. * @return {string}
  11679. * The DOM `className` for this object.
  11680. */
  11681. RemainingTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
  11682. return 'vjs-remaining-time';
  11683. };
  11684. /**
  11685. * The remaining time display prefixes numbers with a "minus" character.
  11686. *
  11687. * @param {number} time
  11688. * A numeric time, in seconds.
  11689. *
  11690. * @return {string}
  11691. * A formatted time
  11692. *
  11693. * @private
  11694. */
  11695. RemainingTimeDisplay.prototype.formatTime_ = function formatTime_(time) {
  11696. // TODO: The "-" should be decorative, and not announced by a screen reader
  11697. return '-' + _TimeDisplay.prototype.formatTime_.call(this, time);
  11698. };
  11699. /**
  11700. * Update remaining time display.
  11701. *
  11702. * @param {EventTarget~Event} [event]
  11703. * The `timeupdate` or `durationchange` event that caused this to run.
  11704. *
  11705. * @listens Player#timeupdate
  11706. * @listens Player#durationchange
  11707. */
  11708. RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
  11709. if (!this.player_.duration()) {
  11710. return;
  11711. }
  11712. // @deprecated We should only use remainingTimeDisplay
  11713. // as of video.js 7
  11714. if (this.player_.remainingTimeDisplay) {
  11715. this.updateFormattedTime_(this.player_.remainingTimeDisplay());
  11716. } else {
  11717. this.updateFormattedTime_(this.player_.remainingTime());
  11718. }
  11719. };
  11720. /**
  11721. * When the player fires ended there should be no time left. Sadly
  11722. * this is not always the case, lets make it seem like that is the case
  11723. * for users.
  11724. *
  11725. * @param {EventTarget~Event} [event]
  11726. * The `ended` event that caused this to run.
  11727. *
  11728. * @listens Player#ended
  11729. */
  11730. RemainingTimeDisplay.prototype.handleEnded = function handleEnded(event) {
  11731. if (!this.player_.duration()) {
  11732. return;
  11733. }
  11734. this.updateFormattedTime_(0);
  11735. };
  11736. return RemainingTimeDisplay;
  11737. }(TimeDisplay);
  11738. /**
  11739. * The text that is added to the `RemainingTimeDisplay` for screen reader users.
  11740. *
  11741. * @type {string}
  11742. * @private
  11743. */
  11744. RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
  11745. /**
  11746. * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
  11747. *
  11748. * @type {string}
  11749. * @private
  11750. *
  11751. * @deprecated in v7; controlText_ is not used in non-active display Components
  11752. */
  11753. RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
  11754. Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
  11755. /**
  11756. * @file live-display.js
  11757. */
  11758. // TODO - Future make it click to snap to live
  11759. /**
  11760. * Displays the live indicator when duration is Infinity.
  11761. *
  11762. * @extends Component
  11763. */
  11764. var LiveDisplay = function (_Component) {
  11765. inherits(LiveDisplay, _Component);
  11766. /**
  11767. * Creates an instance of this class.
  11768. *
  11769. * @param {Player} player
  11770. * The `Player` that this class should be attached to.
  11771. *
  11772. * @param {Object} [options]
  11773. * The key/value store of player options.
  11774. */
  11775. function LiveDisplay(player, options) {
  11776. classCallCheck(this, LiveDisplay);
  11777. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11778. _this.updateShowing();
  11779. _this.on(_this.player(), 'durationchange', _this.updateShowing);
  11780. return _this;
  11781. }
  11782. /**
  11783. * Create the `Component`'s DOM element
  11784. *
  11785. * @return {Element}
  11786. * The element that was created.
  11787. */
  11788. LiveDisplay.prototype.createEl = function createEl$$1() {
  11789. var el = _Component.prototype.createEl.call(this, 'div', {
  11790. className: 'vjs-live-control vjs-control'
  11791. });
  11792. this.contentEl_ = createEl('div', {
  11793. className: 'vjs-live-display',
  11794. innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '\xA0</span>' + this.localize('LIVE')
  11795. }, {
  11796. 'aria-live': 'off'
  11797. });
  11798. el.appendChild(this.contentEl_);
  11799. return el;
  11800. };
  11801. LiveDisplay.prototype.dispose = function dispose() {
  11802. this.contentEl_ = null;
  11803. _Component.prototype.dispose.call(this);
  11804. };
  11805. /**
  11806. * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
  11807. * it accordingly
  11808. *
  11809. * @param {EventTarget~Event} [event]
  11810. * The {@link Player#durationchange} event that caused this function to run.
  11811. *
  11812. * @listens Player#durationchange
  11813. */
  11814. LiveDisplay.prototype.updateShowing = function updateShowing(event) {
  11815. if (this.player().duration() === Infinity) {
  11816. this.show();
  11817. } else {
  11818. this.hide();
  11819. }
  11820. };
  11821. return LiveDisplay;
  11822. }(Component);
  11823. Component.registerComponent('LiveDisplay', LiveDisplay);
  11824. /**
  11825. * @file slider.js
  11826. */
  11827. /**
  11828. * The base functionality for a slider. Can be vertical or horizontal.
  11829. * For instance the volume bar or the seek bar on a video is a slider.
  11830. *
  11831. * @extends Component
  11832. */
  11833. var Slider = function (_Component) {
  11834. inherits(Slider, _Component);
  11835. /**
  11836. * Create an instance of this class
  11837. *
  11838. * @param {Player} player
  11839. * The `Player` that this class should be attached to.
  11840. *
  11841. * @param {Object} [options]
  11842. * The key/value store of player options.
  11843. */
  11844. function Slider(player, options) {
  11845. classCallCheck(this, Slider);
  11846. // Set property names to bar to match with the child Slider class is looking for
  11847. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11848. _this.bar = _this.getChild(_this.options_.barName);
  11849. // Set a horizontal or vertical class on the slider depending on the slider type
  11850. _this.vertical(!!_this.options_.vertical);
  11851. _this.enable();
  11852. return _this;
  11853. }
  11854. /**
  11855. * Are controls are currently enabled for this slider or not.
  11856. *
  11857. * @return {boolean}
  11858. * true if controls are enabled, false otherwise
  11859. */
  11860. Slider.prototype.enabled = function enabled() {
  11861. return this.enabled_;
  11862. };
  11863. /**
  11864. * Enable controls for this slider if they are disabled
  11865. */
  11866. Slider.prototype.enable = function enable() {
  11867. if (this.enabled()) {
  11868. return;
  11869. }
  11870. this.on('mousedown', this.handleMouseDown);
  11871. this.on('touchstart', this.handleMouseDown);
  11872. this.on('focus', this.handleFocus);
  11873. this.on('blur', this.handleBlur);
  11874. this.on('click', this.handleClick);
  11875. this.on(this.player_, 'controlsvisible', this.update);
  11876. if (this.playerEvent) {
  11877. this.on(this.player_, this.playerEvent, this.update);
  11878. }
  11879. this.removeClass('disabled');
  11880. this.setAttribute('tabindex', 0);
  11881. this.enabled_ = true;
  11882. };
  11883. /**
  11884. * Disable controls for this slider if they are enabled
  11885. */
  11886. Slider.prototype.disable = function disable() {
  11887. if (!this.enabled()) {
  11888. return;
  11889. }
  11890. var doc = this.bar.el_.ownerDocument;
  11891. this.off('mousedown', this.handleMouseDown);
  11892. this.off('touchstart', this.handleMouseDown);
  11893. this.off('focus', this.handleFocus);
  11894. this.off('blur', this.handleBlur);
  11895. this.off('click', this.handleClick);
  11896. this.off(this.player_, 'controlsvisible', this.update);
  11897. this.off(doc, 'mousemove', this.handleMouseMove);
  11898. this.off(doc, 'mouseup', this.handleMouseUp);
  11899. this.off(doc, 'touchmove', this.handleMouseMove);
  11900. this.off(doc, 'touchend', this.handleMouseUp);
  11901. this.removeAttribute('tabindex');
  11902. this.addClass('disabled');
  11903. if (this.playerEvent) {
  11904. this.off(this.player_, this.playerEvent, this.update);
  11905. }
  11906. this.enabled_ = false;
  11907. };
  11908. /**
  11909. * Create the `Button`s DOM element.
  11910. *
  11911. * @param {string} type
  11912. * Type of element to create.
  11913. *
  11914. * @param {Object} [props={}]
  11915. * List of properties in Object form.
  11916. *
  11917. * @param {Object} [attributes={}]
  11918. * list of attributes in Object form.
  11919. *
  11920. * @return {Element}
  11921. * The element that gets created.
  11922. */
  11923. Slider.prototype.createEl = function createEl$$1(type) {
  11924. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  11925. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  11926. // Add the slider element class to all sub classes
  11927. props.className = props.className + ' vjs-slider';
  11928. props = assign({
  11929. tabIndex: 0
  11930. }, props);
  11931. attributes = assign({
  11932. 'role': 'slider',
  11933. 'aria-valuenow': 0,
  11934. 'aria-valuemin': 0,
  11935. 'aria-valuemax': 100,
  11936. 'tabIndex': 0
  11937. }, attributes);
  11938. return _Component.prototype.createEl.call(this, type, props, attributes);
  11939. };
  11940. /**
  11941. * Handle `mousedown` or `touchstart` events on the `Slider`.
  11942. *
  11943. * @param {EventTarget~Event} event
  11944. * `mousedown` or `touchstart` event that triggered this function
  11945. *
  11946. * @listens mousedown
  11947. * @listens touchstart
  11948. * @fires Slider#slideractive
  11949. */
  11950. Slider.prototype.handleMouseDown = function handleMouseDown(event) {
  11951. var doc = this.bar.el_.ownerDocument;
  11952. if (event.type === 'mousedown') {
  11953. event.preventDefault();
  11954. }
  11955. // Do not call preventDefault() on touchstart in Chrome
  11956. // to avoid console warnings. Use a 'touch-action: none' style
  11957. // instead to prevent unintented scrolling.
  11958. // https://developers.google.com/web/updates/2017/01/scrolling-intervention
  11959. if (event.type === 'touchstart' && !IS_CHROME) {
  11960. event.preventDefault();
  11961. }
  11962. blockTextSelection();
  11963. this.addClass('vjs-sliding');
  11964. /**
  11965. * Triggered when the slider is in an active state
  11966. *
  11967. * @event Slider#slideractive
  11968. * @type {EventTarget~Event}
  11969. */
  11970. this.trigger('slideractive');
  11971. this.on(doc, 'mousemove', this.handleMouseMove);
  11972. this.on(doc, 'mouseup', this.handleMouseUp);
  11973. this.on(doc, 'touchmove', this.handleMouseMove);
  11974. this.on(doc, 'touchend', this.handleMouseUp);
  11975. this.handleMouseMove(event);
  11976. };
  11977. /**
  11978. * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
  11979. * The `mousemove` and `touchmove` events will only only trigger this function during
  11980. * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
  11981. * {@link Slider#handleMouseUp}.
  11982. *
  11983. * @param {EventTarget~Event} event
  11984. * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
  11985. * this function
  11986. *
  11987. * @listens mousemove
  11988. * @listens touchmove
  11989. */
  11990. Slider.prototype.handleMouseMove = function handleMouseMove(event) {};
  11991. /**
  11992. * Handle `mouseup` or `touchend` events on the `Slider`.
  11993. *
  11994. * @param {EventTarget~Event} event
  11995. * `mouseup` or `touchend` event that triggered this function.
  11996. *
  11997. * @listens touchend
  11998. * @listens mouseup
  11999. * @fires Slider#sliderinactive
  12000. */
  12001. Slider.prototype.handleMouseUp = function handleMouseUp() {
  12002. var doc = this.bar.el_.ownerDocument;
  12003. unblockTextSelection();
  12004. this.removeClass('vjs-sliding');
  12005. /**
  12006. * Triggered when the slider is no longer in an active state.
  12007. *
  12008. * @event Slider#sliderinactive
  12009. * @type {EventTarget~Event}
  12010. */
  12011. this.trigger('sliderinactive');
  12012. this.off(doc, 'mousemove', this.handleMouseMove);
  12013. this.off(doc, 'mouseup', this.handleMouseUp);
  12014. this.off(doc, 'touchmove', this.handleMouseMove);
  12015. this.off(doc, 'touchend', this.handleMouseUp);
  12016. this.update();
  12017. };
  12018. /**
  12019. * Update the progress bar of the `Slider`.
  12020. *
  12021. * @returns {number}
  12022. * The percentage of progress the progress bar represents as a
  12023. * number from 0 to 1.
  12024. */
  12025. Slider.prototype.update = function update() {
  12026. // In VolumeBar init we have a setTimeout for update that pops and update
  12027. // to the end of the execution stack. The player is destroyed before then
  12028. // update will cause an error
  12029. if (!this.el_) {
  12030. return;
  12031. }
  12032. // If scrubbing, we could use a cached value to make the handle keep up
  12033. // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
  12034. // some flash players are slow, so we might want to utilize this later.
  12035. // var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
  12036. var progress = this.getPercent();
  12037. var bar = this.bar;
  12038. // If there's no bar...
  12039. if (!bar) {
  12040. return;
  12041. }
  12042. // Protect against no duration and other division issues
  12043. if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
  12044. progress = 0;
  12045. }
  12046. // Convert to a percentage for setting
  12047. var percentage = (progress * 100).toFixed(2) + '%';
  12048. var style = bar.el().style;
  12049. // Set the new bar width or height
  12050. if (this.vertical()) {
  12051. style.height = percentage;
  12052. } else {
  12053. style.width = percentage;
  12054. }
  12055. return progress;
  12056. };
  12057. /**
  12058. * Calculate distance for slider
  12059. *
  12060. * @param {EventTarget~Event} event
  12061. * The event that caused this function to run.
  12062. *
  12063. * @return {number}
  12064. * The current position of the Slider.
  12065. * - postition.x for vertical `Slider`s
  12066. * - postition.y for horizontal `Slider`s
  12067. */
  12068. Slider.prototype.calculateDistance = function calculateDistance(event) {
  12069. var position = getPointerPosition(this.el_, event);
  12070. if (this.vertical()) {
  12071. return position.y;
  12072. }
  12073. return position.x;
  12074. };
  12075. /**
  12076. * Handle a `focus` event on this `Slider`.
  12077. *
  12078. * @param {EventTarget~Event} event
  12079. * The `focus` event that caused this function to run.
  12080. *
  12081. * @listens focus
  12082. */
  12083. Slider.prototype.handleFocus = function handleFocus() {
  12084. this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  12085. };
  12086. /**
  12087. * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
  12088. * arrow keys. This function will only be called when the slider has focus. See
  12089. * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
  12090. *
  12091. * @param {EventTarget~Event} event
  12092. * the `keydown` event that caused this function to run.
  12093. *
  12094. * @listens keydown
  12095. */
  12096. Slider.prototype.handleKeyPress = function handleKeyPress(event) {
  12097. // Left and Down Arrows
  12098. if (event.which === 37 || event.which === 40) {
  12099. event.preventDefault();
  12100. this.stepBack();
  12101. // Up and Right Arrows
  12102. } else if (event.which === 38 || event.which === 39) {
  12103. event.preventDefault();
  12104. this.stepForward();
  12105. }
  12106. };
  12107. /**
  12108. * Handle a `blur` event on this `Slider`.
  12109. *
  12110. * @param {EventTarget~Event} event
  12111. * The `blur` event that caused this function to run.
  12112. *
  12113. * @listens blur
  12114. */
  12115. Slider.prototype.handleBlur = function handleBlur() {
  12116. this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  12117. };
  12118. /**
  12119. * Listener for click events on slider, used to prevent clicks
  12120. * from bubbling up to parent elements like button menus.
  12121. *
  12122. * @param {Object} event
  12123. * Event that caused this object to run
  12124. */
  12125. Slider.prototype.handleClick = function handleClick(event) {
  12126. event.stopImmediatePropagation();
  12127. event.preventDefault();
  12128. };
  12129. /**
  12130. * Get/set if slider is horizontal for vertical
  12131. *
  12132. * @param {boolean} [bool]
  12133. * - true if slider is vertical,
  12134. * - false is horizontal
  12135. *
  12136. * @return {boolean}
  12137. * - true if slider is vertical, and getting
  12138. * - false if the slider is horizontal, and getting
  12139. */
  12140. Slider.prototype.vertical = function vertical(bool) {
  12141. if (bool === undefined) {
  12142. return this.vertical_ || false;
  12143. }
  12144. this.vertical_ = !!bool;
  12145. if (this.vertical_) {
  12146. this.addClass('vjs-slider-vertical');
  12147. } else {
  12148. this.addClass('vjs-slider-horizontal');
  12149. }
  12150. };
  12151. return Slider;
  12152. }(Component);
  12153. Component.registerComponent('Slider', Slider);
  12154. /**
  12155. * @file load-progress-bar.js
  12156. */
  12157. /**
  12158. * Shows loading progress
  12159. *
  12160. * @extends Component
  12161. */
  12162. var LoadProgressBar = function (_Component) {
  12163. inherits(LoadProgressBar, _Component);
  12164. /**
  12165. * Creates an instance of this class.
  12166. *
  12167. * @param {Player} player
  12168. * The `Player` that this class should be attached to.
  12169. *
  12170. * @param {Object} [options]
  12171. * The key/value store of player options.
  12172. */
  12173. function LoadProgressBar(player, options) {
  12174. classCallCheck(this, LoadProgressBar);
  12175. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  12176. _this.partEls_ = [];
  12177. _this.on(player, 'progress', _this.update);
  12178. return _this;
  12179. }
  12180. /**
  12181. * Create the `Component`'s DOM element
  12182. *
  12183. * @return {Element}
  12184. * The element that was created.
  12185. */
  12186. LoadProgressBar.prototype.createEl = function createEl$$1() {
  12187. return _Component.prototype.createEl.call(this, 'div', {
  12188. className: 'vjs-load-progress',
  12189. innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
  12190. });
  12191. };
  12192. LoadProgressBar.prototype.dispose = function dispose() {
  12193. this.partEls_ = null;
  12194. _Component.prototype.dispose.call(this);
  12195. };
  12196. /**
  12197. * Update progress bar
  12198. *
  12199. * @param {EventTarget~Event} [event]
  12200. * The `progress` event that caused this function to run.
  12201. *
  12202. * @listens Player#progress
  12203. */
  12204. LoadProgressBar.prototype.update = function update(event) {
  12205. var buffered = this.player_.buffered();
  12206. var duration = this.player_.duration();
  12207. var bufferedEnd = this.player_.bufferedEnd();
  12208. var children = this.partEls_;
  12209. // get the percent width of a time compared to the total end
  12210. var percentify = function percentify(time, end) {
  12211. // no NaN
  12212. var percent = time / end || 0;
  12213. return (percent >= 1 ? 1 : percent) * 100 + '%';
  12214. };
  12215. // update the width of the progress bar
  12216. this.el_.style.width = percentify(bufferedEnd, duration);
  12217. // add child elements to represent the individual buffered time ranges
  12218. for (var i = 0; i < buffered.length; i++) {
  12219. var start = buffered.start(i);
  12220. var end = buffered.end(i);
  12221. var part = children[i];
  12222. if (!part) {
  12223. part = this.el_.appendChild(createEl());
  12224. children[i] = part;
  12225. }
  12226. // set the percent based on the width of the progress bar (bufferedEnd)
  12227. part.style.left = percentify(start, bufferedEnd);
  12228. part.style.width = percentify(end - start, bufferedEnd);
  12229. }
  12230. // remove unused buffered range elements
  12231. for (var _i = children.length; _i > buffered.length; _i--) {
  12232. this.el_.removeChild(children[_i - 1]);
  12233. }
  12234. children.length = buffered.length;
  12235. };
  12236. return LoadProgressBar;
  12237. }(Component);
  12238. Component.registerComponent('LoadProgressBar', LoadProgressBar);
  12239. /**
  12240. * @file time-tooltip.js
  12241. */
  12242. /**
  12243. * Time tooltips display a time above the progress bar.
  12244. *
  12245. * @extends Component
  12246. */
  12247. var TimeTooltip = function (_Component) {
  12248. inherits(TimeTooltip, _Component);
  12249. function TimeTooltip() {
  12250. classCallCheck(this, TimeTooltip);
  12251. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  12252. }
  12253. /**
  12254. * Create the time tooltip DOM element
  12255. *
  12256. * @return {Element}
  12257. * The element that was created.
  12258. */
  12259. TimeTooltip.prototype.createEl = function createEl$$1() {
  12260. return _Component.prototype.createEl.call(this, 'div', {
  12261. className: 'vjs-time-tooltip'
  12262. });
  12263. };
  12264. /**
  12265. * Updates the position of the time tooltip relative to the `SeekBar`.
  12266. *
  12267. * @param {Object} seekBarRect
  12268. * The `ClientRect` for the {@link SeekBar} element.
  12269. *
  12270. * @param {number} seekBarPoint
  12271. * A number from 0 to 1, representing a horizontal reference point
  12272. * from the left edge of the {@link SeekBar}
  12273. */
  12274. TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) {
  12275. var tooltipRect = getBoundingClientRect(this.el_);
  12276. var playerRect = getBoundingClientRect(this.player_.el());
  12277. var seekBarPointPx = seekBarRect.width * seekBarPoint;
  12278. // do nothing if either rect isn't available
  12279. // for example, if the player isn't in the DOM for testing
  12280. if (!playerRect || !tooltipRect) {
  12281. return;
  12282. }
  12283. // This is the space left of the `seekBarPoint` available within the bounds
  12284. // of the player. We calculate any gap between the left edge of the player
  12285. // and the left edge of the `SeekBar` and add the number of pixels in the
  12286. // `SeekBar` before hitting the `seekBarPoint`
  12287. var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;
  12288. // This is the space right of the `seekBarPoint` available within the bounds
  12289. // of the player. We calculate the number of pixels from the `seekBarPoint`
  12290. // to the right edge of the `SeekBar` and add to that any gap between the
  12291. // right edge of the `SeekBar` and the player.
  12292. var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);
  12293. // This is the number of pixels by which the tooltip will need to be pulled
  12294. // further to the right to center it over the `seekBarPoint`.
  12295. var pullTooltipBy = tooltipRect.width / 2;
  12296. // Adjust the `pullTooltipBy` distance to the left or right depending on
  12297. // the results of the space calculations above.
  12298. if (spaceLeftOfPoint < pullTooltipBy) {
  12299. pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
  12300. } else if (spaceRightOfPoint < pullTooltipBy) {
  12301. pullTooltipBy = spaceRightOfPoint;
  12302. }
  12303. // Due to the imprecision of decimal/ratio based calculations and varying
  12304. // rounding behaviors, there are cases where the spacing adjustment is off
  12305. // by a pixel or two. This adds insurance to these calculations.
  12306. if (pullTooltipBy < 0) {
  12307. pullTooltipBy = 0;
  12308. } else if (pullTooltipBy > tooltipRect.width) {
  12309. pullTooltipBy = tooltipRect.width;
  12310. }
  12311. this.el_.style.right = '-' + pullTooltipBy + 'px';
  12312. textContent(this.el_, content);
  12313. };
  12314. return TimeTooltip;
  12315. }(Component);
  12316. Component.registerComponent('TimeTooltip', TimeTooltip);
  12317. /**
  12318. * @file play-progress-bar.js
  12319. */
  12320. /**
  12321. * Used by {@link SeekBar} to display media playback progress as part of the
  12322. * {@link ProgressControl}.
  12323. *
  12324. * @extends Component
  12325. */
  12326. var PlayProgressBar = function (_Component) {
  12327. inherits(PlayProgressBar, _Component);
  12328. function PlayProgressBar() {
  12329. classCallCheck(this, PlayProgressBar);
  12330. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  12331. }
  12332. /**
  12333. * Create the the DOM element for this class.
  12334. *
  12335. * @return {Element}
  12336. * The element that was created.
  12337. */
  12338. PlayProgressBar.prototype.createEl = function createEl() {
  12339. return _Component.prototype.createEl.call(this, 'div', {
  12340. className: 'vjs-play-progress vjs-slider-bar',
  12341. innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
  12342. });
  12343. };
  12344. /**
  12345. * Enqueues updates to its own DOM as well as the DOM of its
  12346. * {@link TimeTooltip} child.
  12347. *
  12348. * @param {Object} seekBarRect
  12349. * The `ClientRect` for the {@link SeekBar} element.
  12350. *
  12351. * @param {number} seekBarPoint
  12352. * A number from 0 to 1, representing a horizontal reference point
  12353. * from the left edge of the {@link SeekBar}
  12354. */
  12355. PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) {
  12356. var _this2 = this;
  12357. // If there is an existing rAF ID, cancel it so we don't over-queue.
  12358. if (this.rafId_) {
  12359. this.cancelAnimationFrame(this.rafId_);
  12360. }
  12361. this.rafId_ = this.requestAnimationFrame(function () {
  12362. var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime();
  12363. var content = formatTime(time, _this2.player_.duration());
  12364. var timeTooltip = _this2.getChild('timeTooltip');
  12365. if (timeTooltip) {
  12366. timeTooltip.update(seekBarRect, seekBarPoint, content);
  12367. }
  12368. });
  12369. };
  12370. return PlayProgressBar;
  12371. }(Component);
  12372. /**
  12373. * Default options for {@link PlayProgressBar}.
  12374. *
  12375. * @type {Object}
  12376. * @private
  12377. */
  12378. PlayProgressBar.prototype.options_ = {
  12379. children: []
  12380. };
  12381. // Time tooltips should not be added to a player on mobile devices or IE8
  12382. if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
  12383. PlayProgressBar.prototype.options_.children.push('timeTooltip');
  12384. }
  12385. Component.registerComponent('PlayProgressBar', PlayProgressBar);
  12386. /**
  12387. * @file mouse-time-display.js
  12388. */
  12389. /**
  12390. * The {@link MouseTimeDisplay} component tracks mouse movement over the
  12391. * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
  12392. * indicating the time which is represented by a given point in the
  12393. * {@link ProgressControl}.
  12394. *
  12395. * @extends Component
  12396. */
  12397. var MouseTimeDisplay = function (_Component) {
  12398. inherits(MouseTimeDisplay, _Component);
  12399. /**
  12400. * Creates an instance of this class.
  12401. *
  12402. * @param {Player} player
  12403. * The {@link Player} that this class should be attached to.
  12404. *
  12405. * @param {Object} [options]
  12406. * The key/value store of player options.
  12407. */
  12408. function MouseTimeDisplay(player, options) {
  12409. classCallCheck(this, MouseTimeDisplay);
  12410. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  12411. _this.update = throttle(bind(_this, _this.update), 25);
  12412. return _this;
  12413. }
  12414. /**
  12415. * Create the DOM element for this class.
  12416. *
  12417. * @return {Element}
  12418. * The element that was created.
  12419. */
  12420. MouseTimeDisplay.prototype.createEl = function createEl() {
  12421. return _Component.prototype.createEl.call(this, 'div', {
  12422. className: 'vjs-mouse-display'
  12423. });
  12424. };
  12425. /**
  12426. * Enqueues updates to its own DOM as well as the DOM of its
  12427. * {@link TimeTooltip} child.
  12428. *
  12429. * @param {Object} seekBarRect
  12430. * The `ClientRect` for the {@link SeekBar} element.
  12431. *
  12432. * @param {number} seekBarPoint
  12433. * A number from 0 to 1, representing a horizontal reference point
  12434. * from the left edge of the {@link SeekBar}
  12435. */
  12436. MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) {
  12437. var _this2 = this;
  12438. // If there is an existing rAF ID, cancel it so we don't over-queue.
  12439. if (this.rafId_) {
  12440. this.cancelAnimationFrame(this.rafId_);
  12441. }
  12442. this.rafId_ = this.requestAnimationFrame(function () {
  12443. var duration = _this2.player_.duration();
  12444. var content = formatTime(seekBarPoint * duration, duration);
  12445. _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px';
  12446. _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
  12447. });
  12448. };
  12449. return MouseTimeDisplay;
  12450. }(Component);
  12451. /**
  12452. * Default options for `MouseTimeDisplay`
  12453. *
  12454. * @type {Object}
  12455. * @private
  12456. */
  12457. MouseTimeDisplay.prototype.options_ = {
  12458. children: ['timeTooltip']
  12459. };
  12460. Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
  12461. /**
  12462. * @file seek-bar.js
  12463. */
  12464. // The number of seconds the `step*` functions move the timeline.
  12465. var STEP_SECONDS = 5;
  12466. // The interval at which the bar should update as it progresses.
  12467. var UPDATE_REFRESH_INTERVAL = 30;
  12468. /**
  12469. * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
  12470. * as its `bar`.
  12471. *
  12472. * @extends Slider
  12473. */
  12474. var SeekBar = function (_Slider) {
  12475. inherits(SeekBar, _Slider);
  12476. /**
  12477. * Creates an instance of this class.
  12478. *
  12479. * @param {Player} player
  12480. * The `Player` that this class should be attached to.
  12481. *
  12482. * @param {Object} [options]
  12483. * The key/value store of player options.
  12484. */
  12485. function SeekBar(player, options) {
  12486. classCallCheck(this, SeekBar);
  12487. var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
  12488. _this.setEventHandlers_();
  12489. return _this;
  12490. }
  12491. /**
  12492. * Sets the event handlers
  12493. *
  12494. * @private
  12495. */
  12496. SeekBar.prototype.setEventHandlers_ = function setEventHandlers_() {
  12497. var _this2 = this;
  12498. this.update = throttle(bind(this, this.update), UPDATE_REFRESH_INTERVAL);
  12499. this.on(this.player_, 'timeupdate', this.update);
  12500. this.on(this.player_, 'ended', this.handleEnded);
  12501. // when playing, let's ensure we smoothly update the play progress bar
  12502. // via an interval
  12503. this.updateInterval = null;
  12504. this.on(this.player_, ['playing'], function () {
  12505. _this2.clearInterval(_this2.updateInterval);
  12506. _this2.updateInterval = _this2.setInterval(function () {
  12507. _this2.requestAnimationFrame(function () {
  12508. _this2.update();
  12509. });
  12510. }, UPDATE_REFRESH_INTERVAL);
  12511. });
  12512. this.on(this.player_, ['ended', 'pause', 'waiting'], function () {
  12513. _this2.clearInterval(_this2.updateInterval);
  12514. });
  12515. this.on(this.player_, ['timeupdate', 'ended'], this.update);
  12516. };
  12517. /**
  12518. * Create the `Component`'s DOM element
  12519. *
  12520. * @return {Element}
  12521. * The element that was created.
  12522. */
  12523. SeekBar.prototype.createEl = function createEl$$1() {
  12524. return _Slider.prototype.createEl.call(this, 'div', {
  12525. className: 'vjs-progress-holder'
  12526. }, {
  12527. 'aria-label': this.localize('Progress Bar')
  12528. });
  12529. };
  12530. /**
  12531. * This function updates the play progress bar and accessiblity
  12532. * attributes to whatever is passed in.
  12533. *
  12534. * @param {number} currentTime
  12535. * The currentTime value that should be used for accessiblity
  12536. *
  12537. * @param {number} percent
  12538. * The percentage as a decimal that the bar should be filled from 0-1.
  12539. *
  12540. * @private
  12541. */
  12542. SeekBar.prototype.update_ = function update_(currentTime, percent) {
  12543. var duration = this.player_.duration();
  12544. // machine readable value of progress bar (percentage complete)
  12545. this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
  12546. // human readable value of progress bar (time complete)
  12547. this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
  12548. // Update the `PlayProgressBar`.
  12549. this.bar.update(getBoundingClientRect(this.el_), percent);
  12550. };
  12551. /**
  12552. * Update the seek bar's UI.
  12553. *
  12554. * @param {EventTarget~Event} [event]
  12555. * The `timeupdate` or `ended` event that caused this to run.
  12556. *
  12557. * @listens Player#timeupdate
  12558. *
  12559. * @returns {number}
  12560. * The current percent at a number from 0-1
  12561. */
  12562. SeekBar.prototype.update = function update(event) {
  12563. var percent = _Slider.prototype.update.call(this);
  12564. this.update_(this.getCurrentTime_(), percent);
  12565. return percent;
  12566. };
  12567. /**
  12568. * Get the value of current time but allows for smooth scrubbing,
  12569. * when player can't keep up.
  12570. *
  12571. * @return {number}
  12572. * The current time value to display
  12573. *
  12574. * @private
  12575. */
  12576. SeekBar.prototype.getCurrentTime_ = function getCurrentTime_() {
  12577. return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  12578. };
  12579. /**
  12580. * We want the seek bar to be full on ended
  12581. * no matter what the actual internal values are. so we force it.
  12582. *
  12583. * @param {EventTarget~Event} [event]
  12584. * The `timeupdate` or `ended` event that caused this to run.
  12585. *
  12586. * @listens Player#ended
  12587. */
  12588. SeekBar.prototype.handleEnded = function handleEnded(event) {
  12589. this.update_(this.player_.duration(), 1);
  12590. };
  12591. /**
  12592. * Get the percentage of media played so far.
  12593. *
  12594. * @return {number}
  12595. * The percentage of media played so far (0 to 1).
  12596. */
  12597. SeekBar.prototype.getPercent = function getPercent() {
  12598. var percent = this.getCurrentTime_() / this.player_.duration();
  12599. return percent >= 1 ? 1 : percent;
  12600. };
  12601. /**
  12602. * Handle mouse down on seek bar
  12603. *
  12604. * @param {EventTarget~Event} event
  12605. * The `mousedown` event that caused this to run.
  12606. *
  12607. * @listens mousedown
  12608. */
  12609. SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
  12610. if (!isSingleLeftClick(event)) {
  12611. return;
  12612. }
  12613. // Stop event propagation to prevent double fire in progress-control.js
  12614. event.stopPropagation();
  12615. this.player_.scrubbing(true);
  12616. this.videoWasPlaying = !this.player_.paused();
  12617. this.player_.pause();
  12618. _Slider.prototype.handleMouseDown.call(this, event);
  12619. };
  12620. /**
  12621. * Handle mouse move on seek bar
  12622. *
  12623. * @param {EventTarget~Event} event
  12624. * The `mousemove` event that caused this to run.
  12625. *
  12626. * @listens mousemove
  12627. */
  12628. SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
  12629. if (!isSingleLeftClick(event)) {
  12630. return;
  12631. }
  12632. var newTime = this.calculateDistance(event) * this.player_.duration();
  12633. // Don't let video end while scrubbing.
  12634. if (newTime === this.player_.duration()) {
  12635. newTime = newTime - 0.1;
  12636. }
  12637. // Set new time (tell player to seek to new time)
  12638. this.player_.currentTime(newTime);
  12639. };
  12640. SeekBar.prototype.enable = function enable() {
  12641. _Slider.prototype.enable.call(this);
  12642. var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
  12643. if (!mouseTimeDisplay) {
  12644. return;
  12645. }
  12646. mouseTimeDisplay.show();
  12647. };
  12648. SeekBar.prototype.disable = function disable() {
  12649. _Slider.prototype.disable.call(this);
  12650. var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
  12651. if (!mouseTimeDisplay) {
  12652. return;
  12653. }
  12654. mouseTimeDisplay.hide();
  12655. };
  12656. /**
  12657. * Handle mouse up on seek bar
  12658. *
  12659. * @param {EventTarget~Event} event
  12660. * The `mouseup` event that caused this to run.
  12661. *
  12662. * @listens mouseup
  12663. */
  12664. SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
  12665. _Slider.prototype.handleMouseUp.call(this, event);
  12666. // Stop event propagation to prevent double fire in progress-control.js
  12667. if (event) {
  12668. event.stopPropagation();
  12669. }
  12670. this.player_.scrubbing(false);
  12671. /**
  12672. * Trigger timeupdate because we're done seeking and the time has changed.
  12673. * This is particularly useful for if the player is paused to time the time displays.
  12674. *
  12675. * @event Tech#timeupdate
  12676. * @type {EventTarget~Event}
  12677. */
  12678. this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  12679. if (this.videoWasPlaying) {
  12680. silencePromise(this.player_.play());
  12681. }
  12682. };
  12683. /**
  12684. * Move more quickly fast forward for keyboard-only users
  12685. */
  12686. SeekBar.prototype.stepForward = function stepForward() {
  12687. this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
  12688. };
  12689. /**
  12690. * Move more quickly rewind for keyboard-only users
  12691. */
  12692. SeekBar.prototype.stepBack = function stepBack() {
  12693. this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
  12694. };
  12695. /**
  12696. * Toggles the playback state of the player
  12697. * This gets called when enter or space is used on the seekbar
  12698. *
  12699. * @param {EventTarget~Event} event
  12700. * The `keydown` event that caused this function to be called
  12701. *
  12702. */
  12703. SeekBar.prototype.handleAction = function handleAction(event) {
  12704. if (this.player_.paused()) {
  12705. this.player_.play();
  12706. } else {
  12707. this.player_.pause();
  12708. }
  12709. };
  12710. /**
  12711. * Called when this SeekBar has focus and a key gets pressed down. By
  12712. * default it will call `this.handleAction` when the key is space or enter.
  12713. *
  12714. * @param {EventTarget~Event} event
  12715. * The `keydown` event that caused this function to be called.
  12716. *
  12717. * @listens keydown
  12718. */
  12719. SeekBar.prototype.handleKeyPress = function handleKeyPress(event) {
  12720. // Support Space (32) or Enter (13) key operation to fire a click event
  12721. if (event.which === 32 || event.which === 13) {
  12722. event.preventDefault();
  12723. this.handleAction(event);
  12724. } else if (_Slider.prototype.handleKeyPress) {
  12725. // Pass keypress handling up for unsupported keys
  12726. _Slider.prototype.handleKeyPress.call(this, event);
  12727. }
  12728. };
  12729. return SeekBar;
  12730. }(Slider);
  12731. /**
  12732. * Default options for the `SeekBar`
  12733. *
  12734. * @type {Object}
  12735. * @private
  12736. */
  12737. SeekBar.prototype.options_ = {
  12738. children: ['loadProgressBar', 'playProgressBar'],
  12739. barName: 'playProgressBar'
  12740. };
  12741. // MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8
  12742. if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
  12743. SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
  12744. }
  12745. /**
  12746. * Call the update event for this Slider when this event happens on the player.
  12747. *
  12748. * @type {string}
  12749. */
  12750. SeekBar.prototype.playerEvent = 'timeupdate';
  12751. Component.registerComponent('SeekBar', SeekBar);
  12752. /**
  12753. * @file progress-control.js
  12754. */
  12755. /**
  12756. * The Progress Control component contains the seek bar, load progress,
  12757. * and play progress.
  12758. *
  12759. * @extends Component
  12760. */
  12761. var ProgressControl = function (_Component) {
  12762. inherits(ProgressControl, _Component);
  12763. /**
  12764. * Creates an instance of this class.
  12765. *
  12766. * @param {Player} player
  12767. * The `Player` that this class should be attached to.
  12768. *
  12769. * @param {Object} [options]
  12770. * The key/value store of player options.
  12771. */
  12772. function ProgressControl(player, options) {
  12773. classCallCheck(this, ProgressControl);
  12774. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  12775. _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
  12776. _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25);
  12777. _this.enable();
  12778. return _this;
  12779. }
  12780. /**
  12781. * Create the `Component`'s DOM element
  12782. *
  12783. * @return {Element}
  12784. * The element that was created.
  12785. */
  12786. ProgressControl.prototype.createEl = function createEl$$1() {
  12787. return _Component.prototype.createEl.call(this, 'div', {
  12788. className: 'vjs-progress-control vjs-control'
  12789. });
  12790. };
  12791. /**
  12792. * When the mouse moves over the `ProgressControl`, the pointer position
  12793. * gets passed down to the `MouseTimeDisplay` component.
  12794. *
  12795. * @param {EventTarget~Event} event
  12796. * The `mousemove` event that caused this function to run.
  12797. *
  12798. * @listen mousemove
  12799. */
  12800. ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) {
  12801. var seekBar = this.getChild('seekBar');
  12802. if (seekBar) {
  12803. var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
  12804. var seekBarEl = seekBar.el();
  12805. var seekBarRect = getBoundingClientRect(seekBarEl);
  12806. var seekBarPoint = getPointerPosition(seekBarEl, event).x;
  12807. // The default skin has a gap on either side of the `SeekBar`. This means
  12808. // that it's possible to trigger this behavior outside the boundaries of
  12809. // the `SeekBar`. This ensures we stay within it at all times.
  12810. if (seekBarPoint > 1) {
  12811. seekBarPoint = 1;
  12812. } else if (seekBarPoint < 0) {
  12813. seekBarPoint = 0;
  12814. }
  12815. if (mouseTimeDisplay) {
  12816. mouseTimeDisplay.update(seekBarRect, seekBarPoint);
  12817. }
  12818. }
  12819. };
  12820. /**
  12821. * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
  12822. *
  12823. * @method ProgressControl#throttledHandleMouseSeek
  12824. * @param {EventTarget~Event} event
  12825. * The `mousemove` event that caused this function to run.
  12826. *
  12827. * @listen mousemove
  12828. * @listen touchmove
  12829. */
  12830. /**
  12831. * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
  12832. *
  12833. * @param {EventTarget~Event} event
  12834. * `mousedown` or `touchstart` event that triggered this function
  12835. *
  12836. * @listens mousemove
  12837. * @listens touchmove
  12838. */
  12839. ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) {
  12840. var seekBar = this.getChild('seekBar');
  12841. if (seekBar) {
  12842. seekBar.handleMouseMove(event);
  12843. }
  12844. };
  12845. /**
  12846. * Are controls are currently enabled for this progress control.
  12847. *
  12848. * @return {boolean}
  12849. * true if controls are enabled, false otherwise
  12850. */
  12851. ProgressControl.prototype.enabled = function enabled() {
  12852. return this.enabled_;
  12853. };
  12854. /**
  12855. * Disable all controls on the progress control and its children
  12856. */
  12857. ProgressControl.prototype.disable = function disable() {
  12858. this.children().forEach(function (child) {
  12859. return child.disable && child.disable();
  12860. });
  12861. if (!this.enabled()) {
  12862. return;
  12863. }
  12864. this.off(['mousedown', 'touchstart'], this.handleMouseDown);
  12865. this.off(this.el_, 'mousemove', this.handleMouseMove);
  12866. this.handleMouseUp();
  12867. this.addClass('disabled');
  12868. this.enabled_ = false;
  12869. };
  12870. /**
  12871. * Enable all controls on the progress control and its children
  12872. */
  12873. ProgressControl.prototype.enable = function enable() {
  12874. this.children().forEach(function (child) {
  12875. return child.enable && child.enable();
  12876. });
  12877. if (this.enabled()) {
  12878. return;
  12879. }
  12880. this.on(['mousedown', 'touchstart'], this.handleMouseDown);
  12881. this.on(this.el_, 'mousemove', this.handleMouseMove);
  12882. this.removeClass('disabled');
  12883. this.enabled_ = true;
  12884. };
  12885. /**
  12886. * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
  12887. *
  12888. * @param {EventTarget~Event} event
  12889. * `mousedown` or `touchstart` event that triggered this function
  12890. *
  12891. * @listens mousedown
  12892. * @listens touchstart
  12893. */
  12894. ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) {
  12895. var doc = this.el_.ownerDocument;
  12896. var seekBar = this.getChild('seekBar');
  12897. if (seekBar) {
  12898. seekBar.handleMouseDown(event);
  12899. }
  12900. this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
  12901. this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
  12902. this.on(doc, 'mouseup', this.handleMouseUp);
  12903. this.on(doc, 'touchend', this.handleMouseUp);
  12904. };
  12905. /**
  12906. * Handle `mouseup` or `touchend` events on the `ProgressControl`.
  12907. *
  12908. * @param {EventTarget~Event} event
  12909. * `mouseup` or `touchend` event that triggered this function.
  12910. *
  12911. * @listens touchend
  12912. * @listens mouseup
  12913. */
  12914. ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) {
  12915. var doc = this.el_.ownerDocument;
  12916. var seekBar = this.getChild('seekBar');
  12917. if (seekBar) {
  12918. seekBar.handleMouseUp(event);
  12919. }
  12920. this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
  12921. this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
  12922. this.off(doc, 'mouseup', this.handleMouseUp);
  12923. this.off(doc, 'touchend', this.handleMouseUp);
  12924. };
  12925. return ProgressControl;
  12926. }(Component);
  12927. /**
  12928. * Default options for `ProgressControl`
  12929. *
  12930. * @type {Object}
  12931. * @private
  12932. */
  12933. ProgressControl.prototype.options_ = {
  12934. children: ['seekBar']
  12935. };
  12936. Component.registerComponent('ProgressControl', ProgressControl);
  12937. /**
  12938. * @file fullscreen-toggle.js
  12939. */
  12940. /**
  12941. * Toggle fullscreen video
  12942. *
  12943. * @extends Button
  12944. */
  12945. var FullscreenToggle = function (_Button) {
  12946. inherits(FullscreenToggle, _Button);
  12947. /**
  12948. * Creates an instance of this class.
  12949. *
  12950. * @param {Player} player
  12951. * The `Player` that this class should be attached to.
  12952. *
  12953. * @param {Object} [options]
  12954. * The key/value store of player options.
  12955. */
  12956. function FullscreenToggle(player, options) {
  12957. classCallCheck(this, FullscreenToggle);
  12958. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  12959. _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
  12960. if (document_1[FullscreenApi.fullscreenEnabled] === false) {
  12961. _this.disable();
  12962. }
  12963. return _this;
  12964. }
  12965. /**
  12966. * Builds the default DOM `className`.
  12967. *
  12968. * @return {string}
  12969. * The DOM `className` for this object.
  12970. */
  12971. FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
  12972. return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
  12973. };
  12974. /**
  12975. * Handles fullscreenchange on the player and change control text accordingly.
  12976. *
  12977. * @param {EventTarget~Event} [event]
  12978. * The {@link Player#fullscreenchange} event that caused this function to be
  12979. * called.
  12980. *
  12981. * @listens Player#fullscreenchange
  12982. */
  12983. FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
  12984. if (this.player_.isFullscreen()) {
  12985. this.controlText('Non-Fullscreen');
  12986. } else {
  12987. this.controlText('Fullscreen');
  12988. }
  12989. };
  12990. /**
  12991. * This gets called when an `FullscreenToggle` is "clicked". See
  12992. * {@link ClickableComponent} for more detailed information on what a click can be.
  12993. *
  12994. * @param {EventTarget~Event} [event]
  12995. * The `keydown`, `tap`, or `click` event that caused this function to be
  12996. * called.
  12997. *
  12998. * @listens tap
  12999. * @listens click
  13000. */
  13001. FullscreenToggle.prototype.handleClick = function handleClick(event) {
  13002. if (!this.player_.isFullscreen()) {
  13003. this.player_.requestFullscreen();
  13004. } else {
  13005. this.player_.exitFullscreen();
  13006. }
  13007. };
  13008. return FullscreenToggle;
  13009. }(Button);
  13010. /**
  13011. * The text that should display over the `FullscreenToggle`s controls. Added for localization.
  13012. *
  13013. * @type {string}
  13014. * @private
  13015. */
  13016. FullscreenToggle.prototype.controlText_ = 'Fullscreen';
  13017. Component.registerComponent('FullscreenToggle', FullscreenToggle);
  13018. /**
  13019. * Check if volume control is supported and if it isn't hide the
  13020. * `Component` that was passed using the `vjs-hidden` class.
  13021. *
  13022. * @param {Component} self
  13023. * The component that should be hidden if volume is unsupported
  13024. *
  13025. * @param {Player} player
  13026. * A reference to the player
  13027. *
  13028. * @private
  13029. */
  13030. var checkVolumeSupport = function checkVolumeSupport(self, player) {
  13031. // hide volume controls when they're not supported by the current tech
  13032. if (player.tech_ && !player.tech_.featuresVolumeControl) {
  13033. self.addClass('vjs-hidden');
  13034. }
  13035. self.on(player, 'loadstart', function () {
  13036. if (!player.tech_.featuresVolumeControl) {
  13037. self.addClass('vjs-hidden');
  13038. } else {
  13039. self.removeClass('vjs-hidden');
  13040. }
  13041. });
  13042. };
  13043. /**
  13044. * @file volume-level.js
  13045. */
  13046. /**
  13047. * Shows volume level
  13048. *
  13049. * @extends Component
  13050. */
  13051. var VolumeLevel = function (_Component) {
  13052. inherits(VolumeLevel, _Component);
  13053. function VolumeLevel() {
  13054. classCallCheck(this, VolumeLevel);
  13055. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  13056. }
  13057. /**
  13058. * Create the `Component`'s DOM element
  13059. *
  13060. * @return {Element}
  13061. * The element that was created.
  13062. */
  13063. VolumeLevel.prototype.createEl = function createEl() {
  13064. return _Component.prototype.createEl.call(this, 'div', {
  13065. className: 'vjs-volume-level',
  13066. innerHTML: '<span class="vjs-control-text"></span>'
  13067. });
  13068. };
  13069. return VolumeLevel;
  13070. }(Component);
  13071. Component.registerComponent('VolumeLevel', VolumeLevel);
  13072. /**
  13073. * @file volume-bar.js
  13074. */
  13075. // Required children
  13076. /**
  13077. * The bar that contains the volume level and can be clicked on to adjust the level
  13078. *
  13079. * @extends Slider
  13080. */
  13081. var VolumeBar = function (_Slider) {
  13082. inherits(VolumeBar, _Slider);
  13083. /**
  13084. * Creates an instance of this class.
  13085. *
  13086. * @param {Player} player
  13087. * The `Player` that this class should be attached to.
  13088. *
  13089. * @param {Object} [options]
  13090. * The key/value store of player options.
  13091. */
  13092. function VolumeBar(player, options) {
  13093. classCallCheck(this, VolumeBar);
  13094. var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
  13095. _this.on('slideractive', _this.updateLastVolume_);
  13096. _this.on(player, 'volumechange', _this.updateARIAAttributes);
  13097. player.ready(function () {
  13098. return _this.updateARIAAttributes();
  13099. });
  13100. return _this;
  13101. }
  13102. /**
  13103. * Create the `Component`'s DOM element
  13104. *
  13105. * @return {Element}
  13106. * The element that was created.
  13107. */
  13108. VolumeBar.prototype.createEl = function createEl$$1() {
  13109. return _Slider.prototype.createEl.call(this, 'div', {
  13110. className: 'vjs-volume-bar vjs-slider-bar'
  13111. }, {
  13112. 'aria-label': this.localize('Volume Level'),
  13113. 'aria-live': 'polite'
  13114. });
  13115. };
  13116. /**
  13117. * Handle mouse down on volume bar
  13118. *
  13119. * @param {EventTarget~Event} event
  13120. * The `mousedown` event that caused this to run.
  13121. *
  13122. * @listens mousedown
  13123. */
  13124. VolumeBar.prototype.handleMouseDown = function handleMouseDown(event) {
  13125. if (!isSingleLeftClick(event)) {
  13126. return;
  13127. }
  13128. _Slider.prototype.handleMouseDown.call(this, event);
  13129. };
  13130. /**
  13131. * Handle movement events on the {@link VolumeMenuButton}.
  13132. *
  13133. * @param {EventTarget~Event} event
  13134. * The event that caused this function to run.
  13135. *
  13136. * @listens mousemove
  13137. */
  13138. VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
  13139. if (!isSingleLeftClick(event)) {
  13140. return;
  13141. }
  13142. this.checkMuted();
  13143. this.player_.volume(this.calculateDistance(event));
  13144. };
  13145. /**
  13146. * If the player is muted unmute it.
  13147. */
  13148. VolumeBar.prototype.checkMuted = function checkMuted() {
  13149. if (this.player_.muted()) {
  13150. this.player_.muted(false);
  13151. }
  13152. };
  13153. /**
  13154. * Get percent of volume level
  13155. *
  13156. * @return {number}
  13157. * Volume level percent as a decimal number.
  13158. */
  13159. VolumeBar.prototype.getPercent = function getPercent() {
  13160. if (this.player_.muted()) {
  13161. return 0;
  13162. }
  13163. return this.player_.volume();
  13164. };
  13165. /**
  13166. * Increase volume level for keyboard users
  13167. */
  13168. VolumeBar.prototype.stepForward = function stepForward() {
  13169. this.checkMuted();
  13170. this.player_.volume(this.player_.volume() + 0.1);
  13171. };
  13172. /**
  13173. * Decrease volume level for keyboard users
  13174. */
  13175. VolumeBar.prototype.stepBack = function stepBack() {
  13176. this.checkMuted();
  13177. this.player_.volume(this.player_.volume() - 0.1);
  13178. };
  13179. /**
  13180. * Update ARIA accessibility attributes
  13181. *
  13182. * @param {EventTarget~Event} [event]
  13183. * The `volumechange` event that caused this function to run.
  13184. *
  13185. * @listens Player#volumechange
  13186. */
  13187. VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
  13188. var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
  13189. this.el_.setAttribute('aria-valuenow', ariaValue);
  13190. this.el_.setAttribute('aria-valuetext', ariaValue + '%');
  13191. };
  13192. /**
  13193. * Returns the current value of the player volume as a percentage
  13194. *
  13195. * @private
  13196. */
  13197. VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() {
  13198. return Math.round(this.player_.volume() * 100);
  13199. };
  13200. /**
  13201. * When user starts dragging the VolumeBar, store the volume and listen for
  13202. * the end of the drag. When the drag ends, if the volume was set to zero,
  13203. * set lastVolume to the stored volume.
  13204. *
  13205. * @listens slideractive
  13206. * @private
  13207. */
  13208. VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() {
  13209. var _this2 = this;
  13210. var volumeBeforeDrag = this.player_.volume();
  13211. this.one('sliderinactive', function () {
  13212. if (_this2.player_.volume() === 0) {
  13213. _this2.player_.lastVolume_(volumeBeforeDrag);
  13214. }
  13215. });
  13216. };
  13217. return VolumeBar;
  13218. }(Slider);
  13219. /**
  13220. * Default options for the `VolumeBar`
  13221. *
  13222. * @type {Object}
  13223. * @private
  13224. */
  13225. VolumeBar.prototype.options_ = {
  13226. children: ['volumeLevel'],
  13227. barName: 'volumeLevel'
  13228. };
  13229. /**
  13230. * Call the update event for this Slider when this event happens on the player.
  13231. *
  13232. * @type {string}
  13233. */
  13234. VolumeBar.prototype.playerEvent = 'volumechange';
  13235. Component.registerComponent('VolumeBar', VolumeBar);
  13236. /**
  13237. * @file volume-control.js
  13238. */
  13239. // Required children
  13240. /**
  13241. * The component for controlling the volume level
  13242. *
  13243. * @extends Component
  13244. */
  13245. var VolumeControl = function (_Component) {
  13246. inherits(VolumeControl, _Component);
  13247. /**
  13248. * Creates an instance of this class.
  13249. *
  13250. * @param {Player} player
  13251. * The `Player` that this class should be attached to.
  13252. *
  13253. * @param {Object} [options={}]
  13254. * The key/value store of player options.
  13255. */
  13256. function VolumeControl(player) {
  13257. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13258. classCallCheck(this, VolumeControl);
  13259. options.vertical = options.vertical || false;
  13260. // Pass the vertical option down to the VolumeBar if
  13261. // the VolumeBar is turned on.
  13262. if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
  13263. options.volumeBar = options.volumeBar || {};
  13264. options.volumeBar.vertical = options.vertical;
  13265. }
  13266. // hide this control if volume support is missing
  13267. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  13268. checkVolumeSupport(_this, player);
  13269. _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
  13270. _this.on('mousedown', _this.handleMouseDown);
  13271. _this.on('touchstart', _this.handleMouseDown);
  13272. // while the slider is active (the mouse has been pressed down and
  13273. // is dragging) or in focus we do not want to hide the VolumeBar
  13274. _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
  13275. _this.volumeBar.addClass('vjs-slider-active');
  13276. _this.addClass('vjs-slider-active');
  13277. _this.trigger('slideractive');
  13278. });
  13279. _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
  13280. _this.volumeBar.removeClass('vjs-slider-active');
  13281. _this.removeClass('vjs-slider-active');
  13282. _this.trigger('sliderinactive');
  13283. });
  13284. return _this;
  13285. }
  13286. /**
  13287. * Create the `Component`'s DOM element
  13288. *
  13289. * @return {Element}
  13290. * The element that was created.
  13291. */
  13292. VolumeControl.prototype.createEl = function createEl() {
  13293. var orientationClass = 'vjs-volume-horizontal';
  13294. if (this.options_.vertical) {
  13295. orientationClass = 'vjs-volume-vertical';
  13296. }
  13297. return _Component.prototype.createEl.call(this, 'div', {
  13298. className: 'vjs-volume-control vjs-control ' + orientationClass
  13299. });
  13300. };
  13301. /**
  13302. * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
  13303. *
  13304. * @param {EventTarget~Event} event
  13305. * `mousedown` or `touchstart` event that triggered this function
  13306. *
  13307. * @listens mousedown
  13308. * @listens touchstart
  13309. */
  13310. VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) {
  13311. var doc = this.el_.ownerDocument;
  13312. this.on(doc, 'mousemove', this.throttledHandleMouseMove);
  13313. this.on(doc, 'touchmove', this.throttledHandleMouseMove);
  13314. this.on(doc, 'mouseup', this.handleMouseUp);
  13315. this.on(doc, 'touchend', this.handleMouseUp);
  13316. };
  13317. /**
  13318. * Handle `mouseup` or `touchend` events on the `VolumeControl`.
  13319. *
  13320. * @param {EventTarget~Event} event
  13321. * `mouseup` or `touchend` event that triggered this function.
  13322. *
  13323. * @listens touchend
  13324. * @listens mouseup
  13325. */
  13326. VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) {
  13327. var doc = this.el_.ownerDocument;
  13328. this.off(doc, 'mousemove', this.throttledHandleMouseMove);
  13329. this.off(doc, 'touchmove', this.throttledHandleMouseMove);
  13330. this.off(doc, 'mouseup', this.handleMouseUp);
  13331. this.off(doc, 'touchend', this.handleMouseUp);
  13332. };
  13333. /**
  13334. * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
  13335. *
  13336. * @param {EventTarget~Event} event
  13337. * `mousedown` or `touchstart` event that triggered this function
  13338. *
  13339. * @listens mousedown
  13340. * @listens touchstart
  13341. */
  13342. VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) {
  13343. this.volumeBar.handleMouseMove(event);
  13344. };
  13345. return VolumeControl;
  13346. }(Component);
  13347. /**
  13348. * Default options for the `VolumeControl`
  13349. *
  13350. * @type {Object}
  13351. * @private
  13352. */
  13353. VolumeControl.prototype.options_ = {
  13354. children: ['volumeBar']
  13355. };
  13356. Component.registerComponent('VolumeControl', VolumeControl);
  13357. /**
  13358. * Check if muting volume is supported and if it isn't hide the mute toggle
  13359. * button.
  13360. *
  13361. * @param {Component} self
  13362. * A reference to the mute toggle button
  13363. *
  13364. * @param {Player} player
  13365. * A reference to the player
  13366. *
  13367. * @private
  13368. */
  13369. var checkMuteSupport = function checkMuteSupport(self, player) {
  13370. // hide mute toggle button if it's not supported by the current tech
  13371. if (player.tech_ && !player.tech_.featuresMuteControl) {
  13372. self.addClass('vjs-hidden');
  13373. }
  13374. self.on(player, 'loadstart', function () {
  13375. if (!player.tech_.featuresMuteControl) {
  13376. self.addClass('vjs-hidden');
  13377. } else {
  13378. self.removeClass('vjs-hidden');
  13379. }
  13380. });
  13381. };
  13382. /**
  13383. * @file mute-toggle.js
  13384. */
  13385. /**
  13386. * A button component for muting the audio.
  13387. *
  13388. * @extends Button
  13389. */
  13390. var MuteToggle = function (_Button) {
  13391. inherits(MuteToggle, _Button);
  13392. /**
  13393. * Creates an instance of this class.
  13394. *
  13395. * @param {Player} player
  13396. * The `Player` that this class should be attached to.
  13397. *
  13398. * @param {Object} [options]
  13399. * The key/value store of player options.
  13400. */
  13401. function MuteToggle(player, options) {
  13402. classCallCheck(this, MuteToggle);
  13403. // hide this control if volume support is missing
  13404. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  13405. checkMuteSupport(_this, player);
  13406. _this.on(player, ['loadstart', 'volumechange'], _this.update);
  13407. return _this;
  13408. }
  13409. /**
  13410. * Builds the default DOM `className`.
  13411. *
  13412. * @return {string}
  13413. * The DOM `className` for this object.
  13414. */
  13415. MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
  13416. return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
  13417. };
  13418. /**
  13419. * This gets called when an `MuteToggle` is "clicked". See
  13420. * {@link ClickableComponent} for more detailed information on what a click can be.
  13421. *
  13422. * @param {EventTarget~Event} [event]
  13423. * The `keydown`, `tap`, or `click` event that caused this function to be
  13424. * called.
  13425. *
  13426. * @listens tap
  13427. * @listens click
  13428. */
  13429. MuteToggle.prototype.handleClick = function handleClick(event) {
  13430. var vol = this.player_.volume();
  13431. var lastVolume = this.player_.lastVolume_();
  13432. if (vol === 0) {
  13433. var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
  13434. this.player_.volume(volumeToSet);
  13435. this.player_.muted(false);
  13436. } else {
  13437. this.player_.muted(this.player_.muted() ? false : true);
  13438. }
  13439. };
  13440. /**
  13441. * Update the `MuteToggle` button based on the state of `volume` and `muted`
  13442. * on the player.
  13443. *
  13444. * @param {EventTarget~Event} [event]
  13445. * The {@link Player#loadstart} event if this function was called
  13446. * through an event.
  13447. *
  13448. * @listens Player#loadstart
  13449. * @listens Player#volumechange
  13450. */
  13451. MuteToggle.prototype.update = function update(event) {
  13452. this.updateIcon_();
  13453. this.updateControlText_();
  13454. };
  13455. /**
  13456. * Update the appearance of the `MuteToggle` icon.
  13457. *
  13458. * Possible states (given `level` variable below):
  13459. * - 0: crossed out
  13460. * - 1: zero bars of volume
  13461. * - 2: one bar of volume
  13462. * - 3: two bars of volume
  13463. *
  13464. * @private
  13465. */
  13466. MuteToggle.prototype.updateIcon_ = function updateIcon_() {
  13467. var vol = this.player_.volume();
  13468. var level = 3;
  13469. // in iOS when a player is loaded with muted attribute
  13470. // and volume is changed with a native mute button
  13471. // we want to make sure muted state is updated
  13472. if (IS_IOS) {
  13473. this.player_.muted(this.player_.tech_.el_.muted);
  13474. }
  13475. if (vol === 0 || this.player_.muted()) {
  13476. level = 0;
  13477. } else if (vol < 0.33) {
  13478. level = 1;
  13479. } else if (vol < 0.67) {
  13480. level = 2;
  13481. }
  13482. // TODO improve muted icon classes
  13483. for (var i = 0; i < 4; i++) {
  13484. removeClass(this.el_, 'vjs-vol-' + i);
  13485. }
  13486. addClass(this.el_, 'vjs-vol-' + level);
  13487. };
  13488. /**
  13489. * If `muted` has changed on the player, update the control text
  13490. * (`title` attribute on `vjs-mute-control` element and content of
  13491. * `vjs-control-text` element).
  13492. *
  13493. * @private
  13494. */
  13495. MuteToggle.prototype.updateControlText_ = function updateControlText_() {
  13496. var soundOff = this.player_.muted() || this.player_.volume() === 0;
  13497. var text = soundOff ? 'Unmute' : 'Mute';
  13498. if (this.controlText() !== text) {
  13499. this.controlText(text);
  13500. }
  13501. };
  13502. return MuteToggle;
  13503. }(Button);
  13504. /**
  13505. * The text that should display over the `MuteToggle`s controls. Added for localization.
  13506. *
  13507. * @type {string}
  13508. * @private
  13509. */
  13510. MuteToggle.prototype.controlText_ = 'Mute';
  13511. Component.registerComponent('MuteToggle', MuteToggle);
  13512. /**
  13513. * @file volume-control.js
  13514. */
  13515. // Required children
  13516. /**
  13517. * A Component to contain the MuteToggle and VolumeControl so that
  13518. * they can work together.
  13519. *
  13520. * @extends Component
  13521. */
  13522. var VolumePanel = function (_Component) {
  13523. inherits(VolumePanel, _Component);
  13524. /**
  13525. * Creates an instance of this class.
  13526. *
  13527. * @param {Player} player
  13528. * The `Player` that this class should be attached to.
  13529. *
  13530. * @param {Object} [options={}]
  13531. * The key/value store of player options.
  13532. */
  13533. function VolumePanel(player) {
  13534. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13535. classCallCheck(this, VolumePanel);
  13536. if (typeof options.inline !== 'undefined') {
  13537. options.inline = options.inline;
  13538. } else {
  13539. options.inline = true;
  13540. }
  13541. // pass the inline option down to the VolumeControl as vertical if
  13542. // the VolumeControl is on.
  13543. if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
  13544. options.volumeControl = options.volumeControl || {};
  13545. options.volumeControl.vertical = !options.inline;
  13546. }
  13547. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  13548. _this.on(player, ['loadstart'], _this.volumePanelState_);
  13549. // while the slider is active (the mouse has been pressed down and
  13550. // is dragging) we do not want to hide the VolumeBar
  13551. _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
  13552. _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
  13553. return _this;
  13554. }
  13555. /**
  13556. * Add vjs-slider-active class to the VolumePanel
  13557. *
  13558. * @listens VolumeControl#slideractive
  13559. * @private
  13560. */
  13561. VolumePanel.prototype.sliderActive_ = function sliderActive_() {
  13562. this.addClass('vjs-slider-active');
  13563. };
  13564. /**
  13565. * Removes vjs-slider-active class to the VolumePanel
  13566. *
  13567. * @listens VolumeControl#sliderinactive
  13568. * @private
  13569. */
  13570. VolumePanel.prototype.sliderInactive_ = function sliderInactive_() {
  13571. this.removeClass('vjs-slider-active');
  13572. };
  13573. /**
  13574. * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
  13575. * depending on MuteToggle and VolumeControl state
  13576. *
  13577. * @listens Player#loadstart
  13578. * @private
  13579. */
  13580. VolumePanel.prototype.volumePanelState_ = function volumePanelState_() {
  13581. // hide volume panel if neither volume control or mute toggle
  13582. // are displayed
  13583. if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
  13584. this.addClass('vjs-hidden');
  13585. }
  13586. // if only mute toggle is visible we don't want
  13587. // volume panel expanding when hovered or active
  13588. if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
  13589. this.addClass('vjs-mute-toggle-only');
  13590. }
  13591. };
  13592. /**
  13593. * Create the `Component`'s DOM element
  13594. *
  13595. * @return {Element}
  13596. * The element that was created.
  13597. */
  13598. VolumePanel.prototype.createEl = function createEl() {
  13599. var orientationClass = 'vjs-volume-panel-horizontal';
  13600. if (!this.options_.inline) {
  13601. orientationClass = 'vjs-volume-panel-vertical';
  13602. }
  13603. return _Component.prototype.createEl.call(this, 'div', {
  13604. className: 'vjs-volume-panel vjs-control ' + orientationClass
  13605. });
  13606. };
  13607. return VolumePanel;
  13608. }(Component);
  13609. /**
  13610. * Default options for the `VolumeControl`
  13611. *
  13612. * @type {Object}
  13613. * @private
  13614. */
  13615. VolumePanel.prototype.options_ = {
  13616. children: ['muteToggle', 'volumeControl']
  13617. };
  13618. Component.registerComponent('VolumePanel', VolumePanel);
  13619. /**
  13620. * @file menu.js
  13621. */
  13622. /**
  13623. * The Menu component is used to build popup menus, including subtitle and
  13624. * captions selection menus.
  13625. *
  13626. * @extends Component
  13627. */
  13628. var Menu = function (_Component) {
  13629. inherits(Menu, _Component);
  13630. /**
  13631. * Create an instance of this class.
  13632. *
  13633. * @param {Player} player
  13634. * the player that this component should attach to
  13635. *
  13636. * @param {Object} [options]
  13637. * Object of option names and values
  13638. *
  13639. */
  13640. function Menu(player, options) {
  13641. classCallCheck(this, Menu);
  13642. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  13643. if (options) {
  13644. _this.menuButton_ = options.menuButton;
  13645. }
  13646. _this.focusedChild_ = -1;
  13647. _this.on('keydown', _this.handleKeyPress);
  13648. return _this;
  13649. }
  13650. /**
  13651. * Add a {@link MenuItem} to the menu.
  13652. *
  13653. * @param {Object|string} component
  13654. * The name or instance of the `MenuItem` to add.
  13655. *
  13656. */
  13657. Menu.prototype.addItem = function addItem(component) {
  13658. this.addChild(component);
  13659. component.on('click', bind(this, function (event) {
  13660. // Unpress the associated MenuButton, and move focus back to it
  13661. if (this.menuButton_) {
  13662. this.menuButton_.unpressButton();
  13663. // don't focus menu button if item is a caption settings item
  13664. // because focus will move elsewhere and it logs an error on IE8
  13665. if (component.name() !== 'CaptionSettingsMenuItem') {
  13666. this.menuButton_.focus();
  13667. }
  13668. }
  13669. }));
  13670. };
  13671. /**
  13672. * Create the `Menu`s DOM element.
  13673. *
  13674. * @return {Element}
  13675. * the element that was created
  13676. */
  13677. Menu.prototype.createEl = function createEl$$1() {
  13678. var contentElType = this.options_.contentElType || 'ul';
  13679. this.contentEl_ = createEl(contentElType, {
  13680. className: 'vjs-menu-content'
  13681. });
  13682. this.contentEl_.setAttribute('role', 'menu');
  13683. var el = _Component.prototype.createEl.call(this, 'div', {
  13684. append: this.contentEl_,
  13685. className: 'vjs-menu'
  13686. });
  13687. el.appendChild(this.contentEl_);
  13688. // Prevent clicks from bubbling up. Needed for Menu Buttons,
  13689. // where a click on the parent is significant
  13690. on(el, 'click', function (event) {
  13691. event.preventDefault();
  13692. event.stopImmediatePropagation();
  13693. });
  13694. return el;
  13695. };
  13696. Menu.prototype.dispose = function dispose() {
  13697. this.contentEl_ = null;
  13698. _Component.prototype.dispose.call(this);
  13699. };
  13700. /**
  13701. * Handle a `keydown` event on this menu. This listener is added in the constructor.
  13702. *
  13703. * @param {EventTarget~Event} event
  13704. * A `keydown` event that happened on the menu.
  13705. *
  13706. * @listens keydown
  13707. */
  13708. Menu.prototype.handleKeyPress = function handleKeyPress(event) {
  13709. // Left and Down Arrows
  13710. if (event.which === 37 || event.which === 40) {
  13711. event.preventDefault();
  13712. this.stepForward();
  13713. // Up and Right Arrows
  13714. } else if (event.which === 38 || event.which === 39) {
  13715. event.preventDefault();
  13716. this.stepBack();
  13717. }
  13718. };
  13719. /**
  13720. * Move to next (lower) menu item for keyboard users.
  13721. */
  13722. Menu.prototype.stepForward = function stepForward() {
  13723. var stepChild = 0;
  13724. if (this.focusedChild_ !== undefined) {
  13725. stepChild = this.focusedChild_ + 1;
  13726. }
  13727. this.focus(stepChild);
  13728. };
  13729. /**
  13730. * Move to previous (higher) menu item for keyboard users.
  13731. */
  13732. Menu.prototype.stepBack = function stepBack() {
  13733. var stepChild = 0;
  13734. if (this.focusedChild_ !== undefined) {
  13735. stepChild = this.focusedChild_ - 1;
  13736. }
  13737. this.focus(stepChild);
  13738. };
  13739. /**
  13740. * Set focus on a {@link MenuItem} in the `Menu`.
  13741. *
  13742. * @param {Object|string} [item=0]
  13743. * Index of child item set focus on.
  13744. */
  13745. Menu.prototype.focus = function focus() {
  13746. var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  13747. var children = this.children().slice();
  13748. var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
  13749. if (haveTitle) {
  13750. children.shift();
  13751. }
  13752. if (children.length > 0) {
  13753. if (item < 0) {
  13754. item = 0;
  13755. } else if (item >= children.length) {
  13756. item = children.length - 1;
  13757. }
  13758. this.focusedChild_ = item;
  13759. children[item].el_.focus();
  13760. }
  13761. };
  13762. return Menu;
  13763. }(Component);
  13764. Component.registerComponent('Menu', Menu);
  13765. /**
  13766. * @file menu-button.js
  13767. */
  13768. /**
  13769. * A `MenuButton` class for any popup {@link Menu}.
  13770. *
  13771. * @extends Component
  13772. */
  13773. var MenuButton = function (_Component) {
  13774. inherits(MenuButton, _Component);
  13775. /**
  13776. * Creates an instance of this class.
  13777. *
  13778. * @param {Player} player
  13779. * The `Player` that this class should be attached to.
  13780. *
  13781. * @param {Object} [options={}]
  13782. * The key/value store of player options.
  13783. */
  13784. function MenuButton(player) {
  13785. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13786. classCallCheck(this, MenuButton);
  13787. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  13788. _this.menuButton_ = new Button(player, options);
  13789. _this.menuButton_.controlText(_this.controlText_);
  13790. _this.menuButton_.el_.setAttribute('aria-haspopup', 'true');
  13791. // Add buildCSSClass values to the button, not the wrapper
  13792. var buttonClass = Button.prototype.buildCSSClass();
  13793. _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
  13794. _this.menuButton_.removeClass('vjs-control');
  13795. _this.addChild(_this.menuButton_);
  13796. _this.update();
  13797. _this.enabled_ = true;
  13798. _this.on(_this.menuButton_, 'tap', _this.handleClick);
  13799. _this.on(_this.menuButton_, 'click', _this.handleClick);
  13800. _this.on(_this.menuButton_, 'focus', _this.handleFocus);
  13801. _this.on(_this.menuButton_, 'blur', _this.handleBlur);
  13802. _this.on('keydown', _this.handleSubmenuKeyPress);
  13803. return _this;
  13804. }
  13805. /**
  13806. * Update the menu based on the current state of its items.
  13807. */
  13808. MenuButton.prototype.update = function update() {
  13809. var menu = this.createMenu();
  13810. if (this.menu) {
  13811. this.menu.dispose();
  13812. this.removeChild(this.menu);
  13813. }
  13814. this.menu = menu;
  13815. this.addChild(menu);
  13816. /**
  13817. * Track the state of the menu button
  13818. *
  13819. * @type {Boolean}
  13820. * @private
  13821. */
  13822. this.buttonPressed_ = false;
  13823. this.menuButton_.el_.setAttribute('aria-expanded', 'false');
  13824. if (this.items && this.items.length <= this.hideThreshold_) {
  13825. this.hide();
  13826. } else {
  13827. this.show();
  13828. }
  13829. };
  13830. /**
  13831. * Create the menu and add all items to it.
  13832. *
  13833. * @return {Menu}
  13834. * The constructed menu
  13835. */
  13836. MenuButton.prototype.createMenu = function createMenu() {
  13837. var menu = new Menu(this.player_, { menuButton: this });
  13838. /**
  13839. * Hide the menu if the number of items is less than or equal to this threshold. This defaults
  13840. * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
  13841. * it here because every time we run `createMenu` we need to reset the value.
  13842. *
  13843. * @protected
  13844. * @type {Number}
  13845. */
  13846. this.hideThreshold_ = 0;
  13847. // Add a title list item to the top
  13848. if (this.options_.title) {
  13849. var title = createEl('li', {
  13850. className: 'vjs-menu-title',
  13851. innerHTML: toTitleCase(this.options_.title),
  13852. tabIndex: -1
  13853. });
  13854. this.hideThreshold_ += 1;
  13855. menu.children_.unshift(title);
  13856. prependTo(title, menu.contentEl());
  13857. }
  13858. this.items = this.createItems();
  13859. if (this.items) {
  13860. // Add menu items to the menu
  13861. for (var i = 0; i < this.items.length; i++) {
  13862. menu.addItem(this.items[i]);
  13863. }
  13864. }
  13865. return menu;
  13866. };
  13867. /**
  13868. * Create the list of menu items. Specific to each subclass.
  13869. *
  13870. * @abstract
  13871. */
  13872. MenuButton.prototype.createItems = function createItems() {};
  13873. /**
  13874. * Create the `MenuButtons`s DOM element.
  13875. *
  13876. * @return {Element}
  13877. * The element that gets created.
  13878. */
  13879. MenuButton.prototype.createEl = function createEl$$1() {
  13880. return _Component.prototype.createEl.call(this, 'div', {
  13881. className: this.buildWrapperCSSClass()
  13882. }, {});
  13883. };
  13884. /**
  13885. * Allow sub components to stack CSS class names for the wrapper element
  13886. *
  13887. * @return {string}
  13888. * The constructed wrapper DOM `className`
  13889. */
  13890. MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13891. var menuButtonClass = 'vjs-menu-button';
  13892. // If the inline option is passed, we want to use different styles altogether.
  13893. if (this.options_.inline === true) {
  13894. menuButtonClass += '-inline';
  13895. } else {
  13896. menuButtonClass += '-popup';
  13897. }
  13898. // TODO: Fix the CSS so that this isn't necessary
  13899. var buttonClass = Button.prototype.buildCSSClass();
  13900. return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
  13901. };
  13902. /**
  13903. * Builds the default DOM `className`.
  13904. *
  13905. * @return {string}
  13906. * The DOM `className` for this object.
  13907. */
  13908. MenuButton.prototype.buildCSSClass = function buildCSSClass() {
  13909. var menuButtonClass = 'vjs-menu-button';
  13910. // If the inline option is passed, we want to use different styles altogether.
  13911. if (this.options_.inline === true) {
  13912. menuButtonClass += '-inline';
  13913. } else {
  13914. menuButtonClass += '-popup';
  13915. }
  13916. return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
  13917. };
  13918. /**
  13919. * Get or set the localized control text that will be used for accessibility.
  13920. *
  13921. * > NOTE: This will come from the internal `menuButton_` element.
  13922. *
  13923. * @param {string} [text]
  13924. * Control text for element.
  13925. *
  13926. * @param {Element} [el=this.menuButton_.el()]
  13927. * Element to set the title on.
  13928. *
  13929. * @return {string}
  13930. * - The control text when getting
  13931. */
  13932. MenuButton.prototype.controlText = function controlText(text) {
  13933. var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el();
  13934. return this.menuButton_.controlText(text, el);
  13935. };
  13936. /**
  13937. * Handle a click on a `MenuButton`.
  13938. * See {@link ClickableComponent#handleClick} for instances where this is called.
  13939. *
  13940. * @param {EventTarget~Event} event
  13941. * The `keydown`, `tap`, or `click` event that caused this function to be
  13942. * called.
  13943. *
  13944. * @listens tap
  13945. * @listens click
  13946. */
  13947. MenuButton.prototype.handleClick = function handleClick(event) {
  13948. // When you click the button it adds focus, which will show the menu.
  13949. // So we'll remove focus when the mouse leaves the button. Focus is needed
  13950. // for tab navigation.
  13951. this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) {
  13952. this.unpressButton();
  13953. this.el_.blur();
  13954. }));
  13955. if (this.buttonPressed_) {
  13956. this.unpressButton();
  13957. } else {
  13958. this.pressButton();
  13959. }
  13960. };
  13961. /**
  13962. * Set the focus to the actual button, not to this element
  13963. */
  13964. MenuButton.prototype.focus = function focus() {
  13965. this.menuButton_.focus();
  13966. };
  13967. /**
  13968. * Remove the focus from the actual button, not this element
  13969. */
  13970. MenuButton.prototype.blur = function blur() {
  13971. this.menuButton_.blur();
  13972. };
  13973. /**
  13974. * This gets called when a `MenuButton` gains focus via a `focus` event.
  13975. * Turns on listening for `keydown` events. When they happen it
  13976. * calls `this.handleKeyPress`.
  13977. *
  13978. * @param {EventTarget~Event} event
  13979. * The `focus` event that caused this function to be called.
  13980. *
  13981. * @listens focus
  13982. */
  13983. MenuButton.prototype.handleFocus = function handleFocus() {
  13984. on(document_1, 'keydown', bind(this, this.handleKeyPress));
  13985. };
  13986. /**
  13987. * Called when a `MenuButton` loses focus. Turns off the listener for
  13988. * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
  13989. *
  13990. * @param {EventTarget~Event} event
  13991. * The `blur` event that caused this function to be called.
  13992. *
  13993. * @listens blur
  13994. */
  13995. MenuButton.prototype.handleBlur = function handleBlur() {
  13996. off(document_1, 'keydown', bind(this, this.handleKeyPress));
  13997. };
  13998. /**
  13999. * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
  14000. * {@link ClickableComponent#handleKeyPress} for instances where this is called.
  14001. *
  14002. * @param {EventTarget~Event} event
  14003. * The `keydown` event that caused this function to be called.
  14004. *
  14005. * @listens keydown
  14006. */
  14007. MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
  14008. // Escape (27) key or Tab (9) key unpress the 'button'
  14009. if (event.which === 27 || event.which === 9) {
  14010. if (this.buttonPressed_) {
  14011. this.unpressButton();
  14012. }
  14013. // Don't preventDefault for Tab key - we still want to lose focus
  14014. if (event.which !== 9) {
  14015. event.preventDefault();
  14016. // Set focus back to the menu button's button
  14017. this.menuButton_.el_.focus();
  14018. }
  14019. // Up (38) key or Down (40) key press the 'button'
  14020. } else if (event.which === 38 || event.which === 40) {
  14021. if (!this.buttonPressed_) {
  14022. this.pressButton();
  14023. event.preventDefault();
  14024. }
  14025. }
  14026. };
  14027. /**
  14028. * Handle a `keydown` event on a sub-menu. The listener for this is added in
  14029. * the constructor.
  14030. *
  14031. * @param {EventTarget~Event} event
  14032. * Key press event
  14033. *
  14034. * @listens keydown
  14035. */
  14036. MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
  14037. // Escape (27) key or Tab (9) key unpress the 'button'
  14038. if (event.which === 27 || event.which === 9) {
  14039. if (this.buttonPressed_) {
  14040. this.unpressButton();
  14041. }
  14042. // Don't preventDefault for Tab key - we still want to lose focus
  14043. if (event.which !== 9) {
  14044. event.preventDefault();
  14045. // Set focus back to the menu button's button
  14046. this.menuButton_.el_.focus();
  14047. }
  14048. }
  14049. };
  14050. /**
  14051. * Put the current `MenuButton` into a pressed state.
  14052. */
  14053. MenuButton.prototype.pressButton = function pressButton() {
  14054. if (this.enabled_) {
  14055. this.buttonPressed_ = true;
  14056. this.menu.lockShowing();
  14057. this.menuButton_.el_.setAttribute('aria-expanded', 'true');
  14058. // set the focus into the submenu, except on iOS where it is resulting in
  14059. // undesired scrolling behavior when the player is in an iframe
  14060. if (IS_IOS && isInFrame()) {
  14061. // Return early so that the menu isn't focused
  14062. return;
  14063. }
  14064. this.menu.focus();
  14065. }
  14066. };
  14067. /**
  14068. * Take the current `MenuButton` out of a pressed state.
  14069. */
  14070. MenuButton.prototype.unpressButton = function unpressButton() {
  14071. if (this.enabled_) {
  14072. this.buttonPressed_ = false;
  14073. this.menu.unlockShowing();
  14074. this.menuButton_.el_.setAttribute('aria-expanded', 'false');
  14075. }
  14076. };
  14077. /**
  14078. * Disable the `MenuButton`. Don't allow it to be clicked.
  14079. */
  14080. MenuButton.prototype.disable = function disable() {
  14081. this.unpressButton();
  14082. this.enabled_ = false;
  14083. this.addClass('vjs-disabled');
  14084. this.menuButton_.disable();
  14085. };
  14086. /**
  14087. * Enable the `MenuButton`. Allow it to be clicked.
  14088. */
  14089. MenuButton.prototype.enable = function enable() {
  14090. this.enabled_ = true;
  14091. this.removeClass('vjs-disabled');
  14092. this.menuButton_.enable();
  14093. };
  14094. return MenuButton;
  14095. }(Component);
  14096. Component.registerComponent('MenuButton', MenuButton);
  14097. /**
  14098. * @file track-button.js
  14099. */
  14100. /**
  14101. * The base class for buttons that toggle specific track types (e.g. subtitles).
  14102. *
  14103. * @extends MenuButton
  14104. */
  14105. var TrackButton = function (_MenuButton) {
  14106. inherits(TrackButton, _MenuButton);
  14107. /**
  14108. * Creates an instance of this class.
  14109. *
  14110. * @param {Player} player
  14111. * The `Player` that this class should be attached to.
  14112. *
  14113. * @param {Object} [options]
  14114. * The key/value store of player options.
  14115. */
  14116. function TrackButton(player, options) {
  14117. classCallCheck(this, TrackButton);
  14118. var tracks = options.tracks;
  14119. var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
  14120. if (_this.items.length <= 1) {
  14121. _this.hide();
  14122. }
  14123. if (!tracks) {
  14124. return possibleConstructorReturn(_this);
  14125. }
  14126. var updateHandler = bind(_this, _this.update);
  14127. tracks.addEventListener('removetrack', updateHandler);
  14128. tracks.addEventListener('addtrack', updateHandler);
  14129. _this.player_.on('ready', updateHandler);
  14130. _this.player_.on('dispose', function () {
  14131. tracks.removeEventListener('removetrack', updateHandler);
  14132. tracks.removeEventListener('addtrack', updateHandler);
  14133. });
  14134. return _this;
  14135. }
  14136. return TrackButton;
  14137. }(MenuButton);
  14138. Component.registerComponent('TrackButton', TrackButton);
  14139. /**
  14140. * @file menu-item.js
  14141. */
  14142. /**
  14143. * The component for a menu item. `<li>`
  14144. *
  14145. * @extends ClickableComponent
  14146. */
  14147. var MenuItem = function (_ClickableComponent) {
  14148. inherits(MenuItem, _ClickableComponent);
  14149. /**
  14150. * Creates an instance of the this class.
  14151. *
  14152. * @param {Player} player
  14153. * The `Player` that this class should be attached to.
  14154. *
  14155. * @param {Object} [options={}]
  14156. * The key/value store of player options.
  14157. *
  14158. */
  14159. function MenuItem(player, options) {
  14160. classCallCheck(this, MenuItem);
  14161. var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  14162. _this.selectable = options.selectable;
  14163. _this.isSelected_ = options.selected || false;
  14164. _this.multiSelectable = options.multiSelectable;
  14165. _this.selected(_this.isSelected_);
  14166. if (_this.selectable) {
  14167. if (_this.multiSelectable) {
  14168. _this.el_.setAttribute('role', 'menuitemcheckbox');
  14169. } else {
  14170. _this.el_.setAttribute('role', 'menuitemradio');
  14171. }
  14172. } else {
  14173. _this.el_.setAttribute('role', 'menuitem');
  14174. }
  14175. return _this;
  14176. }
  14177. /**
  14178. * Create the `MenuItem's DOM element
  14179. *
  14180. * @param {string} [type=li]
  14181. * Element's node type, not actually used, always set to `li`.
  14182. *
  14183. * @param {Object} [props={}]
  14184. * An object of properties that should be set on the element
  14185. *
  14186. * @param {Object} [attrs={}]
  14187. * An object of attributes that should be set on the element
  14188. *
  14189. * @return {Element}
  14190. * The element that gets created.
  14191. */
  14192. MenuItem.prototype.createEl = function createEl(type, props, attrs) {
  14193. // The control is textual, not just an icon
  14194. this.nonIconControl = true;
  14195. return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
  14196. className: 'vjs-menu-item',
  14197. innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>',
  14198. tabIndex: -1
  14199. }, props), attrs);
  14200. };
  14201. /**
  14202. * Any click on a `MenuItem` puts it into the selected state.
  14203. * See {@link ClickableComponent#handleClick} for instances where this is called.
  14204. *
  14205. * @param {EventTarget~Event} event
  14206. * The `keydown`, `tap`, or `click` event that caused this function to be
  14207. * called.
  14208. *
  14209. * @listens tap
  14210. * @listens click
  14211. */
  14212. MenuItem.prototype.handleClick = function handleClick(event) {
  14213. this.selected(true);
  14214. };
  14215. /**
  14216. * Set the state for this menu item as selected or not.
  14217. *
  14218. * @param {boolean} selected
  14219. * if the menu item is selected or not
  14220. */
  14221. MenuItem.prototype.selected = function selected(_selected) {
  14222. if (this.selectable) {
  14223. if (_selected) {
  14224. this.addClass('vjs-selected');
  14225. this.el_.setAttribute('aria-checked', 'true');
  14226. // aria-checked isn't fully supported by browsers/screen readers,
  14227. // so indicate selected state to screen reader in the control text.
  14228. this.controlText(', selected');
  14229. this.isSelected_ = true;
  14230. } else {
  14231. this.removeClass('vjs-selected');
  14232. this.el_.setAttribute('aria-checked', 'false');
  14233. // Indicate un-selected state to screen reader
  14234. this.controlText('');
  14235. this.isSelected_ = false;
  14236. }
  14237. }
  14238. };
  14239. return MenuItem;
  14240. }(ClickableComponent);
  14241. Component.registerComponent('MenuItem', MenuItem);
  14242. /**
  14243. * @file text-track-menu-item.js
  14244. */
  14245. /**
  14246. * The specific menu item type for selecting a language within a text track kind
  14247. *
  14248. * @extends MenuItem
  14249. */
  14250. var TextTrackMenuItem = function (_MenuItem) {
  14251. inherits(TextTrackMenuItem, _MenuItem);
  14252. /**
  14253. * Creates an instance of this class.
  14254. *
  14255. * @param {Player} player
  14256. * The `Player` that this class should be attached to.
  14257. *
  14258. * @param {Object} [options]
  14259. * The key/value store of player options.
  14260. */
  14261. function TextTrackMenuItem(player, options) {
  14262. classCallCheck(this, TextTrackMenuItem);
  14263. var track = options.track;
  14264. var tracks = player.textTracks();
  14265. // Modify options for parent MenuItem class's init.
  14266. options.label = track.label || track.language || 'Unknown';
  14267. options.selected = track.mode === 'showing';
  14268. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  14269. _this.track = track;
  14270. var changeHandler = function changeHandler() {
  14271. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  14272. args[_key] = arguments[_key];
  14273. }
  14274. _this.handleTracksChange.apply(_this, args);
  14275. };
  14276. var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
  14277. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  14278. args[_key2] = arguments[_key2];
  14279. }
  14280. _this.handleSelectedLanguageChange.apply(_this, args);
  14281. };
  14282. player.on(['loadstart', 'texttrackchange'], changeHandler);
  14283. tracks.addEventListener('change', changeHandler);
  14284. tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
  14285. _this.on('dispose', function () {
  14286. player.off(['loadstart', 'texttrackchange'], changeHandler);
  14287. tracks.removeEventListener('change', changeHandler);
  14288. tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
  14289. });
  14290. // iOS7 doesn't dispatch change events to TextTrackLists when an
  14291. // associated track's mode changes. Without something like
  14292. // Object.observe() (also not present on iOS7), it's not
  14293. // possible to detect changes to the mode attribute and polyfill
  14294. // the change event. As a poor substitute, we manually dispatch
  14295. // change events whenever the controls modify the mode.
  14296. if (tracks.onchange === undefined) {
  14297. var event = void 0;
  14298. _this.on(['tap', 'click'], function () {
  14299. if (_typeof(window_1.Event) !== 'object') {
  14300. // Android 2.3 throws an Illegal Constructor error for window.Event
  14301. try {
  14302. event = new window_1.Event('change');
  14303. } catch (err) {
  14304. // continue regardless of error
  14305. }
  14306. }
  14307. if (!event) {
  14308. event = document_1.createEvent('Event');
  14309. event.initEvent('change', true, true);
  14310. }
  14311. tracks.dispatchEvent(event);
  14312. });
  14313. }
  14314. // set the default state based on current tracks
  14315. _this.handleTracksChange();
  14316. return _this;
  14317. }
  14318. /**
  14319. * This gets called when an `TextTrackMenuItem` is "clicked". See
  14320. * {@link ClickableComponent} for more detailed information on what a click can be.
  14321. *
  14322. * @param {EventTarget~Event} event
  14323. * The `keydown`, `tap`, or `click` event that caused this function to be
  14324. * called.
  14325. *
  14326. * @listens tap
  14327. * @listens click
  14328. */
  14329. TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
  14330. var kind = this.track.kind;
  14331. var kinds = this.track.kinds;
  14332. var tracks = this.player_.textTracks();
  14333. if (!kinds) {
  14334. kinds = [kind];
  14335. }
  14336. _MenuItem.prototype.handleClick.call(this, event);
  14337. if (!tracks) {
  14338. return;
  14339. }
  14340. for (var i = 0; i < tracks.length; i++) {
  14341. var track = tracks[i];
  14342. if (track === this.track && kinds.indexOf(track.kind) > -1) {
  14343. if (track.mode !== 'showing') {
  14344. track.mode = 'showing';
  14345. }
  14346. } else if (track.mode !== 'disabled') {
  14347. track.mode = 'disabled';
  14348. }
  14349. }
  14350. };
  14351. /**
  14352. * Handle text track list change
  14353. *
  14354. * @param {EventTarget~Event} event
  14355. * The `change` event that caused this function to be called.
  14356. *
  14357. * @listens TextTrackList#change
  14358. */
  14359. TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  14360. var shouldBeSelected = this.track.mode === 'showing';
  14361. // Prevent redundant selected() calls because they may cause
  14362. // screen readers to read the appended control text unnecessarily
  14363. if (shouldBeSelected !== this.isSelected_) {
  14364. this.selected(shouldBeSelected);
  14365. }
  14366. };
  14367. TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
  14368. if (this.track.mode === 'showing') {
  14369. var selectedLanguage = this.player_.cache_.selectedLanguage;
  14370. // Don't replace the kind of track across the same language
  14371. if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
  14372. return;
  14373. }
  14374. this.player_.cache_.selectedLanguage = {
  14375. enabled: true,
  14376. language: this.track.language,
  14377. kind: this.track.kind
  14378. };
  14379. }
  14380. };
  14381. TextTrackMenuItem.prototype.dispose = function dispose() {
  14382. // remove reference to track object on dispose
  14383. this.track = null;
  14384. _MenuItem.prototype.dispose.call(this);
  14385. };
  14386. return TextTrackMenuItem;
  14387. }(MenuItem);
  14388. Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
  14389. /**
  14390. * @file off-text-track-menu-item.js
  14391. */
  14392. /**
  14393. * A special menu item for turning of a specific type of text track
  14394. *
  14395. * @extends TextTrackMenuItem
  14396. */
  14397. var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
  14398. inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
  14399. /**
  14400. * Creates an instance of this class.
  14401. *
  14402. * @param {Player} player
  14403. * The `Player` that this class should be attached to.
  14404. *
  14405. * @param {Object} [options]
  14406. * The key/value store of player options.
  14407. */
  14408. function OffTextTrackMenuItem(player, options) {
  14409. classCallCheck(this, OffTextTrackMenuItem);
  14410. // Create pseudo track info
  14411. // Requires options['kind']
  14412. options.track = {
  14413. player: player,
  14414. kind: options.kind,
  14415. kinds: options.kinds,
  14416. 'default': false,
  14417. mode: 'disabled'
  14418. };
  14419. if (!options.kinds) {
  14420. options.kinds = [options.kind];
  14421. }
  14422. if (options.label) {
  14423. options.track.label = options.label;
  14424. } else {
  14425. options.track.label = options.kinds.join(' and ') + ' off';
  14426. }
  14427. // MenuItem is selectable
  14428. options.selectable = true;
  14429. // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
  14430. options.multiSelectable = false;
  14431. return possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
  14432. }
  14433. /**
  14434. * Handle text track change
  14435. *
  14436. * @param {EventTarget~Event} event
  14437. * The event that caused this function to run
  14438. */
  14439. OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  14440. var tracks = this.player().textTracks();
  14441. var shouldBeSelected = true;
  14442. for (var i = 0, l = tracks.length; i < l; i++) {
  14443. var track = tracks[i];
  14444. if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
  14445. shouldBeSelected = false;
  14446. break;
  14447. }
  14448. }
  14449. // Prevent redundant selected() calls because they may cause
  14450. // screen readers to read the appended control text unnecessarily
  14451. if (shouldBeSelected !== this.isSelected_) {
  14452. this.selected(shouldBeSelected);
  14453. }
  14454. };
  14455. OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
  14456. var tracks = this.player().textTracks();
  14457. var allHidden = true;
  14458. for (var i = 0, l = tracks.length; i < l; i++) {
  14459. var track = tracks[i];
  14460. if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
  14461. allHidden = false;
  14462. break;
  14463. }
  14464. }
  14465. if (allHidden) {
  14466. this.player_.cache_.selectedLanguage = {
  14467. enabled: false
  14468. };
  14469. }
  14470. };
  14471. return OffTextTrackMenuItem;
  14472. }(TextTrackMenuItem);
  14473. Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
  14474. /**
  14475. * @file text-track-button.js
  14476. */
  14477. /**
  14478. * The base class for buttons that toggle specific text track types (e.g. subtitles)
  14479. *
  14480. * @extends MenuButton
  14481. */
  14482. var TextTrackButton = function (_TrackButton) {
  14483. inherits(TextTrackButton, _TrackButton);
  14484. /**
  14485. * Creates an instance of this class.
  14486. *
  14487. * @param {Player} player
  14488. * The `Player` that this class should be attached to.
  14489. *
  14490. * @param {Object} [options={}]
  14491. * The key/value store of player options.
  14492. */
  14493. function TextTrackButton(player) {
  14494. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  14495. classCallCheck(this, TextTrackButton);
  14496. options.tracks = player.textTracks();
  14497. return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
  14498. }
  14499. /**
  14500. * Create a menu item for each text track
  14501. *
  14502. * @param {TextTrackMenuItem[]} [items=[]]
  14503. * Existing array of items to use during creation
  14504. *
  14505. * @return {TextTrackMenuItem[]}
  14506. * Array of menu items that were created
  14507. */
  14508. TextTrackButton.prototype.createItems = function createItems() {
  14509. var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  14510. var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem;
  14511. // Label is an overide for the [track] off label
  14512. // USed to localise captions/subtitles
  14513. var label = void 0;
  14514. if (this.label_) {
  14515. label = this.label_ + ' off';
  14516. }
  14517. // Add an OFF menu item to turn all tracks off
  14518. items.push(new OffTextTrackMenuItem(this.player_, {
  14519. kinds: this.kinds_,
  14520. kind: this.kind_,
  14521. label: label
  14522. }));
  14523. this.hideThreshold_ += 1;
  14524. var tracks = this.player_.textTracks();
  14525. if (!Array.isArray(this.kinds_)) {
  14526. this.kinds_ = [this.kind_];
  14527. }
  14528. for (var i = 0; i < tracks.length; i++) {
  14529. var track = tracks[i];
  14530. // only add tracks that are of an appropriate kind and have a label
  14531. if (this.kinds_.indexOf(track.kind) > -1) {
  14532. var item = new TrackMenuItem(this.player_, {
  14533. track: track,
  14534. // MenuItem is selectable
  14535. selectable: true,
  14536. // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
  14537. multiSelectable: false
  14538. });
  14539. item.addClass('vjs-' + track.kind + '-menu-item');
  14540. items.push(item);
  14541. }
  14542. }
  14543. return items;
  14544. };
  14545. return TextTrackButton;
  14546. }(TrackButton);
  14547. Component.registerComponent('TextTrackButton', TextTrackButton);
  14548. /**
  14549. * @file chapters-track-menu-item.js
  14550. */
  14551. /**
  14552. * The chapter track menu item
  14553. *
  14554. * @extends MenuItem
  14555. */
  14556. var ChaptersTrackMenuItem = function (_MenuItem) {
  14557. inherits(ChaptersTrackMenuItem, _MenuItem);
  14558. /**
  14559. * Creates an instance of this class.
  14560. *
  14561. * @param {Player} player
  14562. * The `Player` that this class should be attached to.
  14563. *
  14564. * @param {Object} [options]
  14565. * The key/value store of player options.
  14566. */
  14567. function ChaptersTrackMenuItem(player, options) {
  14568. classCallCheck(this, ChaptersTrackMenuItem);
  14569. var track = options.track;
  14570. var cue = options.cue;
  14571. var currentTime = player.currentTime();
  14572. // Modify options for parent MenuItem class's init.
  14573. options.selectable = true;
  14574. options.multiSelectable = false;
  14575. options.label = cue.text;
  14576. options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
  14577. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  14578. _this.track = track;
  14579. _this.cue = cue;
  14580. track.addEventListener('cuechange', bind(_this, _this.update));
  14581. return _this;
  14582. }
  14583. /**
  14584. * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
  14585. * {@link ClickableComponent} for more detailed information on what a click can be.
  14586. *
  14587. * @param {EventTarget~Event} [event]
  14588. * The `keydown`, `tap`, or `click` event that caused this function to be
  14589. * called.
  14590. *
  14591. * @listens tap
  14592. * @listens click
  14593. */
  14594. ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
  14595. _MenuItem.prototype.handleClick.call(this);
  14596. this.player_.currentTime(this.cue.startTime);
  14597. this.update(this.cue.startTime);
  14598. };
  14599. /**
  14600. * Update chapter menu item
  14601. *
  14602. * @param {EventTarget~Event} [event]
  14603. * The `cuechange` event that caused this function to run.
  14604. *
  14605. * @listens TextTrack#cuechange
  14606. */
  14607. ChaptersTrackMenuItem.prototype.update = function update(event) {
  14608. var cue = this.cue;
  14609. var currentTime = this.player_.currentTime();
  14610. // vjs.log(currentTime, cue.startTime);
  14611. this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
  14612. };
  14613. return ChaptersTrackMenuItem;
  14614. }(MenuItem);
  14615. Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
  14616. /**
  14617. * @file chapters-button.js
  14618. */
  14619. /**
  14620. * The button component for toggling and selecting chapters
  14621. * Chapters act much differently than other text tracks
  14622. * Cues are navigation vs. other tracks of alternative languages
  14623. *
  14624. * @extends TextTrackButton
  14625. */
  14626. var ChaptersButton = function (_TextTrackButton) {
  14627. inherits(ChaptersButton, _TextTrackButton);
  14628. /**
  14629. * Creates an instance of this class.
  14630. *
  14631. * @param {Player} player
  14632. * The `Player` that this class should be attached to.
  14633. *
  14634. * @param {Object} [options]
  14635. * The key/value store of player options.
  14636. *
  14637. * @param {Component~ReadyCallback} [ready]
  14638. * The function to call when this function is ready.
  14639. */
  14640. function ChaptersButton(player, options, ready) {
  14641. classCallCheck(this, ChaptersButton);
  14642. return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  14643. }
  14644. /**
  14645. * Builds the default DOM `className`.
  14646. *
  14647. * @return {string}
  14648. * The DOM `className` for this object.
  14649. */
  14650. ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
  14651. return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  14652. };
  14653. ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  14654. return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  14655. };
  14656. /**
  14657. * Update the menu based on the current state of its items.
  14658. *
  14659. * @param {EventTarget~Event} [event]
  14660. * An event that triggered this function to run.
  14661. *
  14662. * @listens TextTrackList#addtrack
  14663. * @listens TextTrackList#removetrack
  14664. * @listens TextTrackList#change
  14665. */
  14666. ChaptersButton.prototype.update = function update(event) {
  14667. if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
  14668. this.setTrack(this.findChaptersTrack());
  14669. }
  14670. _TextTrackButton.prototype.update.call(this);
  14671. };
  14672. /**
  14673. * Set the currently selected track for the chapters button.
  14674. *
  14675. * @param {TextTrack} track
  14676. * The new track to select. Nothing will change if this is the currently selected
  14677. * track.
  14678. */
  14679. ChaptersButton.prototype.setTrack = function setTrack(track) {
  14680. if (this.track_ === track) {
  14681. return;
  14682. }
  14683. if (!this.updateHandler_) {
  14684. this.updateHandler_ = this.update.bind(this);
  14685. }
  14686. // here this.track_ refers to the old track instance
  14687. if (this.track_) {
  14688. var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
  14689. if (remoteTextTrackEl) {
  14690. remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
  14691. }
  14692. this.track_ = null;
  14693. }
  14694. this.track_ = track;
  14695. // here this.track_ refers to the new track instance
  14696. if (this.track_) {
  14697. this.track_.mode = 'hidden';
  14698. var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
  14699. if (_remoteTextTrackEl) {
  14700. _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
  14701. }
  14702. }
  14703. };
  14704. /**
  14705. * Find the track object that is currently in use by this ChaptersButton
  14706. *
  14707. * @return {TextTrack|undefined}
  14708. * The current track or undefined if none was found.
  14709. */
  14710. ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
  14711. var tracks = this.player_.textTracks() || [];
  14712. for (var i = tracks.length - 1; i >= 0; i--) {
  14713. // We will always choose the last track as our chaptersTrack
  14714. var track = tracks[i];
  14715. if (track.kind === this.kind_) {
  14716. return track;
  14717. }
  14718. }
  14719. };
  14720. /**
  14721. * Get the caption for the ChaptersButton based on the track label. This will also
  14722. * use the current tracks localized kind as a fallback if a label does not exist.
  14723. *
  14724. * @return {string}
  14725. * The tracks current label or the localized track kind.
  14726. */
  14727. ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
  14728. if (this.track_ && this.track_.label) {
  14729. return this.track_.label;
  14730. }
  14731. return this.localize(toTitleCase(this.kind_));
  14732. };
  14733. /**
  14734. * Create menu from chapter track
  14735. *
  14736. * @return {Menu}
  14737. * New menu for the chapter buttons
  14738. */
  14739. ChaptersButton.prototype.createMenu = function createMenu() {
  14740. this.options_.title = this.getMenuCaption();
  14741. return _TextTrackButton.prototype.createMenu.call(this);
  14742. };
  14743. /**
  14744. * Create a menu item for each text track
  14745. *
  14746. * @return {TextTrackMenuItem[]}
  14747. * Array of menu items
  14748. */
  14749. ChaptersButton.prototype.createItems = function createItems() {
  14750. var items = [];
  14751. if (!this.track_) {
  14752. return items;
  14753. }
  14754. var cues = this.track_.cues;
  14755. if (!cues) {
  14756. return items;
  14757. }
  14758. for (var i = 0, l = cues.length; i < l; i++) {
  14759. var cue = cues[i];
  14760. var mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue: cue });
  14761. items.push(mi);
  14762. }
  14763. return items;
  14764. };
  14765. return ChaptersButton;
  14766. }(TextTrackButton);
  14767. /**
  14768. * `kind` of TextTrack to look for to associate it with this menu.
  14769. *
  14770. * @type {string}
  14771. * @private
  14772. */
  14773. ChaptersButton.prototype.kind_ = 'chapters';
  14774. /**
  14775. * The text that should display over the `ChaptersButton`s controls. Added for localization.
  14776. *
  14777. * @type {string}
  14778. * @private
  14779. */
  14780. ChaptersButton.prototype.controlText_ = 'Chapters';
  14781. Component.registerComponent('ChaptersButton', ChaptersButton);
  14782. /**
  14783. * @file descriptions-button.js
  14784. */
  14785. /**
  14786. * The button component for toggling and selecting descriptions
  14787. *
  14788. * @extends TextTrackButton
  14789. */
  14790. var DescriptionsButton = function (_TextTrackButton) {
  14791. inherits(DescriptionsButton, _TextTrackButton);
  14792. /**
  14793. * Creates an instance of this class.
  14794. *
  14795. * @param {Player} player
  14796. * The `Player` that this class should be attached to.
  14797. *
  14798. * @param {Object} [options]
  14799. * The key/value store of player options.
  14800. *
  14801. * @param {Component~ReadyCallback} [ready]
  14802. * The function to call when this component is ready.
  14803. */
  14804. function DescriptionsButton(player, options, ready) {
  14805. classCallCheck(this, DescriptionsButton);
  14806. var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  14807. var tracks = player.textTracks();
  14808. var changeHandler = bind(_this, _this.handleTracksChange);
  14809. tracks.addEventListener('change', changeHandler);
  14810. _this.on('dispose', function () {
  14811. tracks.removeEventListener('change', changeHandler);
  14812. });
  14813. return _this;
  14814. }
  14815. /**
  14816. * Handle text track change
  14817. *
  14818. * @param {EventTarget~Event} event
  14819. * The event that caused this function to run
  14820. *
  14821. * @listens TextTrackList#change
  14822. */
  14823. DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
  14824. var tracks = this.player().textTracks();
  14825. var disabled = false;
  14826. // Check whether a track of a different kind is showing
  14827. for (var i = 0, l = tracks.length; i < l; i++) {
  14828. var track = tracks[i];
  14829. if (track.kind !== this.kind_ && track.mode === 'showing') {
  14830. disabled = true;
  14831. break;
  14832. }
  14833. }
  14834. // If another track is showing, disable this menu button
  14835. if (disabled) {
  14836. this.disable();
  14837. } else {
  14838. this.enable();
  14839. }
  14840. };
  14841. /**
  14842. * Builds the default DOM `className`.
  14843. *
  14844. * @return {string}
  14845. * The DOM `className` for this object.
  14846. */
  14847. DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
  14848. return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  14849. };
  14850. DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  14851. return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  14852. };
  14853. return DescriptionsButton;
  14854. }(TextTrackButton);
  14855. /**
  14856. * `kind` of TextTrack to look for to associate it with this menu.
  14857. *
  14858. * @type {string}
  14859. * @private
  14860. */
  14861. DescriptionsButton.prototype.kind_ = 'descriptions';
  14862. /**
  14863. * The text that should display over the `DescriptionsButton`s controls. Added for localization.
  14864. *
  14865. * @type {string}
  14866. * @private
  14867. */
  14868. DescriptionsButton.prototype.controlText_ = 'Descriptions';
  14869. Component.registerComponent('DescriptionsButton', DescriptionsButton);
  14870. /**
  14871. * @file subtitles-button.js
  14872. */
  14873. /**
  14874. * The button component for toggling and selecting subtitles
  14875. *
  14876. * @extends TextTrackButton
  14877. */
  14878. var SubtitlesButton = function (_TextTrackButton) {
  14879. inherits(SubtitlesButton, _TextTrackButton);
  14880. /**
  14881. * Creates an instance of this class.
  14882. *
  14883. * @param {Player} player
  14884. * The `Player` that this class should be attached to.
  14885. *
  14886. * @param {Object} [options]
  14887. * The key/value store of player options.
  14888. *
  14889. * @param {Component~ReadyCallback} [ready]
  14890. * The function to call when this component is ready.
  14891. */
  14892. function SubtitlesButton(player, options, ready) {
  14893. classCallCheck(this, SubtitlesButton);
  14894. return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  14895. }
  14896. /**
  14897. * Builds the default DOM `className`.
  14898. *
  14899. * @return {string}
  14900. * The DOM `className` for this object.
  14901. */
  14902. SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
  14903. return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  14904. };
  14905. SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  14906. return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  14907. };
  14908. return SubtitlesButton;
  14909. }(TextTrackButton);
  14910. /**
  14911. * `kind` of TextTrack to look for to associate it with this menu.
  14912. *
  14913. * @type {string}
  14914. * @private
  14915. */
  14916. SubtitlesButton.prototype.kind_ = 'subtitles';
  14917. /**
  14918. * The text that should display over the `SubtitlesButton`s controls. Added for localization.
  14919. *
  14920. * @type {string}
  14921. * @private
  14922. */
  14923. SubtitlesButton.prototype.controlText_ = 'Subtitles';
  14924. Component.registerComponent('SubtitlesButton', SubtitlesButton);
  14925. /**
  14926. * @file caption-settings-menu-item.js
  14927. */
  14928. /**
  14929. * The menu item for caption track settings menu
  14930. *
  14931. * @extends TextTrackMenuItem
  14932. */
  14933. var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
  14934. inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
  14935. /**
  14936. * Creates an instance of this class.
  14937. *
  14938. * @param {Player} player
  14939. * The `Player` that this class should be attached to.
  14940. *
  14941. * @param {Object} [options]
  14942. * The key/value store of player options.
  14943. */
  14944. function CaptionSettingsMenuItem(player, options) {
  14945. classCallCheck(this, CaptionSettingsMenuItem);
  14946. options.track = {
  14947. player: player,
  14948. kind: options.kind,
  14949. label: options.kind + ' settings',
  14950. selectable: false,
  14951. 'default': false,
  14952. mode: 'disabled'
  14953. };
  14954. // CaptionSettingsMenuItem has no concept of 'selected'
  14955. options.selectable = false;
  14956. options.name = 'CaptionSettingsMenuItem';
  14957. var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
  14958. _this.addClass('vjs-texttrack-settings');
  14959. _this.controlText(', opens ' + options.kind + ' settings dialog');
  14960. return _this;
  14961. }
  14962. /**
  14963. * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
  14964. * {@link ClickableComponent} for more detailed information on what a click can be.
  14965. *
  14966. * @param {EventTarget~Event} [event]
  14967. * The `keydown`, `tap`, or `click` event that caused this function to be
  14968. * called.
  14969. *
  14970. * @listens tap
  14971. * @listens click
  14972. */
  14973. CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
  14974. this.player().getChild('textTrackSettings').open();
  14975. };
  14976. return CaptionSettingsMenuItem;
  14977. }(TextTrackMenuItem);
  14978. Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
  14979. /**
  14980. * @file captions-button.js
  14981. */
  14982. /**
  14983. * The button component for toggling and selecting captions
  14984. *
  14985. * @extends TextTrackButton
  14986. */
  14987. var CaptionsButton = function (_TextTrackButton) {
  14988. inherits(CaptionsButton, _TextTrackButton);
  14989. /**
  14990. * Creates an instance of this class.
  14991. *
  14992. * @param {Player} player
  14993. * The `Player` that this class should be attached to.
  14994. *
  14995. * @param {Object} [options]
  14996. * The key/value store of player options.
  14997. *
  14998. * @param {Component~ReadyCallback} [ready]
  14999. * The function to call when this component is ready.
  15000. */
  15001. function CaptionsButton(player, options, ready) {
  15002. classCallCheck(this, CaptionsButton);
  15003. return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  15004. }
  15005. /**
  15006. * Builds the default DOM `className`.
  15007. *
  15008. * @return {string}
  15009. * The DOM `className` for this object.
  15010. */
  15011. CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
  15012. return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  15013. };
  15014. CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  15015. return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  15016. };
  15017. /**
  15018. * Create caption menu items
  15019. *
  15020. * @return {CaptionSettingsMenuItem[]}
  15021. * The array of current menu items.
  15022. */
  15023. CaptionsButton.prototype.createItems = function createItems() {
  15024. var items = [];
  15025. if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
  15026. items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.kind_ }));
  15027. this.hideThreshold_ += 1;
  15028. }
  15029. return _TextTrackButton.prototype.createItems.call(this, items);
  15030. };
  15031. return CaptionsButton;
  15032. }(TextTrackButton);
  15033. /**
  15034. * `kind` of TextTrack to look for to associate it with this menu.
  15035. *
  15036. * @type {string}
  15037. * @private
  15038. */
  15039. CaptionsButton.prototype.kind_ = 'captions';
  15040. /**
  15041. * The text that should display over the `CaptionsButton`s controls. Added for localization.
  15042. *
  15043. * @type {string}
  15044. * @private
  15045. */
  15046. CaptionsButton.prototype.controlText_ = 'Captions';
  15047. Component.registerComponent('CaptionsButton', CaptionsButton);
  15048. /**
  15049. * @file subs-caps-menu-item.js
  15050. */
  15051. /**
  15052. * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
  15053. * in the SubsCapsMenu.
  15054. *
  15055. * @extends TextTrackMenuItem
  15056. */
  15057. var SubsCapsMenuItem = function (_TextTrackMenuItem) {
  15058. inherits(SubsCapsMenuItem, _TextTrackMenuItem);
  15059. function SubsCapsMenuItem() {
  15060. classCallCheck(this, SubsCapsMenuItem);
  15061. return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments));
  15062. }
  15063. SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) {
  15064. var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
  15065. if (this.options_.track.kind === 'captions') {
  15066. innerHTML += '\n <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n ';
  15067. }
  15068. innerHTML += '</span>';
  15069. var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
  15070. innerHTML: innerHTML
  15071. }, props), attrs);
  15072. return el;
  15073. };
  15074. return SubsCapsMenuItem;
  15075. }(TextTrackMenuItem);
  15076. Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
  15077. /**
  15078. * @file sub-caps-button.js
  15079. */
  15080. /**
  15081. * The button component for toggling and selecting captions and/or subtitles
  15082. *
  15083. * @extends TextTrackButton
  15084. */
  15085. var SubsCapsButton = function (_TextTrackButton) {
  15086. inherits(SubsCapsButton, _TextTrackButton);
  15087. function SubsCapsButton(player) {
  15088. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  15089. classCallCheck(this, SubsCapsButton);
  15090. // Although North America uses "captions" in most cases for
  15091. // "captions and subtitles" other locales use "subtitles"
  15092. var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options));
  15093. _this.label_ = 'subtitles';
  15094. if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
  15095. _this.label_ = 'captions';
  15096. }
  15097. _this.menuButton_.controlText(toTitleCase(_this.label_));
  15098. return _this;
  15099. }
  15100. /**
  15101. * Builds the default DOM `className`.
  15102. *
  15103. * @return {string}
  15104. * The DOM `className` for this object.
  15105. */
  15106. SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() {
  15107. return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  15108. };
  15109. SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  15110. return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  15111. };
  15112. /**
  15113. * Create caption/subtitles menu items
  15114. *
  15115. * @return {CaptionSettingsMenuItem[]}
  15116. * The array of current menu items.
  15117. */
  15118. SubsCapsButton.prototype.createItems = function createItems() {
  15119. var items = [];
  15120. if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
  15121. items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.label_ }));
  15122. this.hideThreshold_ += 1;
  15123. }
  15124. items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
  15125. return items;
  15126. };
  15127. return SubsCapsButton;
  15128. }(TextTrackButton);
  15129. /**
  15130. * `kind`s of TextTrack to look for to associate it with this menu.
  15131. *
  15132. * @type {array}
  15133. * @private
  15134. */
  15135. SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
  15136. /**
  15137. * The text that should display over the `SubsCapsButton`s controls.
  15138. *
  15139. *
  15140. * @type {string}
  15141. * @private
  15142. */
  15143. SubsCapsButton.prototype.controlText_ = 'Subtitles';
  15144. Component.registerComponent('SubsCapsButton', SubsCapsButton);
  15145. /**
  15146. * @file audio-track-menu-item.js
  15147. */
  15148. /**
  15149. * An {@link AudioTrack} {@link MenuItem}
  15150. *
  15151. * @extends MenuItem
  15152. */
  15153. var AudioTrackMenuItem = function (_MenuItem) {
  15154. inherits(AudioTrackMenuItem, _MenuItem);
  15155. /**
  15156. * Creates an instance of this class.
  15157. *
  15158. * @param {Player} player
  15159. * The `Player` that this class should be attached to.
  15160. *
  15161. * @param {Object} [options]
  15162. * The key/value store of player options.
  15163. */
  15164. function AudioTrackMenuItem(player, options) {
  15165. classCallCheck(this, AudioTrackMenuItem);
  15166. var track = options.track;
  15167. var tracks = player.audioTracks();
  15168. // Modify options for parent MenuItem class's init.
  15169. options.label = track.label || track.language || 'Unknown';
  15170. options.selected = track.enabled;
  15171. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  15172. _this.track = track;
  15173. _this.addClass('vjs-' + track.kind + '-menu-item');
  15174. var changeHandler = function changeHandler() {
  15175. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  15176. args[_key] = arguments[_key];
  15177. }
  15178. _this.handleTracksChange.apply(_this, args);
  15179. };
  15180. tracks.addEventListener('change', changeHandler);
  15181. _this.on('dispose', function () {
  15182. tracks.removeEventListener('change', changeHandler);
  15183. });
  15184. return _this;
  15185. }
  15186. AudioTrackMenuItem.prototype.createEl = function createEl(type, props, attrs) {
  15187. var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
  15188. if (this.options_.track.kind === 'main-desc') {
  15189. innerHTML += '\n <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n <span class="vjs-control-text"> ' + this.localize('Descriptions') + '</span>\n ';
  15190. }
  15191. innerHTML += '</span>';
  15192. var el = _MenuItem.prototype.createEl.call(this, type, assign({
  15193. innerHTML: innerHTML
  15194. }, props), attrs);
  15195. return el;
  15196. };
  15197. /**
  15198. * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
  15199. * for more detailed information on what a click can be.
  15200. *
  15201. * @param {EventTarget~Event} [event]
  15202. * The `keydown`, `tap`, or `click` event that caused this function to be
  15203. * called.
  15204. *
  15205. * @listens tap
  15206. * @listens click
  15207. */
  15208. AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
  15209. var tracks = this.player_.audioTracks();
  15210. _MenuItem.prototype.handleClick.call(this, event);
  15211. for (var i = 0; i < tracks.length; i++) {
  15212. var track = tracks[i];
  15213. track.enabled = track === this.track;
  15214. }
  15215. };
  15216. /**
  15217. * Handle any {@link AudioTrack} change.
  15218. *
  15219. * @param {EventTarget~Event} [event]
  15220. * The {@link AudioTrackList#change} event that caused this to run.
  15221. *
  15222. * @listens AudioTrackList#change
  15223. */
  15224. AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  15225. this.selected(this.track.enabled);
  15226. };
  15227. return AudioTrackMenuItem;
  15228. }(MenuItem);
  15229. Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
  15230. /**
  15231. * @file audio-track-button.js
  15232. */
  15233. /**
  15234. * The base class for buttons that toggle specific {@link AudioTrack} types.
  15235. *
  15236. * @extends TrackButton
  15237. */
  15238. var AudioTrackButton = function (_TrackButton) {
  15239. inherits(AudioTrackButton, _TrackButton);
  15240. /**
  15241. * Creates an instance of this class.
  15242. *
  15243. * @param {Player} player
  15244. * The `Player` that this class should be attached to.
  15245. *
  15246. * @param {Object} [options={}]
  15247. * The key/value store of player options.
  15248. */
  15249. function AudioTrackButton(player) {
  15250. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  15251. classCallCheck(this, AudioTrackButton);
  15252. options.tracks = player.audioTracks();
  15253. return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
  15254. }
  15255. /**
  15256. * Builds the default DOM `className`.
  15257. *
  15258. * @return {string}
  15259. * The DOM `className` for this object.
  15260. */
  15261. AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
  15262. return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
  15263. };
  15264. AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  15265. return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this);
  15266. };
  15267. /**
  15268. * Create a menu item for each audio track
  15269. *
  15270. * @param {AudioTrackMenuItem[]} [items=[]]
  15271. * An array of existing menu items to use.
  15272. *
  15273. * @return {AudioTrackMenuItem[]}
  15274. * An array of menu items
  15275. */
  15276. AudioTrackButton.prototype.createItems = function createItems() {
  15277. var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  15278. // if there's only one audio track, there no point in showing it
  15279. this.hideThreshold_ = 1;
  15280. var tracks = this.player_.audioTracks();
  15281. for (var i = 0; i < tracks.length; i++) {
  15282. var track = tracks[i];
  15283. items.push(new AudioTrackMenuItem(this.player_, {
  15284. track: track,
  15285. // MenuItem is selectable
  15286. selectable: true,
  15287. // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
  15288. multiSelectable: false
  15289. }));
  15290. }
  15291. return items;
  15292. };
  15293. return AudioTrackButton;
  15294. }(TrackButton);
  15295. /**
  15296. * The text that should display over the `AudioTrackButton`s controls. Added for localization.
  15297. *
  15298. * @type {string}
  15299. * @private
  15300. */
  15301. AudioTrackButton.prototype.controlText_ = 'Audio Track';
  15302. Component.registerComponent('AudioTrackButton', AudioTrackButton);
  15303. /**
  15304. * @file playback-rate-menu-item.js
  15305. */
  15306. /**
  15307. * The specific menu item type for selecting a playback rate.
  15308. *
  15309. * @extends MenuItem
  15310. */
  15311. var PlaybackRateMenuItem = function (_MenuItem) {
  15312. inherits(PlaybackRateMenuItem, _MenuItem);
  15313. /**
  15314. * Creates an instance of this class.
  15315. *
  15316. * @param {Player} player
  15317. * The `Player` that this class should be attached to.
  15318. *
  15319. * @param {Object} [options]
  15320. * The key/value store of player options.
  15321. */
  15322. function PlaybackRateMenuItem(player, options) {
  15323. classCallCheck(this, PlaybackRateMenuItem);
  15324. var label = options.rate;
  15325. var rate = parseFloat(label, 10);
  15326. // Modify options for parent MenuItem class's init.
  15327. options.label = label;
  15328. options.selected = rate === 1;
  15329. options.selectable = true;
  15330. options.multiSelectable = false;
  15331. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  15332. _this.label = label;
  15333. _this.rate = rate;
  15334. _this.on(player, 'ratechange', _this.update);
  15335. return _this;
  15336. }
  15337. /**
  15338. * This gets called when an `PlaybackRateMenuItem` is "clicked". See
  15339. * {@link ClickableComponent} for more detailed information on what a click can be.
  15340. *
  15341. * @param {EventTarget~Event} [event]
  15342. * The `keydown`, `tap`, or `click` event that caused this function to be
  15343. * called.
  15344. *
  15345. * @listens tap
  15346. * @listens click
  15347. */
  15348. PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
  15349. _MenuItem.prototype.handleClick.call(this);
  15350. this.player().playbackRate(this.rate);
  15351. };
  15352. /**
  15353. * Update the PlaybackRateMenuItem when the playbackrate changes.
  15354. *
  15355. * @param {EventTarget~Event} [event]
  15356. * The `ratechange` event that caused this function to run.
  15357. *
  15358. * @listens Player#ratechange
  15359. */
  15360. PlaybackRateMenuItem.prototype.update = function update(event) {
  15361. this.selected(this.player().playbackRate() === this.rate);
  15362. };
  15363. return PlaybackRateMenuItem;
  15364. }(MenuItem);
  15365. /**
  15366. * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
  15367. *
  15368. * @type {string}
  15369. * @private
  15370. */
  15371. PlaybackRateMenuItem.prototype.contentElType = 'button';
  15372. Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
  15373. /**
  15374. * @file playback-rate-menu-button.js
  15375. */
  15376. /**
  15377. * The component for controlling the playback rate.
  15378. *
  15379. * @extends MenuButton
  15380. */
  15381. var PlaybackRateMenuButton = function (_MenuButton) {
  15382. inherits(PlaybackRateMenuButton, _MenuButton);
  15383. /**
  15384. * Creates an instance of this class.
  15385. *
  15386. * @param {Player} player
  15387. * The `Player` that this class should be attached to.
  15388. *
  15389. * @param {Object} [options]
  15390. * The key/value store of player options.
  15391. */
  15392. function PlaybackRateMenuButton(player, options) {
  15393. classCallCheck(this, PlaybackRateMenuButton);
  15394. var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
  15395. _this.updateVisibility();
  15396. _this.updateLabel();
  15397. _this.on(player, 'loadstart', _this.updateVisibility);
  15398. _this.on(player, 'ratechange', _this.updateLabel);
  15399. return _this;
  15400. }
  15401. /**
  15402. * Create the `Component`'s DOM element
  15403. *
  15404. * @return {Element}
  15405. * The element that was created.
  15406. */
  15407. PlaybackRateMenuButton.prototype.createEl = function createEl$$1() {
  15408. var el = _MenuButton.prototype.createEl.call(this);
  15409. this.labelEl_ = createEl('div', {
  15410. className: 'vjs-playback-rate-value',
  15411. innerHTML: '1x'
  15412. });
  15413. el.appendChild(this.labelEl_);
  15414. return el;
  15415. };
  15416. PlaybackRateMenuButton.prototype.dispose = function dispose() {
  15417. this.labelEl_ = null;
  15418. _MenuButton.prototype.dispose.call(this);
  15419. };
  15420. /**
  15421. * Builds the default DOM `className`.
  15422. *
  15423. * @return {string}
  15424. * The DOM `className` for this object.
  15425. */
  15426. PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
  15427. return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
  15428. };
  15429. PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  15430. return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this);
  15431. };
  15432. /**
  15433. * Create the playback rate menu
  15434. *
  15435. * @return {Menu}
  15436. * Menu object populated with {@link PlaybackRateMenuItem}s
  15437. */
  15438. PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
  15439. var menu = new Menu(this.player());
  15440. var rates = this.playbackRates();
  15441. if (rates) {
  15442. for (var i = rates.length - 1; i >= 0; i--) {
  15443. menu.addChild(new PlaybackRateMenuItem(this.player(), { rate: rates[i] + 'x' }));
  15444. }
  15445. }
  15446. return menu;
  15447. };
  15448. /**
  15449. * Updates ARIA accessibility attributes
  15450. */
  15451. PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
  15452. // Current playback rate
  15453. this.el().setAttribute('aria-valuenow', this.player().playbackRate());
  15454. };
  15455. /**
  15456. * This gets called when an `PlaybackRateMenuButton` is "clicked". See
  15457. * {@link ClickableComponent} for more detailed information on what a click can be.
  15458. *
  15459. * @param {EventTarget~Event} [event]
  15460. * The `keydown`, `tap`, or `click` event that caused this function to be
  15461. * called.
  15462. *
  15463. * @listens tap
  15464. * @listens click
  15465. */
  15466. PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
  15467. // select next rate option
  15468. var currentRate = this.player().playbackRate();
  15469. var rates = this.playbackRates();
  15470. // this will select first one if the last one currently selected
  15471. var newRate = rates[0];
  15472. for (var i = 0; i < rates.length; i++) {
  15473. if (rates[i] > currentRate) {
  15474. newRate = rates[i];
  15475. break;
  15476. }
  15477. }
  15478. this.player().playbackRate(newRate);
  15479. };
  15480. /**
  15481. * Get possible playback rates
  15482. *
  15483. * @return {Array}
  15484. * All possible playback rates
  15485. */
  15486. PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
  15487. return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
  15488. };
  15489. /**
  15490. * Get whether playback rates is supported by the tech
  15491. * and an array of playback rates exists
  15492. *
  15493. * @return {boolean}
  15494. * Whether changing playback rate is supported
  15495. */
  15496. PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
  15497. return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
  15498. };
  15499. /**
  15500. * Hide playback rate controls when they're no playback rate options to select
  15501. *
  15502. * @param {EventTarget~Event} [event]
  15503. * The event that caused this function to run.
  15504. *
  15505. * @listens Player#loadstart
  15506. */
  15507. PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
  15508. if (this.playbackRateSupported()) {
  15509. this.removeClass('vjs-hidden');
  15510. } else {
  15511. this.addClass('vjs-hidden');
  15512. }
  15513. };
  15514. /**
  15515. * Update button label when rate changed
  15516. *
  15517. * @param {EventTarget~Event} [event]
  15518. * The event that caused this function to run.
  15519. *
  15520. * @listens Player#ratechange
  15521. */
  15522. PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
  15523. if (this.playbackRateSupported()) {
  15524. this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
  15525. }
  15526. };
  15527. return PlaybackRateMenuButton;
  15528. }(MenuButton);
  15529. /**
  15530. * The text that should display over the `FullscreenToggle`s controls. Added for localization.
  15531. *
  15532. * @type {string}
  15533. * @private
  15534. */
  15535. PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
  15536. Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
  15537. /**
  15538. * @file spacer.js
  15539. */
  15540. /**
  15541. * Just an empty spacer element that can be used as an append point for plugins, etc.
  15542. * Also can be used to create space between elements when necessary.
  15543. *
  15544. * @extends Component
  15545. */
  15546. var Spacer = function (_Component) {
  15547. inherits(Spacer, _Component);
  15548. function Spacer() {
  15549. classCallCheck(this, Spacer);
  15550. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  15551. }
  15552. /**
  15553. * Builds the default DOM `className`.
  15554. *
  15555. * @return {string}
  15556. * The DOM `className` for this object.
  15557. */
  15558. Spacer.prototype.buildCSSClass = function buildCSSClass() {
  15559. return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
  15560. };
  15561. /**
  15562. * Create the `Component`'s DOM element
  15563. *
  15564. * @return {Element}
  15565. * The element that was created.
  15566. */
  15567. Spacer.prototype.createEl = function createEl() {
  15568. return _Component.prototype.createEl.call(this, 'div', {
  15569. className: this.buildCSSClass()
  15570. });
  15571. };
  15572. return Spacer;
  15573. }(Component);
  15574. Component.registerComponent('Spacer', Spacer);
  15575. /**
  15576. * @file custom-control-spacer.js
  15577. */
  15578. /**
  15579. * Spacer specifically meant to be used as an insertion point for new plugins, etc.
  15580. *
  15581. * @extends Spacer
  15582. */
  15583. var CustomControlSpacer = function (_Spacer) {
  15584. inherits(CustomControlSpacer, _Spacer);
  15585. function CustomControlSpacer() {
  15586. classCallCheck(this, CustomControlSpacer);
  15587. return possibleConstructorReturn(this, _Spacer.apply(this, arguments));
  15588. }
  15589. /**
  15590. * Builds the default DOM `className`.
  15591. *
  15592. * @return {string}
  15593. * The DOM `className` for this object.
  15594. */
  15595. CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
  15596. return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
  15597. };
  15598. /**
  15599. * Create the `Component`'s DOM element
  15600. *
  15601. * @return {Element}
  15602. * The element that was created.
  15603. */
  15604. CustomControlSpacer.prototype.createEl = function createEl() {
  15605. var el = _Spacer.prototype.createEl.call(this, {
  15606. className: this.buildCSSClass()
  15607. });
  15608. // No-flex/table-cell mode requires there be some content
  15609. // in the cell to fill the remaining space of the table.
  15610. el.innerHTML = '\xA0';
  15611. return el;
  15612. };
  15613. return CustomControlSpacer;
  15614. }(Spacer);
  15615. Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
  15616. /**
  15617. * @file control-bar.js
  15618. */
  15619. // Required children
  15620. /**
  15621. * Container of main controls.
  15622. *
  15623. * @extends Component
  15624. */
  15625. var ControlBar = function (_Component) {
  15626. inherits(ControlBar, _Component);
  15627. function ControlBar() {
  15628. classCallCheck(this, ControlBar);
  15629. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  15630. }
  15631. /**
  15632. * Create the `Component`'s DOM element
  15633. *
  15634. * @return {Element}
  15635. * The element that was created.
  15636. */
  15637. ControlBar.prototype.createEl = function createEl() {
  15638. return _Component.prototype.createEl.call(this, 'div', {
  15639. className: 'vjs-control-bar',
  15640. dir: 'ltr'
  15641. });
  15642. };
  15643. return ControlBar;
  15644. }(Component);
  15645. /**
  15646. * Default options for `ControlBar`
  15647. *
  15648. * @type {Object}
  15649. * @private
  15650. */
  15651. ControlBar.prototype.options_ = {
  15652. children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
  15653. };
  15654. Component.registerComponent('ControlBar', ControlBar);
  15655. /**
  15656. * @file error-display.js
  15657. */
  15658. /**
  15659. * A display that indicates an error has occurred. This means that the video
  15660. * is unplayable.
  15661. *
  15662. * @extends ModalDialog
  15663. */
  15664. var ErrorDisplay = function (_ModalDialog) {
  15665. inherits(ErrorDisplay, _ModalDialog);
  15666. /**
  15667. * Creates an instance of this class.
  15668. *
  15669. * @param {Player} player
  15670. * The `Player` that this class should be attached to.
  15671. *
  15672. * @param {Object} [options]
  15673. * The key/value store of player options.
  15674. */
  15675. function ErrorDisplay(player, options) {
  15676. classCallCheck(this, ErrorDisplay);
  15677. var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
  15678. _this.on(player, 'error', _this.open);
  15679. return _this;
  15680. }
  15681. /**
  15682. * Builds the default DOM `className`.
  15683. *
  15684. * @return {string}
  15685. * The DOM `className` for this object.
  15686. *
  15687. * @deprecated Since version 5.
  15688. */
  15689. ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
  15690. return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
  15691. };
  15692. /**
  15693. * Gets the localized error message based on the `Player`s error.
  15694. *
  15695. * @return {string}
  15696. * The `Player`s error message localized or an empty string.
  15697. */
  15698. ErrorDisplay.prototype.content = function content() {
  15699. var error = this.player().error();
  15700. return error ? this.localize(error.message) : '';
  15701. };
  15702. return ErrorDisplay;
  15703. }(ModalDialog);
  15704. /**
  15705. * The default options for an `ErrorDisplay`.
  15706. *
  15707. * @private
  15708. */
  15709. ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
  15710. pauseOnOpen: false,
  15711. fillAlways: true,
  15712. temporary: false,
  15713. uncloseable: true
  15714. });
  15715. Component.registerComponent('ErrorDisplay', ErrorDisplay);
  15716. /**
  15717. * @file text-track-settings.js
  15718. */
  15719. var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
  15720. var COLOR_BLACK = ['#000', 'Black'];
  15721. var COLOR_BLUE = ['#00F', 'Blue'];
  15722. var COLOR_CYAN = ['#0FF', 'Cyan'];
  15723. var COLOR_GREEN = ['#0F0', 'Green'];
  15724. var COLOR_MAGENTA = ['#F0F', 'Magenta'];
  15725. var COLOR_RED = ['#F00', 'Red'];
  15726. var COLOR_WHITE = ['#FFF', 'White'];
  15727. var COLOR_YELLOW = ['#FF0', 'Yellow'];
  15728. var OPACITY_OPAQUE = ['1', 'Opaque'];
  15729. var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
  15730. var OPACITY_TRANS = ['0', 'Transparent'];
  15731. // Configuration for the various <select> elements in the DOM of this component.
  15732. //
  15733. // Possible keys include:
  15734. //
  15735. // `default`:
  15736. // The default option index. Only needs to be provided if not zero.
  15737. // `parser`:
  15738. // A function which is used to parse the value from the selected option in
  15739. // a customized way.
  15740. // `selector`:
  15741. // The selector used to find the associated <select> element.
  15742. var selectConfigs = {
  15743. backgroundColor: {
  15744. selector: '.vjs-bg-color > select',
  15745. id: 'captions-background-color-%s',
  15746. label: 'Color',
  15747. options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
  15748. },
  15749. backgroundOpacity: {
  15750. selector: '.vjs-bg-opacity > select',
  15751. id: 'captions-background-opacity-%s',
  15752. label: 'Transparency',
  15753. options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
  15754. },
  15755. color: {
  15756. selector: '.vjs-fg-color > select',
  15757. id: 'captions-foreground-color-%s',
  15758. label: 'Color',
  15759. options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
  15760. },
  15761. edgeStyle: {
  15762. selector: '.vjs-edge-style > select',
  15763. id: '%s',
  15764. label: 'Text Edge Style',
  15765. options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
  15766. },
  15767. fontFamily: {
  15768. selector: '.vjs-font-family > select',
  15769. id: 'captions-font-family-%s',
  15770. label: 'Font Family',
  15771. options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
  15772. },
  15773. fontPercent: {
  15774. selector: '.vjs-font-percent > select',
  15775. id: 'captions-font-size-%s',
  15776. label: 'Font Size',
  15777. 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%']],
  15778. 'default': 2,
  15779. parser: function parser(v) {
  15780. return v === '1.00' ? null : Number(v);
  15781. }
  15782. },
  15783. textOpacity: {
  15784. selector: '.vjs-text-opacity > select',
  15785. id: 'captions-foreground-opacity-%s',
  15786. label: 'Transparency',
  15787. options: [OPACITY_OPAQUE, OPACITY_SEMI]
  15788. },
  15789. // Options for this object are defined below.
  15790. windowColor: {
  15791. selector: '.vjs-window-color > select',
  15792. id: 'captions-window-color-%s',
  15793. label: 'Color'
  15794. },
  15795. // Options for this object are defined below.
  15796. windowOpacity: {
  15797. selector: '.vjs-window-opacity > select',
  15798. id: 'captions-window-opacity-%s',
  15799. label: 'Transparency',
  15800. options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
  15801. }
  15802. };
  15803. selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
  15804. /**
  15805. * Get the actual value of an option.
  15806. *
  15807. * @param {string} value
  15808. * The value to get
  15809. *
  15810. * @param {Function} [parser]
  15811. * Optional function to adjust the value.
  15812. *
  15813. * @return {Mixed}
  15814. * - Will be `undefined` if no value exists
  15815. * - Will be `undefined` if the given value is "none".
  15816. * - Will be the actual value otherwise.
  15817. *
  15818. * @private
  15819. */
  15820. function parseOptionValue(value, parser) {
  15821. if (parser) {
  15822. value = parser(value);
  15823. }
  15824. if (value && value !== 'none') {
  15825. return value;
  15826. }
  15827. }
  15828. /**
  15829. * Gets the value of the selected <option> element within a <select> element.
  15830. *
  15831. * @param {Element} el
  15832. * the element to look in
  15833. *
  15834. * @param {Function} [parser]
  15835. * Optional function to adjust the value.
  15836. *
  15837. * @return {Mixed}
  15838. * - Will be `undefined` if no value exists
  15839. * - Will be `undefined` if the given value is "none".
  15840. * - Will be the actual value otherwise.
  15841. *
  15842. * @private
  15843. */
  15844. function getSelectedOptionValue(el, parser) {
  15845. var value = el.options[el.options.selectedIndex].value;
  15846. return parseOptionValue(value, parser);
  15847. }
  15848. /**
  15849. * Sets the selected <option> element within a <select> element based on a
  15850. * given value.
  15851. *
  15852. * @param {Element} el
  15853. * The element to look in.
  15854. *
  15855. * @param {string} value
  15856. * the property to look on.
  15857. *
  15858. * @param {Function} [parser]
  15859. * Optional function to adjust the value before comparing.
  15860. *
  15861. * @private
  15862. */
  15863. function setSelectedOption(el, value, parser) {
  15864. if (!value) {
  15865. return;
  15866. }
  15867. for (var i = 0; i < el.options.length; i++) {
  15868. if (parseOptionValue(el.options[i].value, parser) === value) {
  15869. el.selectedIndex = i;
  15870. break;
  15871. }
  15872. }
  15873. }
  15874. /**
  15875. * Manipulate Text Tracks settings.
  15876. *
  15877. * @extends ModalDialog
  15878. */
  15879. var TextTrackSettings = function (_ModalDialog) {
  15880. inherits(TextTrackSettings, _ModalDialog);
  15881. /**
  15882. * Creates an instance of this class.
  15883. *
  15884. * @param {Player} player
  15885. * The `Player` that this class should be attached to.
  15886. *
  15887. * @param {Object} [options]
  15888. * The key/value store of player options.
  15889. */
  15890. function TextTrackSettings(player, options) {
  15891. classCallCheck(this, TextTrackSettings);
  15892. options.temporary = false;
  15893. var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
  15894. _this.updateDisplay = bind(_this, _this.updateDisplay);
  15895. // fill the modal and pretend we have opened it
  15896. _this.fill();
  15897. _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
  15898. _this.endDialog = createEl('p', {
  15899. className: 'vjs-control-text',
  15900. textContent: _this.localize('End of dialog window.')
  15901. });
  15902. _this.el().appendChild(_this.endDialog);
  15903. _this.setDefaults();
  15904. // Grab `persistTextTrackSettings` from the player options if not passed in child options
  15905. if (options.persistTextTrackSettings === undefined) {
  15906. _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
  15907. }
  15908. _this.on(_this.$('.vjs-done-button'), 'click', function () {
  15909. _this.saveSettings();
  15910. _this.close();
  15911. });
  15912. _this.on(_this.$('.vjs-default-button'), 'click', function () {
  15913. _this.setDefaults();
  15914. _this.updateDisplay();
  15915. });
  15916. each(selectConfigs, function (config) {
  15917. _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
  15918. });
  15919. if (_this.options_.persistTextTrackSettings) {
  15920. _this.restoreSettings();
  15921. }
  15922. return _this;
  15923. }
  15924. TextTrackSettings.prototype.dispose = function dispose() {
  15925. this.endDialog = null;
  15926. _ModalDialog.prototype.dispose.call(this);
  15927. };
  15928. /**
  15929. * Create a <select> element with configured options.
  15930. *
  15931. * @param {string} key
  15932. * Configuration key to use during creation.
  15933. *
  15934. * @return {string}
  15935. * An HTML string.
  15936. *
  15937. * @private
  15938. */
  15939. TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
  15940. var _this2 = this;
  15941. var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
  15942. var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label';
  15943. var config = selectConfigs[key];
  15944. var id = config.id.replace('%s', this.id_);
  15945. var selectLabelledbyIds = [legendId, id].join(' ').trim();
  15946. return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + selectLabelledbyIds + '">'].concat(config.options.map(function (o) {
  15947. var optionId = id + '-' + o[1].replace(/\W+/g, '');
  15948. return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + selectLabelledbyIds + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join('');
  15949. })).concat('</select>').join('');
  15950. };
  15951. /**
  15952. * Create foreground color element for the component
  15953. *
  15954. * @return {string}
  15955. * An HTML string.
  15956. *
  15957. * @private
  15958. */
  15959. TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
  15960. var legendId = 'captions-text-legend-' + this.id_;
  15961. return ['<fieldset class="vjs-fg-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Text'), '</legend>', this.createElSelect_('color', legendId), '<span class="vjs-text-opacity vjs-opacity">', this.createElSelect_('textOpacity', legendId), '</span>', '</fieldset>'].join('');
  15962. };
  15963. /**
  15964. * Create background color element for the component
  15965. *
  15966. * @return {string}
  15967. * An HTML string.
  15968. *
  15969. * @private
  15970. */
  15971. TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
  15972. var legendId = 'captions-background-' + this.id_;
  15973. return ['<fieldset class="vjs-bg-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Background'), '</legend>', this.createElSelect_('backgroundColor', legendId), '<span class="vjs-bg-opacity vjs-opacity">', this.createElSelect_('backgroundOpacity', legendId), '</span>', '</fieldset>'].join('');
  15974. };
  15975. /**
  15976. * Create window color element for the component
  15977. *
  15978. * @return {string}
  15979. * An HTML string.
  15980. *
  15981. * @private
  15982. */
  15983. TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
  15984. var legendId = 'captions-window-' + this.id_;
  15985. return ['<fieldset class="vjs-window-color vjs-track-setting">', '<legend id="' + legendId + '">', this.localize('Window'), '</legend>', this.createElSelect_('windowColor', legendId), '<span class="vjs-window-opacity vjs-opacity">', this.createElSelect_('windowOpacity', legendId), '</span>', '</fieldset>'].join('');
  15986. };
  15987. /**
  15988. * Create color elements for the component
  15989. *
  15990. * @return {Element}
  15991. * The element that was created
  15992. *
  15993. * @private
  15994. */
  15995. TextTrackSettings.prototype.createElColors_ = function createElColors_() {
  15996. return createEl('div', {
  15997. className: 'vjs-track-settings-colors',
  15998. innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
  15999. });
  16000. };
  16001. /**
  16002. * Create font elements for the component
  16003. *
  16004. * @return {Element}
  16005. * The element that was created.
  16006. *
  16007. * @private
  16008. */
  16009. TextTrackSettings.prototype.createElFont_ = function createElFont_() {
  16010. return createEl('div', {
  16011. className: 'vjs-track-settings-font',
  16012. innerHTML: ['<fieldset class="vjs-font-percent vjs-track-setting">', this.createElSelect_('fontPercent', '', 'legend'), '</fieldset>', '<fieldset class="vjs-edge-style vjs-track-setting">', this.createElSelect_('edgeStyle', '', 'legend'), '</fieldset>', '<fieldset class="vjs-font-family vjs-track-setting">', this.createElSelect_('fontFamily', '', 'legend'), '</fieldset>'].join('')
  16013. });
  16014. };
  16015. /**
  16016. * Create controls for the component
  16017. *
  16018. * @return {Element}
  16019. * The element that was created.
  16020. *
  16021. * @private
  16022. */
  16023. TextTrackSettings.prototype.createElControls_ = function createElControls_() {
  16024. var defaultsDescription = this.localize('restore all settings to the default values');
  16025. return createEl('div', {
  16026. className: 'vjs-track-settings-controls',
  16027. innerHTML: ['<button type="button" class="vjs-default-button" title="' + defaultsDescription + '">', this.localize('Reset'), '<span class="vjs-control-text"> ' + defaultsDescription + '</span>', '</button>', '<button type="button" class="vjs-done-button">' + this.localize('Done') + '</button>'].join('')
  16028. });
  16029. };
  16030. TextTrackSettings.prototype.content = function content() {
  16031. return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
  16032. };
  16033. TextTrackSettings.prototype.label = function label() {
  16034. return this.localize('Caption Settings Dialog');
  16035. };
  16036. TextTrackSettings.prototype.description = function description() {
  16037. return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
  16038. };
  16039. TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() {
  16040. return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
  16041. };
  16042. /**
  16043. * Gets an object of text track settings (or null).
  16044. *
  16045. * @return {Object}
  16046. * An object with config values parsed from the DOM or localStorage.
  16047. */
  16048. TextTrackSettings.prototype.getValues = function getValues() {
  16049. var _this3 = this;
  16050. return reduce(selectConfigs, function (accum, config, key) {
  16051. var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
  16052. if (value !== undefined) {
  16053. accum[key] = value;
  16054. }
  16055. return accum;
  16056. }, {});
  16057. };
  16058. /**
  16059. * Sets text track settings from an object of values.
  16060. *
  16061. * @param {Object} values
  16062. * An object with config values parsed from the DOM or localStorage.
  16063. */
  16064. TextTrackSettings.prototype.setValues = function setValues(values) {
  16065. var _this4 = this;
  16066. each(selectConfigs, function (config, key) {
  16067. setSelectedOption(_this4.$(config.selector), values[key], config.parser);
  16068. });
  16069. };
  16070. /**
  16071. * Sets all `<select>` elements to their default values.
  16072. */
  16073. TextTrackSettings.prototype.setDefaults = function setDefaults() {
  16074. var _this5 = this;
  16075. each(selectConfigs, function (config) {
  16076. var index = config.hasOwnProperty('default') ? config['default'] : 0;
  16077. _this5.$(config.selector).selectedIndex = index;
  16078. });
  16079. };
  16080. /**
  16081. * Restore texttrack settings from localStorage
  16082. */
  16083. TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
  16084. var values = void 0;
  16085. try {
  16086. values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY));
  16087. } catch (err) {
  16088. log.warn(err);
  16089. }
  16090. if (values) {
  16091. this.setValues(values);
  16092. }
  16093. };
  16094. /**
  16095. * Save text track settings to localStorage
  16096. */
  16097. TextTrackSettings.prototype.saveSettings = function saveSettings() {
  16098. if (!this.options_.persistTextTrackSettings) {
  16099. return;
  16100. }
  16101. var values = this.getValues();
  16102. try {
  16103. if (Object.keys(values).length) {
  16104. window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
  16105. } else {
  16106. window_1.localStorage.removeItem(LOCAL_STORAGE_KEY);
  16107. }
  16108. } catch (err) {
  16109. log.warn(err);
  16110. }
  16111. };
  16112. /**
  16113. * Update display of text track settings
  16114. */
  16115. TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
  16116. var ttDisplay = this.player_.getChild('textTrackDisplay');
  16117. if (ttDisplay) {
  16118. ttDisplay.updateDisplay();
  16119. }
  16120. };
  16121. /**
  16122. * conditionally blur the element and refocus the captions button
  16123. *
  16124. * @private
  16125. */
  16126. TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() {
  16127. this.previouslyActiveEl_ = null;
  16128. this.off(document_1, 'keydown', this.handleKeyDown);
  16129. var cb = this.player_.controlBar;
  16130. var subsCapsBtn = cb && cb.subsCapsButton;
  16131. var ccBtn = cb && cb.captionsButton;
  16132. if (subsCapsBtn) {
  16133. subsCapsBtn.focus();
  16134. } else if (ccBtn) {
  16135. ccBtn.focus();
  16136. }
  16137. };
  16138. return TextTrackSettings;
  16139. }(ModalDialog);
  16140. Component.registerComponent('TextTrackSettings', TextTrackSettings);
  16141. /**
  16142. * @file resize-manager.js
  16143. */
  16144. /**
  16145. * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
  16146. *
  16147. * It'll either create an iframe and use a debounced resize handler on it or use the new {@link https://wicg.github.io/ResizeObserver/|ResizeObserver}.
  16148. *
  16149. * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
  16150. * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
  16151. * @example <caption>How to disable the resize manager</caption>
  16152. * const player = videojs('#vid', {
  16153. * resizeManager: false
  16154. * });
  16155. *
  16156. * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
  16157. *
  16158. * @extends Component
  16159. */
  16160. var ResizeManager = function (_Component) {
  16161. inherits(ResizeManager, _Component);
  16162. /**
  16163. * Create the ResizeManager.
  16164. *
  16165. * @param {Object} player
  16166. * The `Player` that this class should be attached to.
  16167. *
  16168. * @param {Object} [options]
  16169. * The key/value store of ResizeManager options.
  16170. *
  16171. * @param {Object} [options.ResizeObserver]
  16172. * A polyfill for ResizeObserver can be passed in here.
  16173. * If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
  16174. */
  16175. function ResizeManager(player, options) {
  16176. classCallCheck(this, ResizeManager);
  16177. var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window_1.ResizeObserver;
  16178. // if `null` was passed, we want to disable the ResizeObserver
  16179. if (options.ResizeObserver === null) {
  16180. RESIZE_OBSERVER_AVAILABLE = false;
  16181. }
  16182. // Only create an element when ResizeObserver isn't available
  16183. var options_ = mergeOptions({ createEl: !RESIZE_OBSERVER_AVAILABLE }, options);
  16184. var _this = possibleConstructorReturn(this, _Component.call(this, player, options_));
  16185. _this.ResizeObserver = options.ResizeObserver || window_1.ResizeObserver;
  16186. _this.loadListener_ = null;
  16187. _this.resizeObserver_ = null;
  16188. _this.debouncedHandler_ = debounce(function () {
  16189. _this.resizeHandler();
  16190. }, 100, false, _this);
  16191. if (RESIZE_OBSERVER_AVAILABLE) {
  16192. _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
  16193. _this.resizeObserver_.observe(player.el());
  16194. } else {
  16195. _this.loadListener_ = function () {
  16196. if (!_this.el_ || !_this.el_.contentWindow) {
  16197. return;
  16198. }
  16199. on(_this.el_.contentWindow, 'resize', _this.debouncedHandler_);
  16200. };
  16201. _this.one('load', _this.loadListener_);
  16202. }
  16203. return _this;
  16204. }
  16205. ResizeManager.prototype.createEl = function createEl() {
  16206. return _Component.prototype.createEl.call(this, 'iframe', {
  16207. className: 'vjs-resize-manager'
  16208. });
  16209. };
  16210. /**
  16211. * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
  16212. *
  16213. * @fires Player#playerresize
  16214. */
  16215. ResizeManager.prototype.resizeHandler = function resizeHandler() {
  16216. /**
  16217. * Called when the player size has changed
  16218. *
  16219. * @event Player#playerresize
  16220. * @type {EventTarget~Event}
  16221. */
  16222. // make sure player is still around to trigger
  16223. // prevents this from causing an error after dispose
  16224. if (!this.player_ || !this.player_.trigger) {
  16225. return;
  16226. }
  16227. this.player_.trigger('playerresize');
  16228. };
  16229. ResizeManager.prototype.dispose = function dispose() {
  16230. if (this.debouncedHandler_) {
  16231. this.debouncedHandler_.cancel();
  16232. }
  16233. if (this.resizeObserver_) {
  16234. if (this.player_.el()) {
  16235. this.resizeObserver_.unobserve(this.player_.el());
  16236. }
  16237. this.resizeObserver_.disconnect();
  16238. }
  16239. if (this.el_ && this.el_.contentWindow) {
  16240. off(this.el_.contentWindow, 'resize', this.debouncedHandler_);
  16241. }
  16242. if (this.loadListener_) {
  16243. this.off('load', this.loadListener_);
  16244. }
  16245. this.ResizeObserver = null;
  16246. this.resizeObserver = null;
  16247. this.debouncedHandler_ = null;
  16248. this.loadListener_ = null;
  16249. };
  16250. return ResizeManager;
  16251. }(Component);
  16252. Component.registerComponent('ResizeManager', ResizeManager);
  16253. /**
  16254. * This function is used to fire a sourceset when there is something
  16255. * similar to `mediaEl.load()` being called. It will try to find the source via
  16256. * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
  16257. * with the source that was found or empty string if we cannot know. If it cannot
  16258. * find a source then `sourceset` will not be fired.
  16259. *
  16260. * @param {Html5} tech
  16261. * The tech object that sourceset was setup on
  16262. *
  16263. * @return {boolean}
  16264. * returns false if the sourceset was not fired and true otherwise.
  16265. */
  16266. var sourcesetLoad = function sourcesetLoad(tech) {
  16267. var el = tech.el();
  16268. // if `el.src` is set, that source will be loaded.
  16269. if (el.hasAttribute('src')) {
  16270. tech.triggerSourceset(el.src);
  16271. return true;
  16272. }
  16273. /**
  16274. * Since there isn't a src property on the media element, source elements will be used for
  16275. * implementing the source selection algorithm. This happens asynchronously and
  16276. * for most cases were there is more than one source we cannot tell what source will
  16277. * be loaded, without re-implementing the source selection algorithm. At this time we are not
  16278. * going to do that. There are three special cases that we do handle here though:
  16279. *
  16280. * 1. If there are no sources, do not fire `sourceset`.
  16281. * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
  16282. * 3. If there is more than one `<source>` but all of them have the same `src` url.
  16283. * That will be our src.
  16284. */
  16285. var sources = tech.$$('source');
  16286. var srcUrls = [];
  16287. var src = '';
  16288. // if there are no sources, do not fire sourceset
  16289. if (!sources.length) {
  16290. return false;
  16291. }
  16292. // only count valid/non-duplicate source elements
  16293. for (var i = 0; i < sources.length; i++) {
  16294. var url = sources[i].src;
  16295. if (url && srcUrls.indexOf(url) === -1) {
  16296. srcUrls.push(url);
  16297. }
  16298. }
  16299. // there were no valid sources
  16300. if (!srcUrls.length) {
  16301. return false;
  16302. }
  16303. // there is only one valid source element url
  16304. // use that
  16305. if (srcUrls.length === 1) {
  16306. src = srcUrls[0];
  16307. }
  16308. tech.triggerSourceset(src);
  16309. return true;
  16310. };
  16311. /**
  16312. * our implementation of an `innerHTML` descriptor for browsers
  16313. * that do not have one.
  16314. */
  16315. var innerHTMLDescriptorPolyfill = {};
  16316. if (!IS_IE8) {
  16317. innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
  16318. get: function get() {
  16319. return this.cloneNode(true).innerHTML;
  16320. },
  16321. set: function set(v) {
  16322. // make a dummy node to use innerHTML on
  16323. var dummy = document_1.createElement(this.nodeName.toLowerCase());
  16324. // set innerHTML to the value provided
  16325. dummy.innerHTML = v;
  16326. // make a document fragment to hold the nodes from dummy
  16327. var docFrag = document_1.createDocumentFragment();
  16328. // copy all of the nodes created by the innerHTML on dummy
  16329. // to the document fragment
  16330. while (dummy.childNodes.length) {
  16331. docFrag.appendChild(dummy.childNodes[0]);
  16332. }
  16333. // remove content
  16334. this.innerText = '';
  16335. // now we add all of that html in one by appending the
  16336. // document fragment. This is how innerHTML does it.
  16337. window_1.Element.prototype.appendChild.call(this, docFrag);
  16338. // then return the result that innerHTML's setter would
  16339. return this.innerHTML;
  16340. }
  16341. });
  16342. }
  16343. /**
  16344. * Get a property descriptor given a list of priorities and the
  16345. * property to get.
  16346. */
  16347. var getDescriptor = function getDescriptor(priority, prop) {
  16348. var descriptor = {};
  16349. for (var i = 0; i < priority.length; i++) {
  16350. descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
  16351. if (descriptor && descriptor.set && descriptor.get) {
  16352. break;
  16353. }
  16354. }
  16355. descriptor.enumerable = true;
  16356. descriptor.configurable = true;
  16357. return descriptor;
  16358. };
  16359. var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
  16360. return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, window_1.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
  16361. };
  16362. /**
  16363. * Patches browser internal functions so that we can tell syncronously
  16364. * if a `<source>` was appended to the media element. For some reason this
  16365. * causes a `sourceset` if the the media element is ready and has no source.
  16366. * This happens when:
  16367. * - The page has just loaded and the media element does not have a source.
  16368. * - The media element was emptied of all sources, then `load()` was called.
  16369. *
  16370. * It does this by patching the following functions/properties when they are supported:
  16371. *
  16372. * - `append()` - can be used to add a `<source>` element to the media element
  16373. * - `appendChild()` - can be used to add a `<source>` element to the media element
  16374. * - `insertAdjacentHTML()` - can be used to add a `<source>` element to the media element
  16375. * - `innerHTML` - can be used to add a `<source>` element to the media element
  16376. *
  16377. * @param {Html5} tech
  16378. * The tech object that sourceset is being setup on.
  16379. */
  16380. var firstSourceWatch = function firstSourceWatch(tech) {
  16381. var el = tech.el();
  16382. // make sure firstSourceWatch isn't setup twice.
  16383. if (el.resetSourceWatch_) {
  16384. return;
  16385. }
  16386. var old = {};
  16387. var innerDescriptor = getInnerHTMLDescriptor(tech);
  16388. var appendWrapper = function appendWrapper(appendFn) {
  16389. return function () {
  16390. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  16391. args[_key] = arguments[_key];
  16392. }
  16393. var retval = appendFn.apply(el, args);
  16394. sourcesetLoad(tech);
  16395. return retval;
  16396. };
  16397. };
  16398. ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
  16399. if (!el[k]) {
  16400. return;
  16401. }
  16402. // store the old function
  16403. old[k] = el[k];
  16404. // call the old function with a sourceset if a source
  16405. // was loaded
  16406. el[k] = appendWrapper(old[k]);
  16407. });
  16408. Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
  16409. set: appendWrapper(innerDescriptor.set)
  16410. }));
  16411. el.resetSourceWatch_ = function () {
  16412. el.resetSourceWatch_ = null;
  16413. Object.keys(old).forEach(function (k) {
  16414. el[k] = old[k];
  16415. });
  16416. Object.defineProperty(el, 'innerHTML', innerDescriptor);
  16417. };
  16418. // on the first sourceset, we need to revert our changes
  16419. tech.one('sourceset', el.resetSourceWatch_);
  16420. };
  16421. /**
  16422. * our implementation of a `src` descriptor for browsers
  16423. * that do not have one.
  16424. */
  16425. var srcDescriptorPolyfill = {};
  16426. if (!IS_IE8) {
  16427. srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
  16428. get: function get() {
  16429. if (this.hasAttribute('src')) {
  16430. return getAbsoluteURL(window_1.Element.prototype.getAttribute.call(this, 'src'));
  16431. }
  16432. return '';
  16433. },
  16434. set: function set(v) {
  16435. window_1.Element.prototype.setAttribute.call(this, 'src', v);
  16436. return v;
  16437. }
  16438. });
  16439. }
  16440. var getSrcDescriptor = function getSrcDescriptor(tech) {
  16441. return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
  16442. };
  16443. /**
  16444. * setup `sourceset` handling on the `Html5` tech. This function
  16445. * patches the following element properties/functions:
  16446. *
  16447. * - `src` - to determine when `src` is set
  16448. * - `setAttribute()` - to determine when `src` is set
  16449. * - `load()` - this re-triggers the source selection algorithm, and can
  16450. * cause a sourceset.
  16451. *
  16452. * If there is no source when we are adding `sourceset` support or during a `load()`
  16453. * we also patch the functions listed in `firstSourceWatch`.
  16454. *
  16455. * @param {Html5} tech
  16456. * The tech to patch
  16457. */
  16458. var setupSourceset = function setupSourceset(tech) {
  16459. if (!tech.featuresSourceset) {
  16460. return;
  16461. }
  16462. var el = tech.el();
  16463. // make sure sourceset isn't setup twice.
  16464. if (el.resetSourceset_) {
  16465. return;
  16466. }
  16467. var srcDescriptor = getSrcDescriptor(tech);
  16468. var oldSetAttribute = el.setAttribute;
  16469. var oldLoad = el.load;
  16470. Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
  16471. set: function set(v) {
  16472. var retval = srcDescriptor.set.call(el, v);
  16473. // we use the getter here to get the actual value set on src
  16474. tech.triggerSourceset(el.src);
  16475. return retval;
  16476. }
  16477. }));
  16478. el.setAttribute = function (n, v) {
  16479. var retval = oldSetAttribute.call(el, n, v);
  16480. if (/src/i.test(n)) {
  16481. tech.triggerSourceset(el.src);
  16482. }
  16483. return retval;
  16484. };
  16485. el.load = function () {
  16486. var retval = oldLoad.call(el);
  16487. // if load was called, but there was no source to fire
  16488. // sourceset on. We have to watch for a source append
  16489. // as that can trigger a `sourceset` when the media element
  16490. // has no source
  16491. if (!sourcesetLoad(tech)) {
  16492. tech.triggerSourceset('');
  16493. firstSourceWatch(tech);
  16494. }
  16495. return retval;
  16496. };
  16497. if (el.currentSrc) {
  16498. tech.triggerSourceset(el.currentSrc);
  16499. } else if (!sourcesetLoad(tech)) {
  16500. firstSourceWatch(tech);
  16501. }
  16502. el.resetSourceset_ = function () {
  16503. el.resetSourceset_ = null;
  16504. el.load = oldLoad;
  16505. el.setAttribute = oldSetAttribute;
  16506. Object.defineProperty(el, 'src', srcDescriptor);
  16507. if (el.resetSourceWatch_) {
  16508. el.resetSourceWatch_();
  16509. }
  16510. };
  16511. };
  16512. var _templateObject$2 = 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.']);
  16513. /**
  16514. * @file html5.js
  16515. */
  16516. /**
  16517. * HTML5 Media Controller - Wrapper for HTML5 Media API
  16518. *
  16519. * @mixes Tech~SouceHandlerAdditions
  16520. * @extends Tech
  16521. */
  16522. var Html5 = function (_Tech) {
  16523. inherits(Html5, _Tech);
  16524. /**
  16525. * Create an instance of this Tech.
  16526. *
  16527. * @param {Object} [options]
  16528. * The key/value store of player options.
  16529. *
  16530. * @param {Component~ReadyCallback} ready
  16531. * Callback function to call when the `HTML5` Tech is ready.
  16532. */
  16533. function Html5(options, ready) {
  16534. classCallCheck(this, Html5);
  16535. var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready));
  16536. var source = options.source;
  16537. var crossoriginTracks = false;
  16538. // Set the source if one is provided
  16539. // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
  16540. // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
  16541. // anyway so the error gets fired.
  16542. if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
  16543. _this.setSource(source);
  16544. } else {
  16545. _this.handleLateInit_(_this.el_);
  16546. }
  16547. // setup sourceset after late sourceset/init
  16548. if (options.enableSourceset) {
  16549. _this.setupSourcesetHandling_();
  16550. }
  16551. if (_this.el_.hasChildNodes()) {
  16552. var nodes = _this.el_.childNodes;
  16553. var nodesLength = nodes.length;
  16554. var removeNodes = [];
  16555. while (nodesLength--) {
  16556. var node = nodes[nodesLength];
  16557. var nodeName = node.nodeName.toLowerCase();
  16558. if (nodeName === 'track') {
  16559. if (!_this.featuresNativeTextTracks) {
  16560. // Empty video tag tracks so the built-in player doesn't use them also.
  16561. // This may not be fast enough to stop HTML5 browsers from reading the tags
  16562. // so we'll need to turn off any default tracks if we're manually doing
  16563. // captions and subtitles. videoElement.textTracks
  16564. removeNodes.push(node);
  16565. } else {
  16566. // store HTMLTrackElement and TextTrack to remote list
  16567. _this.remoteTextTrackEls().addTrackElement_(node);
  16568. _this.remoteTextTracks().addTrack(node.track);
  16569. _this.textTracks().addTrack(node.track);
  16570. if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
  16571. crossoriginTracks = true;
  16572. }
  16573. }
  16574. }
  16575. }
  16576. for (var i = 0; i < removeNodes.length; i++) {
  16577. _this.el_.removeChild(removeNodes[i]);
  16578. }
  16579. }
  16580. _this.proxyNativeTracks_();
  16581. if (_this.featuresNativeTextTracks && crossoriginTracks) {
  16582. log.warn(tsml(_templateObject$2));
  16583. }
  16584. // prevent iOS Safari from disabling metadata text tracks during native playback
  16585. _this.restoreMetadataTracksInIOSNativePlayer_();
  16586. // Determine if native controls should be used
  16587. // Our goal should be to get the custom controls on mobile solid everywhere
  16588. // so we can remove this all together. Right now this will block custom
  16589. // controls on touch enabled laptops like the Chrome Pixel
  16590. if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
  16591. _this.setControls(true);
  16592. }
  16593. // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
  16594. // into a `fullscreenchange` event
  16595. _this.proxyWebkitFullscreen_();
  16596. _this.triggerReady();
  16597. return _this;
  16598. }
  16599. /**
  16600. * Dispose of `HTML5` media element and remove all tracks.
  16601. */
  16602. Html5.prototype.dispose = function dispose() {
  16603. if (this.el_ && this.el_.resetSourceset_) {
  16604. this.el_.resetSourceset_();
  16605. }
  16606. Html5.disposeMediaElement(this.el_);
  16607. this.options_ = null;
  16608. // tech will handle clearing of the emulated track list
  16609. _Tech.prototype.dispose.call(this);
  16610. };
  16611. /**
  16612. * Modify the media element so that we can detect when
  16613. * the source is changed. Fires `sourceset` just after the source has changed
  16614. */
  16615. Html5.prototype.setupSourcesetHandling_ = function setupSourcesetHandling_() {
  16616. setupSourceset(this);
  16617. };
  16618. /**
  16619. * When a captions track is enabled in the iOS Safari native player, all other
  16620. * tracks are disabled (including metadata tracks), which nulls all of their
  16621. * associated cue points. This will restore metadata tracks to their pre-fullscreen
  16622. * state in those cases so that cue points are not needlessly lost.
  16623. *
  16624. * @private
  16625. */
  16626. Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
  16627. var textTracks = this.textTracks();
  16628. var metadataTracksPreFullscreenState = void 0;
  16629. // captures a snapshot of every metadata track's current state
  16630. var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
  16631. metadataTracksPreFullscreenState = [];
  16632. for (var i = 0; i < textTracks.length; i++) {
  16633. var track = textTracks[i];
  16634. if (track.kind === 'metadata') {
  16635. metadataTracksPreFullscreenState.push({
  16636. track: track,
  16637. storedMode: track.mode
  16638. });
  16639. }
  16640. }
  16641. };
  16642. // snapshot each metadata track's initial state, and update the snapshot
  16643. // each time there is a track 'change' event
  16644. takeMetadataTrackSnapshot();
  16645. textTracks.addEventListener('change', takeMetadataTrackSnapshot);
  16646. this.on('dispose', function () {
  16647. return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  16648. });
  16649. var restoreTrackMode = function restoreTrackMode() {
  16650. for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
  16651. var storedTrack = metadataTracksPreFullscreenState[i];
  16652. if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
  16653. storedTrack.track.mode = storedTrack.storedMode;
  16654. }
  16655. }
  16656. // we only want this handler to be executed on the first 'change' event
  16657. textTracks.removeEventListener('change', restoreTrackMode);
  16658. };
  16659. // when we enter fullscreen playback, stop updating the snapshot and
  16660. // restore all track modes to their pre-fullscreen state
  16661. this.on('webkitbeginfullscreen', function () {
  16662. textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  16663. // remove the listener before adding it just in case it wasn't previously removed
  16664. textTracks.removeEventListener('change', restoreTrackMode);
  16665. textTracks.addEventListener('change', restoreTrackMode);
  16666. });
  16667. // start updating the snapshot again after leaving fullscreen
  16668. this.on('webkitendfullscreen', function () {
  16669. // remove the listener before adding it just in case it wasn't previously removed
  16670. textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  16671. textTracks.addEventListener('change', takeMetadataTrackSnapshot);
  16672. // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
  16673. textTracks.removeEventListener('change', restoreTrackMode);
  16674. });
  16675. };
  16676. /**
  16677. * Proxy all native track list events to our track lists if the browser we are playing
  16678. * in supports that type of track list.
  16679. *
  16680. * @private
  16681. */
  16682. Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() {
  16683. var _this2 = this;
  16684. NORMAL.names.forEach(function (name) {
  16685. var props = NORMAL[name];
  16686. var elTracks = _this2.el()[props.getterName];
  16687. var techTracks = _this2[props.getterName]();
  16688. if (!_this2['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
  16689. return;
  16690. }
  16691. var listeners = {
  16692. change: function change(e) {
  16693. techTracks.trigger({
  16694. type: 'change',
  16695. target: techTracks,
  16696. currentTarget: techTracks,
  16697. srcElement: techTracks
  16698. });
  16699. },
  16700. addtrack: function addtrack(e) {
  16701. techTracks.addTrack(e.track);
  16702. },
  16703. removetrack: function removetrack(e) {
  16704. techTracks.removeTrack(e.track);
  16705. }
  16706. };
  16707. var removeOldTracks = function removeOldTracks() {
  16708. var removeTracks = [];
  16709. for (var i = 0; i < techTracks.length; i++) {
  16710. var found = false;
  16711. for (var j = 0; j < elTracks.length; j++) {
  16712. if (elTracks[j] === techTracks[i]) {
  16713. found = true;
  16714. break;
  16715. }
  16716. }
  16717. if (!found) {
  16718. removeTracks.push(techTracks[i]);
  16719. }
  16720. }
  16721. while (removeTracks.length) {
  16722. techTracks.removeTrack(removeTracks.shift());
  16723. }
  16724. };
  16725. Object.keys(listeners).forEach(function (eventName) {
  16726. var listener = listeners[eventName];
  16727. elTracks.addEventListener(eventName, listener);
  16728. _this2.on('dispose', function (e) {
  16729. return elTracks.removeEventListener(eventName, listener);
  16730. });
  16731. });
  16732. // Remove (native) tracks that are not used anymore
  16733. _this2.on('loadstart', removeOldTracks);
  16734. _this2.on('dispose', function (e) {
  16735. return _this2.off('loadstart', removeOldTracks);
  16736. });
  16737. });
  16738. };
  16739. /**
  16740. * Create the `Html5` Tech's DOM element.
  16741. *
  16742. * @return {Element}
  16743. * The element that gets created.
  16744. */
  16745. Html5.prototype.createEl = function createEl$$1() {
  16746. var el = this.options_.tag;
  16747. // Check if this browser supports moving the element into the box.
  16748. // On the iPhone video will break if you move the element,
  16749. // So we have to create a brand new element.
  16750. // If we ingested the player div, we do not need to move the media element.
  16751. if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
  16752. // If the original tag is still there, clone and remove it.
  16753. if (el) {
  16754. var clone = el.cloneNode(true);
  16755. if (el.parentNode) {
  16756. el.parentNode.insertBefore(clone, el);
  16757. }
  16758. Html5.disposeMediaElement(el);
  16759. el = clone;
  16760. } else {
  16761. el = document_1.createElement('video');
  16762. // determine if native controls should be used
  16763. var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
  16764. var attributes = mergeOptions({}, tagAttributes);
  16765. if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
  16766. delete attributes.controls;
  16767. }
  16768. setAttributes(el, assign(attributes, {
  16769. id: this.options_.techId,
  16770. 'class': 'vjs-tech'
  16771. }));
  16772. }
  16773. el.playerId = this.options_.playerId;
  16774. }
  16775. if (typeof this.options_.preload !== 'undefined') {
  16776. setAttribute(el, 'preload', this.options_.preload);
  16777. }
  16778. // Update specific tag settings, in case they were overridden
  16779. // `autoplay` has to be *last* so that `muted` and `playsinline` are present
  16780. // when iOS/Safari or other browsers attempt to autoplay.
  16781. var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
  16782. for (var i = 0; i < settingsAttrs.length; i++) {
  16783. var attr = settingsAttrs[i];
  16784. var value = this.options_[attr];
  16785. if (typeof value !== 'undefined') {
  16786. if (value) {
  16787. setAttribute(el, attr, attr);
  16788. } else {
  16789. removeAttribute(el, attr);
  16790. }
  16791. el[attr] = value;
  16792. }
  16793. }
  16794. return el;
  16795. };
  16796. /**
  16797. * This will be triggered if the loadstart event has already fired, before videojs was
  16798. * ready. Two known examples of when this can happen are:
  16799. * 1. If we're loading the playback object after it has started loading
  16800. * 2. The media is already playing the (often with autoplay on) then
  16801. *
  16802. * This function will fire another loadstart so that videojs can catchup.
  16803. *
  16804. * @fires Tech#loadstart
  16805. *
  16806. * @return {undefined}
  16807. * returns nothing.
  16808. */
  16809. Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
  16810. if (el.networkState === 0 || el.networkState === 3) {
  16811. // The video element hasn't started loading the source yet
  16812. // or didn't find a source
  16813. return;
  16814. }
  16815. if (el.readyState === 0) {
  16816. // NetworkState is set synchronously BUT loadstart is fired at the
  16817. // end of the current stack, usually before setInterval(fn, 0).
  16818. // So at this point we know loadstart may have already fired or is
  16819. // about to fire, and either way the player hasn't seen it yet.
  16820. // We don't want to fire loadstart prematurely here and cause a
  16821. // double loadstart so we'll wait and see if it happens between now
  16822. // and the next loop, and fire it if not.
  16823. // HOWEVER, we also want to make sure it fires before loadedmetadata
  16824. // which could also happen between now and the next loop, so we'll
  16825. // watch for that also.
  16826. var loadstartFired = false;
  16827. var setLoadstartFired = function setLoadstartFired() {
  16828. loadstartFired = true;
  16829. };
  16830. this.on('loadstart', setLoadstartFired);
  16831. var triggerLoadstart = function triggerLoadstart() {
  16832. // We did miss the original loadstart. Make sure the player
  16833. // sees loadstart before loadedmetadata
  16834. if (!loadstartFired) {
  16835. this.trigger('loadstart');
  16836. }
  16837. };
  16838. this.on('loadedmetadata', triggerLoadstart);
  16839. this.ready(function () {
  16840. this.off('loadstart', setLoadstartFired);
  16841. this.off('loadedmetadata', triggerLoadstart);
  16842. if (!loadstartFired) {
  16843. // We did miss the original native loadstart. Fire it now.
  16844. this.trigger('loadstart');
  16845. }
  16846. });
  16847. return;
  16848. }
  16849. // From here on we know that loadstart already fired and we missed it.
  16850. // The other readyState events aren't as much of a problem if we double
  16851. // them, so not going to go to as much trouble as loadstart to prevent
  16852. // that unless we find reason to.
  16853. var eventsToTrigger = ['loadstart'];
  16854. // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
  16855. eventsToTrigger.push('loadedmetadata');
  16856. // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
  16857. if (el.readyState >= 2) {
  16858. eventsToTrigger.push('loadeddata');
  16859. }
  16860. // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
  16861. if (el.readyState >= 3) {
  16862. eventsToTrigger.push('canplay');
  16863. }
  16864. // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
  16865. if (el.readyState >= 4) {
  16866. eventsToTrigger.push('canplaythrough');
  16867. }
  16868. // We still need to give the player time to add event listeners
  16869. this.ready(function () {
  16870. eventsToTrigger.forEach(function (type) {
  16871. this.trigger(type);
  16872. }, this);
  16873. });
  16874. };
  16875. /**
  16876. * Set current time for the `HTML5` tech.
  16877. *
  16878. * @param {number} seconds
  16879. * Set the current time of the media to this.
  16880. */
  16881. Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
  16882. try {
  16883. this.el_.currentTime = seconds;
  16884. } catch (e) {
  16885. log(e, 'Video is not ready. (Video.js)');
  16886. // this.warning(VideoJS.warnings.videoNotReady);
  16887. }
  16888. };
  16889. /**
  16890. * Get the current duration of the HTML5 media element.
  16891. *
  16892. * @return {number}
  16893. * The duration of the media or 0 if there is no duration.
  16894. */
  16895. Html5.prototype.duration = function duration() {
  16896. var _this3 = this;
  16897. // Android Chrome will report duration as Infinity for VOD HLS until after
  16898. // playback has started, which triggers the live display erroneously.
  16899. // Return NaN if playback has not started and trigger a durationupdate once
  16900. // the duration can be reliably known.
  16901. if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
  16902. // Wait for the first `timeupdate` with currentTime > 0 - there may be
  16903. // several with 0
  16904. var checkProgress = function checkProgress() {
  16905. if (_this3.el_.currentTime > 0) {
  16906. // Trigger durationchange for genuinely live video
  16907. if (_this3.el_.duration === Infinity) {
  16908. _this3.trigger('durationchange');
  16909. }
  16910. _this3.off('timeupdate', checkProgress);
  16911. }
  16912. };
  16913. this.on('timeupdate', checkProgress);
  16914. return NaN;
  16915. }
  16916. return this.el_.duration || NaN;
  16917. };
  16918. /**
  16919. * Get the current width of the HTML5 media element.
  16920. *
  16921. * @return {number}
  16922. * The width of the HTML5 media element.
  16923. */
  16924. Html5.prototype.width = function width() {
  16925. return this.el_.offsetWidth;
  16926. };
  16927. /**
  16928. * Get the current height of the HTML5 media element.
  16929. *
  16930. * @return {number}
  16931. * The heigth of the HTML5 media element.
  16932. */
  16933. Html5.prototype.height = function height() {
  16934. return this.el_.offsetHeight;
  16935. };
  16936. /**
  16937. * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
  16938. * `fullscreenchange` event.
  16939. *
  16940. * @private
  16941. * @fires fullscreenchange
  16942. * @listens webkitendfullscreen
  16943. * @listens webkitbeginfullscreen
  16944. * @listens webkitbeginfullscreen
  16945. */
  16946. Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
  16947. var _this4 = this;
  16948. if (!('webkitDisplayingFullscreen' in this.el_)) {
  16949. return;
  16950. }
  16951. var endFn = function endFn() {
  16952. this.trigger('fullscreenchange', { isFullscreen: false });
  16953. };
  16954. var beginFn = function beginFn() {
  16955. if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
  16956. this.one('webkitendfullscreen', endFn);
  16957. this.trigger('fullscreenchange', { isFullscreen: true });
  16958. }
  16959. };
  16960. this.on('webkitbeginfullscreen', beginFn);
  16961. this.on('dispose', function () {
  16962. _this4.off('webkitbeginfullscreen', beginFn);
  16963. _this4.off('webkitendfullscreen', endFn);
  16964. });
  16965. };
  16966. /**
  16967. * Check if fullscreen is supported on the current playback device.
  16968. *
  16969. * @return {boolean}
  16970. * - True if fullscreen is supported.
  16971. * - False if fullscreen is not supported.
  16972. */
  16973. Html5.prototype.supportsFullScreen = function supportsFullScreen() {
  16974. if (typeof this.el_.webkitEnterFullScreen === 'function') {
  16975. var userAgent = window_1.navigator && window_1.navigator.userAgent || '';
  16976. // Seems to be broken in Chromium/Chrome && Safari in Leopard
  16977. if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
  16978. return true;
  16979. }
  16980. }
  16981. return false;
  16982. };
  16983. /**
  16984. * Request that the `HTML5` Tech enter fullscreen.
  16985. */
  16986. Html5.prototype.enterFullScreen = function enterFullScreen() {
  16987. var video = this.el_;
  16988. if (video.paused && video.networkState <= video.HAVE_METADATA) {
  16989. // attempt to prime the video element for programmatic access
  16990. // this isn't necessary on the desktop but shouldn't hurt
  16991. this.el_.play();
  16992. // playing and pausing synchronously during the transition to fullscreen
  16993. // can get iOS ~6.1 devices into a play/pause loop
  16994. this.setTimeout(function () {
  16995. video.pause();
  16996. video.webkitEnterFullScreen();
  16997. }, 0);
  16998. } else {
  16999. video.webkitEnterFullScreen();
  17000. }
  17001. };
  17002. /**
  17003. * Request that the `HTML5` Tech exit fullscreen.
  17004. */
  17005. Html5.prototype.exitFullScreen = function exitFullScreen() {
  17006. this.el_.webkitExitFullScreen();
  17007. };
  17008. /**
  17009. * A getter/setter for the `Html5` Tech's source object.
  17010. * > Note: Please use {@link Html5#setSource}
  17011. *
  17012. * @param {Tech~SourceObject} [src]
  17013. * The source object you want to set on the `HTML5` techs element.
  17014. *
  17015. * @return {Tech~SourceObject|undefined}
  17016. * - The current source object when a source is not passed in.
  17017. * - undefined when setting
  17018. *
  17019. * @deprecated Since version 5.
  17020. */
  17021. Html5.prototype.src = function src(_src) {
  17022. if (_src === undefined) {
  17023. return this.el_.src;
  17024. }
  17025. // Setting src through `src` instead of `setSrc` will be deprecated
  17026. this.setSrc(_src);
  17027. };
  17028. /**
  17029. * Reset the tech by removing all sources and then calling
  17030. * {@link Html5.resetMediaElement}.
  17031. */
  17032. Html5.prototype.reset = function reset() {
  17033. Html5.resetMediaElement(this.el_);
  17034. };
  17035. /**
  17036. * Get the current source on the `HTML5` Tech. Falls back to returning the source from
  17037. * the HTML5 media element.
  17038. *
  17039. * @return {Tech~SourceObject}
  17040. * The current source object from the HTML5 tech. With a fallback to the
  17041. * elements source.
  17042. */
  17043. Html5.prototype.currentSrc = function currentSrc() {
  17044. if (this.currentSource_) {
  17045. return this.currentSource_.src;
  17046. }
  17047. return this.el_.currentSrc;
  17048. };
  17049. /**
  17050. * Set controls attribute for the HTML5 media Element.
  17051. *
  17052. * @param {string} val
  17053. * Value to set the controls attribute to
  17054. */
  17055. Html5.prototype.setControls = function setControls(val) {
  17056. this.el_.controls = !!val;
  17057. };
  17058. /**
  17059. * Create and returns a remote {@link TextTrack} object.
  17060. *
  17061. * @param {string} kind
  17062. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  17063. *
  17064. * @param {string} [label]
  17065. * Label to identify the text track
  17066. *
  17067. * @param {string} [language]
  17068. * Two letter language abbreviation
  17069. *
  17070. * @return {TextTrack}
  17071. * The TextTrack that gets created.
  17072. */
  17073. Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  17074. if (!this.featuresNativeTextTracks) {
  17075. return _Tech.prototype.addTextTrack.call(this, kind, label, language);
  17076. }
  17077. return this.el_.addTextTrack(kind, label, language);
  17078. };
  17079. /**
  17080. * Creates either native TextTrack or an emulated TextTrack depending
  17081. * on the value of `featuresNativeTextTracks`
  17082. *
  17083. * @param {Object} options
  17084. * The object should contain the options to intialize the TextTrack with.
  17085. *
  17086. * @param {string} [options.kind]
  17087. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
  17088. *
  17089. * @param {string} [options.label].
  17090. * Label to identify the text track
  17091. *
  17092. * @param {string} [options.language]
  17093. * Two letter language abbreviation.
  17094. *
  17095. * @param {boolean} [options.default]
  17096. * Default this track to on.
  17097. *
  17098. * @param {string} [options.id]
  17099. * The internal id to assign this track.
  17100. *
  17101. * @param {string} [options.src]
  17102. * A source url for the track.
  17103. *
  17104. * @return {HTMLTrackElement}
  17105. * The track element that gets created.
  17106. */
  17107. Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
  17108. if (!this.featuresNativeTextTracks) {
  17109. return _Tech.prototype.createRemoteTextTrack.call(this, options);
  17110. }
  17111. var htmlTrackElement = document_1.createElement('track');
  17112. if (options.kind) {
  17113. htmlTrackElement.kind = options.kind;
  17114. }
  17115. if (options.label) {
  17116. htmlTrackElement.label = options.label;
  17117. }
  17118. if (options.language || options.srclang) {
  17119. htmlTrackElement.srclang = options.language || options.srclang;
  17120. }
  17121. if (options['default']) {
  17122. htmlTrackElement['default'] = options['default'];
  17123. }
  17124. if (options.id) {
  17125. htmlTrackElement.id = options.id;
  17126. }
  17127. if (options.src) {
  17128. htmlTrackElement.src = options.src;
  17129. }
  17130. return htmlTrackElement;
  17131. };
  17132. /**
  17133. * Creates a remote text track object and returns an html track element.
  17134. *
  17135. * @param {Object} options The object should contain values for
  17136. * kind, language, label, and src (location of the WebVTT file)
  17137. * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
  17138. * automatically removed from the video element whenever the source changes
  17139. * @return {HTMLTrackElement} An Html Track Element.
  17140. * This can be an emulated {@link HTMLTrackElement} or a native one.
  17141. * @deprecated The default value of the "manualCleanup" parameter will default
  17142. * to "false" in upcoming versions of Video.js
  17143. */
  17144. Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
  17145. var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
  17146. if (this.featuresNativeTextTracks) {
  17147. this.el().appendChild(htmlTrackElement);
  17148. }
  17149. return htmlTrackElement;
  17150. };
  17151. /**
  17152. * Remove remote `TextTrack` from `TextTrackList` object
  17153. *
  17154. * @param {TextTrack} track
  17155. * `TextTrack` object to remove
  17156. */
  17157. Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
  17158. _Tech.prototype.removeRemoteTextTrack.call(this, track);
  17159. if (this.featuresNativeTextTracks) {
  17160. var tracks = this.$$('track');
  17161. var i = tracks.length;
  17162. while (i--) {
  17163. if (track === tracks[i] || track === tracks[i].track) {
  17164. this.el().removeChild(tracks[i]);
  17165. }
  17166. }
  17167. }
  17168. };
  17169. /**
  17170. * Gets available media playback quality metrics as specified by the W3C's Media
  17171. * Playback Quality API.
  17172. *
  17173. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  17174. *
  17175. * @return {Object}
  17176. * An object with supported media playback quality metrics
  17177. */
  17178. Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  17179. if (typeof this.el().getVideoPlaybackQuality === 'function') {
  17180. return this.el().getVideoPlaybackQuality();
  17181. }
  17182. var videoPlaybackQuality = {};
  17183. if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
  17184. videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
  17185. videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
  17186. }
  17187. if (window_1.performance && typeof window_1.performance.now === 'function') {
  17188. videoPlaybackQuality.creationTime = window_1.performance.now();
  17189. } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') {
  17190. videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart;
  17191. }
  17192. return videoPlaybackQuality;
  17193. };
  17194. return Html5;
  17195. }(Tech);
  17196. /* HTML5 Support Testing ---------------------------------------------------- */
  17197. if (isReal()) {
  17198. /**
  17199. * Element for testing browser HTML5 media capabilities
  17200. *
  17201. * @type {Element}
  17202. * @constant
  17203. * @private
  17204. */
  17205. Html5.TEST_VID = document_1.createElement('video');
  17206. var track = document_1.createElement('track');
  17207. track.kind = 'captions';
  17208. track.srclang = 'en';
  17209. track.label = 'English';
  17210. Html5.TEST_VID.appendChild(track);
  17211. }
  17212. /**
  17213. * Check if HTML5 media is supported by this browser/device.
  17214. *
  17215. * @return {boolean}
  17216. * - True if HTML5 media is supported.
  17217. * - False if HTML5 media is not supported.
  17218. */
  17219. Html5.isSupported = function () {
  17220. // IE9 with no Media Player is a LIAR! (#984)
  17221. try {
  17222. Html5.TEST_VID.volume = 0.5;
  17223. } catch (e) {
  17224. return false;
  17225. }
  17226. return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
  17227. };
  17228. /**
  17229. * Check if the tech can support the given type
  17230. *
  17231. * @param {string} type
  17232. * The mimetype to check
  17233. * @return {string} 'probably', 'maybe', or '' (empty string)
  17234. */
  17235. Html5.canPlayType = function (type) {
  17236. return Html5.TEST_VID.canPlayType(type);
  17237. };
  17238. /**
  17239. * Check if the tech can support the given source
  17240. * @param {Object} srcObj
  17241. * The source object
  17242. * @param {Object} options
  17243. * The options passed to the tech
  17244. * @return {string} 'probably', 'maybe', or '' (empty string)
  17245. */
  17246. Html5.canPlaySource = function (srcObj, options) {
  17247. return Html5.canPlayType(srcObj.type);
  17248. };
  17249. /**
  17250. * Check if the volume can be changed in this browser/device.
  17251. * Volume cannot be changed in a lot of mobile devices.
  17252. * Specifically, it can't be changed from 1 on iOS.
  17253. *
  17254. * @return {boolean}
  17255. * - True if volume can be controlled
  17256. * - False otherwise
  17257. */
  17258. Html5.canControlVolume = function () {
  17259. // IE will error if Windows Media Player not installed #3315
  17260. try {
  17261. var volume = Html5.TEST_VID.volume;
  17262. Html5.TEST_VID.volume = volume / 2 + 0.1;
  17263. return volume !== Html5.TEST_VID.volume;
  17264. } catch (e) {
  17265. return false;
  17266. }
  17267. };
  17268. /**
  17269. * Check if the volume can be muted in this browser/device.
  17270. * Some devices, e.g. iOS, don't allow changing volume
  17271. * but permits muting/unmuting.
  17272. *
  17273. * @return {bolean}
  17274. * - True if volume can be muted
  17275. * - False otherwise
  17276. */
  17277. Html5.canMuteVolume = function () {
  17278. try {
  17279. var muted = Html5.TEST_VID.muted;
  17280. // in some versions of iOS muted property doesn't always
  17281. // work, so we want to set both property and attribute
  17282. Html5.TEST_VID.muted = !muted;
  17283. if (Html5.TEST_VID.muted) {
  17284. setAttribute(Html5.TEST_VID, 'muted', 'muted');
  17285. } else {
  17286. removeAttribute(Html5.TEST_VID, 'muted', 'muted');
  17287. }
  17288. return muted !== Html5.TEST_VID.muted;
  17289. } catch (e) {
  17290. return false;
  17291. }
  17292. };
  17293. /**
  17294. * Check if the playback rate can be changed in this browser/device.
  17295. *
  17296. * @return {boolean}
  17297. * - True if playback rate can be controlled
  17298. * - False otherwise
  17299. */
  17300. Html5.canControlPlaybackRate = function () {
  17301. // Playback rate API is implemented in Android Chrome, but doesn't do anything
  17302. // https://github.com/videojs/video.js/issues/3180
  17303. if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
  17304. return false;
  17305. }
  17306. // IE will error if Windows Media Player not installed #3315
  17307. try {
  17308. var playbackRate = Html5.TEST_VID.playbackRate;
  17309. Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
  17310. return playbackRate !== Html5.TEST_VID.playbackRate;
  17311. } catch (e) {
  17312. return false;
  17313. }
  17314. };
  17315. /**
  17316. * Check if we can override a video/audio elements attributes, with
  17317. * Object.defineProperty.
  17318. *
  17319. * @return {boolean}
  17320. * - True if builtin attributes can be overriden
  17321. * - False otherwise
  17322. */
  17323. Html5.canOverrideAttributes = function () {
  17324. if (IS_IE8) {
  17325. return false;
  17326. }
  17327. // if we cannot overwrite the src/innerHTML property, there is no support
  17328. // iOS 7 safari for instance cannot do this.
  17329. try {
  17330. var noop = function noop() {};
  17331. Object.defineProperty(document_1.createElement('video'), 'src', { get: noop, set: noop });
  17332. Object.defineProperty(document_1.createElement('audio'), 'src', { get: noop, set: noop });
  17333. Object.defineProperty(document_1.createElement('video'), 'innerHTML', { get: noop, set: noop });
  17334. Object.defineProperty(document_1.createElement('audio'), 'innerHTML', { get: noop, set: noop });
  17335. } catch (e) {
  17336. return false;
  17337. }
  17338. return true;
  17339. };
  17340. /**
  17341. * Check to see if native `TextTrack`s are supported by this browser/device.
  17342. *
  17343. * @return {boolean}
  17344. * - True if native `TextTrack`s are supported.
  17345. * - False otherwise
  17346. */
  17347. Html5.supportsNativeTextTracks = function () {
  17348. return IS_ANY_SAFARI || IS_IOS && IS_CHROME;
  17349. };
  17350. /**
  17351. * Check to see if native `VideoTrack`s are supported by this browser/device
  17352. *
  17353. * @return {boolean}
  17354. * - True if native `VideoTrack`s are supported.
  17355. * - False otherwise
  17356. */
  17357. Html5.supportsNativeVideoTracks = function () {
  17358. return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
  17359. };
  17360. /**
  17361. * Check to see if native `AudioTrack`s are supported by this browser/device
  17362. *
  17363. * @return {boolean}
  17364. * - True if native `AudioTrack`s are supported.
  17365. * - False otherwise
  17366. */
  17367. Html5.supportsNativeAudioTracks = function () {
  17368. return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
  17369. };
  17370. /**
  17371. * An array of events available on the Html5 tech.
  17372. *
  17373. * @private
  17374. * @type {Array}
  17375. */
  17376. Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange'];
  17377. /**
  17378. * Boolean indicating whether the `Tech` supports volume control.
  17379. *
  17380. * @type {boolean}
  17381. * @default {@link Html5.canControlVolume}
  17382. */
  17383. Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
  17384. /**
  17385. * Boolean indicating whether the `Tech` supports muting volume.
  17386. *
  17387. * @type {bolean}
  17388. * @default {@link Html5.canMuteVolume}
  17389. */
  17390. Html5.prototype.featuresMuteControl = Html5.canMuteVolume();
  17391. /**
  17392. * Boolean indicating whether the `Tech` supports changing the speed at which the media
  17393. * plays. Examples:
  17394. * - Set player to play 2x (twice) as fast
  17395. * - Set player to play 0.5x (half) as fast
  17396. *
  17397. * @type {boolean}
  17398. * @default {@link Html5.canControlPlaybackRate}
  17399. */
  17400. Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
  17401. /**
  17402. * Boolean indicating wether the `Tech` supports the `sourceset` event.
  17403. *
  17404. * @type {boolean}
  17405. * @default
  17406. */
  17407. Html5.prototype.featuresSourceset = Html5.canOverrideAttributes();
  17408. /**
  17409. * Boolean indicating whether the `HTML5` tech currently supports the media element
  17410. * moving in the DOM. iOS breaks if you move the media element, so this is set this to
  17411. * false there. Everywhere else this should be true.
  17412. *
  17413. * @type {boolean}
  17414. * @default
  17415. */
  17416. Html5.prototype.movingMediaElementInDOM = !IS_IOS;
  17417. // TODO: Previous comment: No longer appears to be used. Can probably be removed.
  17418. // Is this true?
  17419. /**
  17420. * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
  17421. * when going into fullscreen.
  17422. *
  17423. * @type {boolean}
  17424. * @default
  17425. */
  17426. Html5.prototype.featuresFullscreenResize = true;
  17427. /**
  17428. * Boolean indicating whether the `HTML5` tech currently supports the progress event.
  17429. * If this is false, manual `progress` events will be triggred instead.
  17430. *
  17431. * @type {boolean}
  17432. * @default
  17433. */
  17434. Html5.prototype.featuresProgressEvents = true;
  17435. /**
  17436. * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
  17437. * If this is false, manual `timeupdate` events will be triggred instead.
  17438. *
  17439. * @default
  17440. */
  17441. Html5.prototype.featuresTimeupdateEvents = true;
  17442. /**
  17443. * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
  17444. *
  17445. * @type {boolean}
  17446. * @default {@link Html5.supportsNativeTextTracks}
  17447. */
  17448. Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
  17449. /**
  17450. * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
  17451. *
  17452. * @type {boolean}
  17453. * @default {@link Html5.supportsNativeVideoTracks}
  17454. */
  17455. Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
  17456. /**
  17457. * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
  17458. *
  17459. * @type {boolean}
  17460. * @default {@link Html5.supportsNativeAudioTracks}
  17461. */
  17462. Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
  17463. // HTML5 Feature detection and Device Fixes --------------------------------- //
  17464. var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
  17465. var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
  17466. var mp4RE = /^video\/mp4/i;
  17467. Html5.patchCanPlayType = function () {
  17468. // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
  17469. // Firefox and Chrome report correctly
  17470. if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX && !IS_CHROME) {
  17471. Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
  17472. if (type && mpegurlRE.test(type)) {
  17473. return 'maybe';
  17474. }
  17475. return canPlayType.call(this, type);
  17476. };
  17477. // Override Android 2.2 and less canPlayType method which is broken
  17478. } else if (IS_OLD_ANDROID) {
  17479. Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
  17480. if (type && mp4RE.test(type)) {
  17481. return 'maybe';
  17482. }
  17483. return canPlayType.call(this, type);
  17484. };
  17485. }
  17486. };
  17487. Html5.unpatchCanPlayType = function () {
  17488. var r = Html5.TEST_VID.constructor.prototype.canPlayType;
  17489. Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
  17490. return r;
  17491. };
  17492. // by default, patch the media element
  17493. Html5.patchCanPlayType();
  17494. Html5.disposeMediaElement = function (el) {
  17495. if (!el) {
  17496. return;
  17497. }
  17498. if (el.parentNode) {
  17499. el.parentNode.removeChild(el);
  17500. }
  17501. // remove any child track or source nodes to prevent their loading
  17502. while (el.hasChildNodes()) {
  17503. el.removeChild(el.firstChild);
  17504. }
  17505. // remove any src reference. not setting `src=''` because that causes a warning
  17506. // in firefox
  17507. el.removeAttribute('src');
  17508. // force the media element to update its loading state by calling load()
  17509. // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
  17510. if (typeof el.load === 'function') {
  17511. // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
  17512. (function () {
  17513. try {
  17514. el.load();
  17515. } catch (e) {
  17516. // not supported
  17517. }
  17518. })();
  17519. }
  17520. };
  17521. Html5.resetMediaElement = function (el) {
  17522. if (!el) {
  17523. return;
  17524. }
  17525. var sources = el.querySelectorAll('source');
  17526. var i = sources.length;
  17527. while (i--) {
  17528. el.removeChild(sources[i]);
  17529. }
  17530. // remove any src reference.
  17531. // not setting `src=''` because that throws an error
  17532. el.removeAttribute('src');
  17533. if (typeof el.load === 'function') {
  17534. // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
  17535. (function () {
  17536. try {
  17537. el.load();
  17538. } catch (e) {
  17539. // satisfy linter
  17540. }
  17541. })();
  17542. }
  17543. };
  17544. /* Native HTML5 element property wrapping ----------------------------------- */
  17545. // Wrap native boolean attributes with getters that check both property and attribute
  17546. // The list is as followed:
  17547. // muted, defaultMuted, autoplay, controls, loop, playsinline
  17548. [
  17549. /**
  17550. * Get the value of `muted` from the media element. `muted` indicates
  17551. * that the volume for the media should be set to silent. This does not actually change
  17552. * the `volume` attribute.
  17553. *
  17554. * @method Html5#muted
  17555. * @return {boolean}
  17556. * - True if the value of `volume` should be ignored and the audio set to silent.
  17557. * - False if the value of `volume` should be used.
  17558. *
  17559. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
  17560. */
  17561. 'muted',
  17562. /**
  17563. * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
  17564. * whether the media should start muted or not. Only changes the default state of the
  17565. * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
  17566. * current state.
  17567. *
  17568. * @method Html5#defaultMuted
  17569. * @return {boolean}
  17570. * - The value of `defaultMuted` from the media element.
  17571. * - True indicates that the media should start muted.
  17572. * - False indicates that the media should not start muted
  17573. *
  17574. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
  17575. */
  17576. 'defaultMuted',
  17577. /**
  17578. * Get the value of `autoplay` from the media element. `autoplay` indicates
  17579. * that the media should start to play as soon as the page is ready.
  17580. *
  17581. * @method Html5#autoplay
  17582. * @return {boolean}
  17583. * - The value of `autoplay` from the media element.
  17584. * - True indicates that the media should start as soon as the page loads.
  17585. * - False indicates that the media should not start as soon as the page loads.
  17586. *
  17587. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
  17588. */
  17589. 'autoplay',
  17590. /**
  17591. * Get the value of `controls` from the media element. `controls` indicates
  17592. * whether the native media controls should be shown or hidden.
  17593. *
  17594. * @method Html5#controls
  17595. * @return {boolean}
  17596. * - The value of `controls` from the media element.
  17597. * - True indicates that native controls should be showing.
  17598. * - False indicates that native controls should be hidden.
  17599. *
  17600. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
  17601. */
  17602. 'controls',
  17603. /**
  17604. * Get the value of `loop` from the media element. `loop` indicates
  17605. * that the media should return to the start of the media and continue playing once
  17606. * it reaches the end.
  17607. *
  17608. * @method Html5#loop
  17609. * @return {boolean}
  17610. * - The value of `loop` from the media element.
  17611. * - True indicates that playback should seek back to start once
  17612. * the end of a media is reached.
  17613. * - False indicates that playback should not loop back to the start when the
  17614. * end of the media is reached.
  17615. *
  17616. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
  17617. */
  17618. 'loop',
  17619. /**
  17620. * Get the value of `playsinline` from the media element. `playsinline` indicates
  17621. * to the browser that non-fullscreen playback is preferred when fullscreen
  17622. * playback is the native default, such as in iOS Safari.
  17623. *
  17624. * @method Html5#playsinline
  17625. * @return {boolean}
  17626. * - The value of `playsinline` from the media element.
  17627. * - True indicates that the media should play inline.
  17628. * - False indicates that the media should not play inline.
  17629. *
  17630. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  17631. */
  17632. 'playsinline'].forEach(function (prop) {
  17633. Html5.prototype[prop] = function () {
  17634. return this.el_[prop] || this.el_.hasAttribute(prop);
  17635. };
  17636. });
  17637. // Wrap native boolean attributes with setters that set both property and attribute
  17638. // The list is as followed:
  17639. // setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
  17640. // setControls is special-cased above
  17641. [
  17642. /**
  17643. * Set the value of `muted` on the media element. `muted` indicates that the current
  17644. * audio level should be silent.
  17645. *
  17646. * @method Html5#setMuted
  17647. * @param {boolean} muted
  17648. * - True if the audio should be set to silent
  17649. * - False otherwise
  17650. *
  17651. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
  17652. */
  17653. 'muted',
  17654. /**
  17655. * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
  17656. * audio level should be silent, but will only effect the muted level on intial playback..
  17657. *
  17658. * @method Html5.prototype.setDefaultMuted
  17659. * @param {boolean} defaultMuted
  17660. * - True if the audio should be set to silent
  17661. * - False otherwise
  17662. *
  17663. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
  17664. */
  17665. 'defaultMuted',
  17666. /**
  17667. * Set the value of `autoplay` on the media element. `autoplay` indicates
  17668. * that the media should start to play as soon as the page is ready.
  17669. *
  17670. * @method Html5#setAutoplay
  17671. * @param {boolean} autoplay
  17672. * - True indicates that the media should start as soon as the page loads.
  17673. * - False indicates that the media should not start as soon as the page loads.
  17674. *
  17675. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
  17676. */
  17677. 'autoplay',
  17678. /**
  17679. * Set the value of `loop` on the media element. `loop` indicates
  17680. * that the media should return to the start of the media and continue playing once
  17681. * it reaches the end.
  17682. *
  17683. * @method Html5#setLoop
  17684. * @param {boolean} loop
  17685. * - True indicates that playback should seek back to start once
  17686. * the end of a media is reached.
  17687. * - False indicates that playback should not loop back to the start when the
  17688. * end of the media is reached.
  17689. *
  17690. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
  17691. */
  17692. 'loop',
  17693. /**
  17694. * Set the value of `playsinline` from the media element. `playsinline` indicates
  17695. * to the browser that non-fullscreen playback is preferred when fullscreen
  17696. * playback is the native default, such as in iOS Safari.
  17697. *
  17698. * @method Html5#setPlaysinline
  17699. * @param {boolean} playsinline
  17700. * - True indicates that the media should play inline.
  17701. * - False indicates that the media should not play inline.
  17702. *
  17703. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  17704. */
  17705. 'playsinline'].forEach(function (prop) {
  17706. Html5.prototype['set' + toTitleCase(prop)] = function (v) {
  17707. this.el_[prop] = v;
  17708. if (v) {
  17709. this.el_.setAttribute(prop, prop);
  17710. } else {
  17711. this.el_.removeAttribute(prop);
  17712. }
  17713. };
  17714. });
  17715. // Wrap native properties with a getter
  17716. // The list is as followed
  17717. // paused, currentTime, buffered, volume, poster, preload, error, seeking
  17718. // seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
  17719. // readyState, videoWidth, videoHeight
  17720. [
  17721. /**
  17722. * Get the value of `paused` from the media element. `paused` indicates whether the media element
  17723. * is currently paused or not.
  17724. *
  17725. * @method Html5#paused
  17726. * @return {boolean}
  17727. * The value of `paused` from the media element.
  17728. *
  17729. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
  17730. */
  17731. 'paused',
  17732. /**
  17733. * Get the value of `currentTime` from the media element. `currentTime` indicates
  17734. * the current second that the media is at in playback.
  17735. *
  17736. * @method Html5#currentTime
  17737. * @return {number}
  17738. * The value of `currentTime` from the media element.
  17739. *
  17740. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
  17741. */
  17742. 'currentTime',
  17743. /**
  17744. * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
  17745. * object that represents the parts of the media that are already downloaded and
  17746. * available for playback.
  17747. *
  17748. * @method Html5#buffered
  17749. * @return {TimeRange}
  17750. * The value of `buffered` from the media element.
  17751. *
  17752. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
  17753. */
  17754. 'buffered',
  17755. /**
  17756. * Get the value of `volume` from the media element. `volume` indicates
  17757. * the current playback volume of audio for a media. `volume` will be a value from 0
  17758. * (silent) to 1 (loudest and default).
  17759. *
  17760. * @method Html5#volume
  17761. * @return {number}
  17762. * The value of `volume` from the media element. Value will be between 0-1.
  17763. *
  17764. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
  17765. */
  17766. 'volume',
  17767. /**
  17768. * Get the value of `poster` from the media element. `poster` indicates
  17769. * that the url of an image file that can/will be shown when no media data is available.
  17770. *
  17771. * @method Html5#poster
  17772. * @return {string}
  17773. * The value of `poster` from the media element. Value will be a url to an
  17774. * image.
  17775. *
  17776. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
  17777. */
  17778. 'poster',
  17779. /**
  17780. * Get the value of `preload` from the media element. `preload` indicates
  17781. * what should download before the media is interacted with. It can have the following
  17782. * values:
  17783. * - none: nothing should be downloaded
  17784. * - metadata: poster and the first few frames of the media may be downloaded to get
  17785. * media dimensions and other metadata
  17786. * - auto: allow the media and metadata for the media to be downloaded before
  17787. * interaction
  17788. *
  17789. * @method Html5#preload
  17790. * @return {string}
  17791. * The value of `preload` from the media element. Will be 'none', 'metadata',
  17792. * or 'auto'.
  17793. *
  17794. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
  17795. */
  17796. 'preload',
  17797. /**
  17798. * Get the value of the `error` from the media element. `error` indicates any
  17799. * MediaError that may have occured during playback. If error returns null there is no
  17800. * current error.
  17801. *
  17802. * @method Html5#error
  17803. * @return {MediaError|null}
  17804. * The value of `error` from the media element. Will be `MediaError` if there
  17805. * is a current error and null otherwise.
  17806. *
  17807. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
  17808. */
  17809. 'error',
  17810. /**
  17811. * Get the value of `seeking` from the media element. `seeking` indicates whether the
  17812. * media is currently seeking to a new position or not.
  17813. *
  17814. * @method Html5#seeking
  17815. * @return {boolean}
  17816. * - The value of `seeking` from the media element.
  17817. * - True indicates that the media is currently seeking to a new position.
  17818. * - Flase indicates that the media is not seeking to a new position at this time.
  17819. *
  17820. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
  17821. */
  17822. 'seeking',
  17823. /**
  17824. * Get the value of `seekable` from the media element. `seekable` returns a
  17825. * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
  17826. *
  17827. * @method Html5#seekable
  17828. * @return {TimeRange}
  17829. * The value of `seekable` from the media element. A `TimeRange` object
  17830. * indicating the current ranges of time that can be seeked to.
  17831. *
  17832. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
  17833. */
  17834. 'seekable',
  17835. /**
  17836. * Get the value of `ended` from the media element. `ended` indicates whether
  17837. * the media has reached the end or not.
  17838. *
  17839. * @method Html5#ended
  17840. * @return {boolean}
  17841. * - The value of `ended` from the media element.
  17842. * - True indicates that the media has ended.
  17843. * - False indicates that the media has not ended.
  17844. *
  17845. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
  17846. */
  17847. 'ended',
  17848. /**
  17849. * Get the value of `playbackRate` from the media element. `playbackRate` indicates
  17850. * the rate at which the media is currently playing back. Examples:
  17851. * - if playbackRate is set to 2, media will play twice as fast.
  17852. * - if playbackRate is set to 0.5, media will play half as fast.
  17853. *
  17854. * @method Html5#playbackRate
  17855. * @return {number}
  17856. * The value of `playbackRate` from the media element. A number indicating
  17857. * the current playback speed of the media, where 1 is normal speed.
  17858. *
  17859. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  17860. */
  17861. 'playbackRate',
  17862. /**
  17863. * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
  17864. * the rate at which the media is currently playing back. This value will not indicate the current
  17865. * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
  17866. *
  17867. * Examples:
  17868. * - if defaultPlaybackRate is set to 2, media will play twice as fast.
  17869. * - if defaultPlaybackRate is set to 0.5, media will play half as fast.
  17870. *
  17871. * @method Html5.prototype.defaultPlaybackRate
  17872. * @return {number}
  17873. * The value of `defaultPlaybackRate` from the media element. A number indicating
  17874. * the current playback speed of the media, where 1 is normal speed.
  17875. *
  17876. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  17877. */
  17878. 'defaultPlaybackRate',
  17879. /**
  17880. * Get the value of `played` from the media element. `played` returns a `TimeRange`
  17881. * object representing points in the media timeline that have been played.
  17882. *
  17883. * @method Html5#played
  17884. * @return {TimeRange}
  17885. * The value of `played` from the media element. A `TimeRange` object indicating
  17886. * the ranges of time that have been played.
  17887. *
  17888. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
  17889. */
  17890. 'played',
  17891. /**
  17892. * Get the value of `networkState` from the media element. `networkState` indicates
  17893. * the current network state. It returns an enumeration from the following list:
  17894. * - 0: NETWORK_EMPTY
  17895. * - 1: NEWORK_IDLE
  17896. * - 2: NETWORK_LOADING
  17897. * - 3: NETWORK_NO_SOURCE
  17898. *
  17899. * @method Html5#networkState
  17900. * @return {number}
  17901. * The value of `networkState` from the media element. This will be a number
  17902. * from the list in the description.
  17903. *
  17904. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
  17905. */
  17906. 'networkState',
  17907. /**
  17908. * Get the value of `readyState` from the media element. `readyState` indicates
  17909. * the current state of the media element. It returns an enumeration from the
  17910. * following list:
  17911. * - 0: HAVE_NOTHING
  17912. * - 1: HAVE_METADATA
  17913. * - 2: HAVE_CURRENT_DATA
  17914. * - 3: HAVE_FUTURE_DATA
  17915. * - 4: HAVE_ENOUGH_DATA
  17916. *
  17917. * @method Html5#readyState
  17918. * @return {number}
  17919. * The value of `readyState` from the media element. This will be a number
  17920. * from the list in the description.
  17921. *
  17922. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
  17923. */
  17924. 'readyState',
  17925. /**
  17926. * Get the value of `videoWidth` from the video element. `videoWidth` indicates
  17927. * the current width of the video in css pixels.
  17928. *
  17929. * @method Html5#videoWidth
  17930. * @return {number}
  17931. * The value of `videoWidth` from the video element. This will be a number
  17932. * in css pixels.
  17933. *
  17934. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
  17935. */
  17936. 'videoWidth',
  17937. /**
  17938. * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
  17939. * the current height of the video in css pixels.
  17940. *
  17941. * @method Html5#videoHeight
  17942. * @return {number}
  17943. * The value of `videoHeight` from the video element. This will be a number
  17944. * in css pixels.
  17945. *
  17946. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
  17947. */
  17948. 'videoHeight'].forEach(function (prop) {
  17949. Html5.prototype[prop] = function () {
  17950. return this.el_[prop];
  17951. };
  17952. });
  17953. // Wrap native properties with a setter in this format:
  17954. // set + toTitleCase(name)
  17955. // The list is as follows:
  17956. // setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
  17957. [
  17958. /**
  17959. * Set the value of `volume` on the media element. `volume` indicates the current
  17960. * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
  17961. * so on.
  17962. *
  17963. * @method Html5#setVolume
  17964. * @param {number} percentAsDecimal
  17965. * The volume percent as a decimal. Valid range is from 0-1.
  17966. *
  17967. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
  17968. */
  17969. 'volume',
  17970. /**
  17971. * Set the value of `src` on the media element. `src` indicates the current
  17972. * {@link Tech~SourceObject} for the media.
  17973. *
  17974. * @method Html5#setSrc
  17975. * @param {Tech~SourceObject} src
  17976. * The source object to set as the current source.
  17977. *
  17978. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
  17979. */
  17980. 'src',
  17981. /**
  17982. * Set the value of `poster` on the media element. `poster` is the url to
  17983. * an image file that can/will be shown when no media data is available.
  17984. *
  17985. * @method Html5#setPoster
  17986. * @param {string} poster
  17987. * The url to an image that should be used as the `poster` for the media
  17988. * element.
  17989. *
  17990. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
  17991. */
  17992. 'poster',
  17993. /**
  17994. * Set the value of `preload` on the media element. `preload` indicates
  17995. * what should download before the media is interacted with. It can have the following
  17996. * values:
  17997. * - none: nothing should be downloaded
  17998. * - metadata: poster and the first few frames of the media may be downloaded to get
  17999. * media dimensions and other metadata
  18000. * - auto: allow the media and metadata for the media to be downloaded before
  18001. * interaction
  18002. *
  18003. * @method Html5#setPreload
  18004. * @param {string} preload
  18005. * The value of `preload` to set on the media element. Must be 'none', 'metadata',
  18006. * or 'auto'.
  18007. *
  18008. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
  18009. */
  18010. 'preload',
  18011. /**
  18012. * Set the value of `playbackRate` on the media element. `playbackRate` indicates
  18013. * the rate at which the media should play back. Examples:
  18014. * - if playbackRate is set to 2, media will play twice as fast.
  18015. * - if playbackRate is set to 0.5, media will play half as fast.
  18016. *
  18017. * @method Html5#setPlaybackRate
  18018. * @return {number}
  18019. * The value of `playbackRate` from the media element. A number indicating
  18020. * the current playback speed of the media, where 1 is normal speed.
  18021. *
  18022. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  18023. */
  18024. 'playbackRate',
  18025. /**
  18026. * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
  18027. * the rate at which the media should play back upon initial startup. Changing this value
  18028. * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
  18029. *
  18030. * Example Values:
  18031. * - if playbackRate is set to 2, media will play twice as fast.
  18032. * - if playbackRate is set to 0.5, media will play half as fast.
  18033. *
  18034. * @method Html5.prototype.setDefaultPlaybackRate
  18035. * @return {number}
  18036. * The value of `defaultPlaybackRate` from the media element. A number indicating
  18037. * the current playback speed of the media, where 1 is normal speed.
  18038. *
  18039. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
  18040. */
  18041. 'defaultPlaybackRate'].forEach(function (prop) {
  18042. Html5.prototype['set' + toTitleCase(prop)] = function (v) {
  18043. this.el_[prop] = v;
  18044. };
  18045. });
  18046. // wrap native functions with a function
  18047. // The list is as follows:
  18048. // pause, load play
  18049. [
  18050. /**
  18051. * A wrapper around the media elements `pause` function. This will call the `HTML5`
  18052. * media elements `pause` function.
  18053. *
  18054. * @method Html5#pause
  18055. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
  18056. */
  18057. 'pause',
  18058. /**
  18059. * A wrapper around the media elements `load` function. This will call the `HTML5`s
  18060. * media element `load` function.
  18061. *
  18062. * @method Html5#load
  18063. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
  18064. */
  18065. 'load',
  18066. /**
  18067. * A wrapper around the media elements `play` function. This will call the `HTML5`s
  18068. * media element `play` function.
  18069. *
  18070. * @method Html5#play
  18071. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
  18072. */
  18073. 'play'].forEach(function (prop) {
  18074. Html5.prototype[prop] = function () {
  18075. return this.el_[prop]();
  18076. };
  18077. });
  18078. Tech.withSourceHandlers(Html5);
  18079. /**
  18080. * Native source handler for Html5, simply passes the source to the media element.
  18081. *
  18082. * @proprety {Tech~SourceObject} source
  18083. * The source object
  18084. *
  18085. * @proprety {Html5} tech
  18086. * The instance of the HTML5 tech.
  18087. */
  18088. Html5.nativeSourceHandler = {};
  18089. /**
  18090. * Check if the media element can play the given mime type.
  18091. *
  18092. * @param {string} type
  18093. * The mimetype to check
  18094. *
  18095. * @return {string}
  18096. * 'probably', 'maybe', or '' (empty string)
  18097. */
  18098. Html5.nativeSourceHandler.canPlayType = function (type) {
  18099. // IE9 on Windows 7 without MediaPlayer throws an error here
  18100. // https://github.com/videojs/video.js/issues/519
  18101. try {
  18102. return Html5.TEST_VID.canPlayType(type);
  18103. } catch (e) {
  18104. return '';
  18105. }
  18106. };
  18107. /**
  18108. * Check if the media element can handle a source natively.
  18109. *
  18110. * @param {Tech~SourceObject} source
  18111. * The source object
  18112. *
  18113. * @param {Object} [options]
  18114. * Options to be passed to the tech.
  18115. *
  18116. * @return {string}
  18117. * 'probably', 'maybe', or '' (empty string).
  18118. */
  18119. Html5.nativeSourceHandler.canHandleSource = function (source, options) {
  18120. // If a type was provided we should rely on that
  18121. if (source.type) {
  18122. return Html5.nativeSourceHandler.canPlayType(source.type);
  18123. // If no type, fall back to checking 'video/[EXTENSION]'
  18124. } else if (source.src) {
  18125. var ext = getFileExtension(source.src);
  18126. return Html5.nativeSourceHandler.canPlayType('video/' + ext);
  18127. }
  18128. return '';
  18129. };
  18130. /**
  18131. * Pass the source to the native media element.
  18132. *
  18133. * @param {Tech~SourceObject} source
  18134. * The source object
  18135. *
  18136. * @param {Html5} tech
  18137. * The instance of the Html5 tech
  18138. *
  18139. * @param {Object} [options]
  18140. * The options to pass to the source
  18141. */
  18142. Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
  18143. tech.setSrc(source.src);
  18144. };
  18145. /**
  18146. * A noop for the native dispose function, as cleanup is not needed.
  18147. */
  18148. Html5.nativeSourceHandler.dispose = function () {};
  18149. // Register the native source handler
  18150. Html5.registerSourceHandler(Html5.nativeSourceHandler);
  18151. Tech.registerTech('Html5', Html5);
  18152. var _templateObject$1 = taggedTemplateLiteralLoose(['\n Using the tech directly can be dangerous. I hope you know what you\'re doing.\n See https://github.com/videojs/video.js/issues/2617 for more info.\n '], ['\n Using the tech directly can be dangerous. I hope you know what you\'re doing.\n See https://github.com/videojs/video.js/issues/2617 for more info.\n ']);
  18153. /**
  18154. * @file player.js
  18155. */
  18156. // Subclasses Component
  18157. // The following imports are used only to ensure that the corresponding modules
  18158. // are always included in the video.js package. Importing the modules will
  18159. // execute them and they will register themselves with video.js.
  18160. // Import Html5 tech, at least for disposing the original video tag.
  18161. // The following tech events are simply re-triggered
  18162. // on the player when they happen
  18163. var TECH_EVENTS_RETRIGGER = [
  18164. /**
  18165. * Fired while the user agent is downloading media data.
  18166. *
  18167. * @event Player#progress
  18168. * @type {EventTarget~Event}
  18169. */
  18170. /**
  18171. * Retrigger the `progress` event that was triggered by the {@link Tech}.
  18172. *
  18173. * @private
  18174. * @method Player#handleTechProgress_
  18175. * @fires Player#progress
  18176. * @listens Tech#progress
  18177. */
  18178. 'progress',
  18179. /**
  18180. * Fires when the loading of an audio/video is aborted.
  18181. *
  18182. * @event Player#abort
  18183. * @type {EventTarget~Event}
  18184. */
  18185. /**
  18186. * Retrigger the `abort` event that was triggered by the {@link Tech}.
  18187. *
  18188. * @private
  18189. * @method Player#handleTechAbort_
  18190. * @fires Player#abort
  18191. * @listens Tech#abort
  18192. */
  18193. 'abort',
  18194. /**
  18195. * Fires when the browser is intentionally not getting media data.
  18196. *
  18197. * @event Player#suspend
  18198. * @type {EventTarget~Event}
  18199. */
  18200. /**
  18201. * Retrigger the `suspend` event that was triggered by the {@link Tech}.
  18202. *
  18203. * @private
  18204. * @method Player#handleTechSuspend_
  18205. * @fires Player#suspend
  18206. * @listens Tech#suspend
  18207. */
  18208. 'suspend',
  18209. /**
  18210. * Fires when the current playlist is empty.
  18211. *
  18212. * @event Player#emptied
  18213. * @type {EventTarget~Event}
  18214. */
  18215. /**
  18216. * Retrigger the `emptied` event that was triggered by the {@link Tech}.
  18217. *
  18218. * @private
  18219. * @method Player#handleTechEmptied_
  18220. * @fires Player#emptied
  18221. * @listens Tech#emptied
  18222. */
  18223. 'emptied',
  18224. /**
  18225. * Fires when the browser is trying to get media data, but data is not available.
  18226. *
  18227. * @event Player#stalled
  18228. * @type {EventTarget~Event}
  18229. */
  18230. /**
  18231. * Retrigger the `stalled` event that was triggered by the {@link Tech}.
  18232. *
  18233. * @private
  18234. * @method Player#handleTechStalled_
  18235. * @fires Player#stalled
  18236. * @listens Tech#stalled
  18237. */
  18238. 'stalled',
  18239. /**
  18240. * Fires when the browser has loaded meta data for the audio/video.
  18241. *
  18242. * @event Player#loadedmetadata
  18243. * @type {EventTarget~Event}
  18244. */
  18245. /**
  18246. * Retrigger the `stalled` event that was triggered by the {@link Tech}.
  18247. *
  18248. * @private
  18249. * @method Player#handleTechLoadedmetadata_
  18250. * @fires Player#loadedmetadata
  18251. * @listens Tech#loadedmetadata
  18252. */
  18253. 'loadedmetadata',
  18254. /**
  18255. * Fires when the browser has loaded the current frame of the audio/video.
  18256. *
  18257. * @event Player#loadeddata
  18258. * @type {event}
  18259. */
  18260. /**
  18261. * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
  18262. *
  18263. * @private
  18264. * @method Player#handleTechLoaddeddata_
  18265. * @fires Player#loadeddata
  18266. * @listens Tech#loadeddata
  18267. */
  18268. 'loadeddata',
  18269. /**
  18270. * Fires when the current playback position has changed.
  18271. *
  18272. * @event Player#timeupdate
  18273. * @type {event}
  18274. */
  18275. /**
  18276. * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
  18277. *
  18278. * @private
  18279. * @method Player#handleTechTimeUpdate_
  18280. * @fires Player#timeupdate
  18281. * @listens Tech#timeupdate
  18282. */
  18283. 'timeupdate',
  18284. /**
  18285. * Fires when the video's intrinsic dimensions change
  18286. *
  18287. * @event Player#resize
  18288. * @type {event}
  18289. */
  18290. /**
  18291. * Retrigger the `resize` event that was triggered by the {@link Tech}.
  18292. *
  18293. * @private
  18294. * @method Player#handleTechResize_
  18295. * @fires Player#resize
  18296. * @listens Tech#resize
  18297. */
  18298. 'resize',
  18299. /**
  18300. * Fires when the volume has been changed
  18301. *
  18302. * @event Player#volumechange
  18303. * @type {event}
  18304. */
  18305. /**
  18306. * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
  18307. *
  18308. * @private
  18309. * @method Player#handleTechVolumechange_
  18310. * @fires Player#volumechange
  18311. * @listens Tech#volumechange
  18312. */
  18313. 'volumechange',
  18314. /**
  18315. * Fires when the text track has been changed
  18316. *
  18317. * @event Player#texttrackchange
  18318. * @type {event}
  18319. */
  18320. /**
  18321. * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
  18322. *
  18323. * @private
  18324. * @method Player#handleTechTexttrackchange_
  18325. * @fires Player#texttrackchange
  18326. * @listens Tech#texttrackchange
  18327. */
  18328. 'texttrackchange'];
  18329. // events to queue when playback rate is zero
  18330. // this is a hash for the sole purpose of mapping non-camel-cased event names
  18331. // to camel-cased function names
  18332. var TECH_EVENTS_QUEUE = {
  18333. canplay: 'CanPlay',
  18334. canplaythrough: 'CanPlayThrough',
  18335. playing: 'Playing',
  18336. seeked: 'Seeked'
  18337. };
  18338. var BREAKPOINT_ORDER = ['tiny', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'huge'];
  18339. var BREAKPOINT_CLASSES = {};
  18340. // grep: vjs-layout-tiny
  18341. // grep: vjs-layout-x-small
  18342. // grep: vjs-layout-small
  18343. // grep: vjs-layout-medium
  18344. // grep: vjs-layout-large
  18345. // grep: vjs-layout-x-large
  18346. // grep: vjs-layout-huge
  18347. BREAKPOINT_ORDER.forEach(function (k) {
  18348. var v = k.charAt(0) === 'x' ? 'x-' + k.substring(1) : k;
  18349. BREAKPOINT_CLASSES[k] = 'vjs-layout-' + v;
  18350. });
  18351. var DEFAULT_BREAKPOINTS = {
  18352. tiny: 210,
  18353. xsmall: 320,
  18354. small: 425,
  18355. medium: 768,
  18356. large: 1440,
  18357. xlarge: 2560,
  18358. huge: Infinity
  18359. };
  18360. /**
  18361. * An instance of the `Player` class is created when any of the Video.js setup methods
  18362. * are used to initialize a video.
  18363. *
  18364. * After an instance has been created it can be accessed globally in two ways:
  18365. * 1. By calling `videojs('example_video_1');`
  18366. * 2. By using it directly via `videojs.players.example_video_1;`
  18367. *
  18368. * @extends Component
  18369. */
  18370. var Player = function (_Component) {
  18371. inherits(Player, _Component);
  18372. /**
  18373. * Create an instance of this class.
  18374. *
  18375. * @param {Element} tag
  18376. * The original video DOM element used for configuring options.
  18377. *
  18378. * @param {Object} [options]
  18379. * Object of option names and values.
  18380. *
  18381. * @param {Component~ReadyCallback} [ready]
  18382. * Ready callback function.
  18383. */
  18384. function Player(tag, options, ready) {
  18385. classCallCheck(this, Player);
  18386. // Make sure tag ID exists
  18387. tag.id = tag.id || options.id || 'vjs_video_' + newGUID();
  18388. // Set Options
  18389. // The options argument overrides options set in the video tag
  18390. // which overrides globally set options.
  18391. // This latter part coincides with the load order
  18392. // (tag must exist before Player)
  18393. options = assign(Player.getTagSettings(tag), options);
  18394. // Delay the initialization of children because we need to set up
  18395. // player properties first, and can't use `this` before `super()`
  18396. options.initChildren = false;
  18397. // Same with creating the element
  18398. options.createEl = false;
  18399. // don't auto mixin the evented mixin
  18400. options.evented = false;
  18401. // we don't want the player to report touch activity on itself
  18402. // see enableTouchActivity in Component
  18403. options.reportTouchActivity = false;
  18404. // If language is not set, get the closest lang attribute
  18405. if (!options.language) {
  18406. if (typeof tag.closest === 'function') {
  18407. var closest = tag.closest('[lang]');
  18408. if (closest && closest.getAttribute) {
  18409. options.language = closest.getAttribute('lang');
  18410. }
  18411. } else {
  18412. var element = tag;
  18413. while (element && element.nodeType === 1) {
  18414. if (getAttributes(element).hasOwnProperty('lang')) {
  18415. options.language = element.getAttribute('lang');
  18416. break;
  18417. }
  18418. element = element.parentNode;
  18419. }
  18420. }
  18421. }
  18422. // Run base component initializing with new options
  18423. // create logger
  18424. var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
  18425. _this.log = createLogger(_this.id_);
  18426. // Tracks when a tech changes the poster
  18427. _this.isPosterFromTech_ = false;
  18428. // Holds callback info that gets queued when playback rate is zero
  18429. // and a seek is happening
  18430. _this.queuedCallbacks_ = [];
  18431. // Turn off API access because we're loading a new tech that might load asynchronously
  18432. _this.isReady_ = false;
  18433. // Init state hasStarted_
  18434. _this.hasStarted_ = false;
  18435. // Init state userActive_
  18436. _this.userActive_ = false;
  18437. // if the global option object was accidentally blown away by
  18438. // someone, bail early with an informative error
  18439. if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
  18440. throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
  18441. }
  18442. // Store the original tag used to set options
  18443. _this.tag = tag;
  18444. // Store the tag attributes used to restore html5 element
  18445. _this.tagAttributes = tag && getAttributes(tag);
  18446. // Update current language
  18447. _this.language(_this.options_.language);
  18448. // Update Supported Languages
  18449. if (options.languages) {
  18450. // Normalise player option languages to lowercase
  18451. var languagesToLower = {};
  18452. Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) {
  18453. languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1];
  18454. });
  18455. _this.languages_ = languagesToLower;
  18456. } else {
  18457. _this.languages_ = Player.prototype.options_.languages;
  18458. }
  18459. // Cache for video property values.
  18460. _this.cache_ = {};
  18461. // Set poster
  18462. _this.poster_ = options.poster || '';
  18463. // Set controls
  18464. _this.controls_ = !!options.controls;
  18465. // Set default values for lastVolume
  18466. _this.cache_.lastVolume = 1;
  18467. // Original tag settings stored in options
  18468. // now remove immediately so native controls don't flash.
  18469. // May be turned back on by HTML5 tech if nativeControlsForTouch is true
  18470. tag.controls = false;
  18471. tag.removeAttribute('controls');
  18472. // the attribute overrides the option
  18473. if (tag.hasAttribute('autoplay')) {
  18474. _this.options_.autoplay = true;
  18475. } else {
  18476. // otherwise use the setter to validate and
  18477. // set the correct value.
  18478. _this.autoplay(_this.options_.autoplay);
  18479. }
  18480. /*
  18481. * Store the internal state of scrubbing
  18482. *
  18483. * @private
  18484. * @return {Boolean} True if the user is scrubbing
  18485. */
  18486. _this.scrubbing_ = false;
  18487. _this.el_ = _this.createEl();
  18488. // Set default value for lastPlaybackRate
  18489. _this.cache_.lastPlaybackRate = _this.defaultPlaybackRate();
  18490. // Make this an evented object and use `el_` as its event bus.
  18491. evented(_this, { eventBusKey: 'el_' });
  18492. // We also want to pass the original player options to each component and plugin
  18493. // as well so they don't need to reach back into the player for options later.
  18494. // We also need to do another copy of this.options_ so we don't end up with
  18495. // an infinite loop.
  18496. var playerOptionsCopy = mergeOptions(_this.options_);
  18497. // Load plugins
  18498. if (options.plugins) {
  18499. var plugins = options.plugins;
  18500. Object.keys(plugins).forEach(function (name$$1) {
  18501. if (typeof this[name$$1] === 'function') {
  18502. this[name$$1](plugins[name$$1]);
  18503. } else {
  18504. throw new Error('plugin "' + name$$1 + '" does not exist');
  18505. }
  18506. }, _this);
  18507. }
  18508. _this.options_.playerOptions = playerOptionsCopy;
  18509. _this.middleware_ = [];
  18510. _this.initChildren();
  18511. // Set isAudio based on whether or not an audio tag was used
  18512. _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
  18513. // Update controls className. Can't do this when the controls are initially
  18514. // set because the element doesn't exist yet.
  18515. if (_this.controls()) {
  18516. _this.addClass('vjs-controls-enabled');
  18517. } else {
  18518. _this.addClass('vjs-controls-disabled');
  18519. }
  18520. // Set ARIA label and region role depending on player type
  18521. _this.el_.setAttribute('role', 'region');
  18522. if (_this.isAudio()) {
  18523. _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
  18524. } else {
  18525. _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
  18526. }
  18527. if (_this.isAudio()) {
  18528. _this.addClass('vjs-audio');
  18529. }
  18530. if (_this.flexNotSupported_()) {
  18531. _this.addClass('vjs-no-flex');
  18532. }
  18533. // TODO: Make this smarter. Toggle user state between touching/mousing
  18534. // using events, since devices can have both touch and mouse events.
  18535. // if (browser.TOUCH_ENABLED) {
  18536. // this.addClass('vjs-touch-enabled');
  18537. // }
  18538. // iOS Safari has broken hover handling
  18539. if (!IS_IOS) {
  18540. _this.addClass('vjs-workinghover');
  18541. }
  18542. // Make player easily findable by ID
  18543. Player.players[_this.id_] = _this;
  18544. // Add a major version class to aid css in plugins
  18545. var majorVersion = version.split('.')[0];
  18546. _this.addClass('vjs-v' + majorVersion);
  18547. // When the player is first initialized, trigger activity so components
  18548. // like the control bar show themselves if needed
  18549. _this.userActive(true);
  18550. _this.reportUserActivity();
  18551. _this.one('play', _this.listenForUserActivity_);
  18552. _this.on('fullscreenchange', _this.handleFullscreenChange_);
  18553. _this.on('stageclick', _this.handleStageClick_);
  18554. _this.breakpoints(_this.options_.breakpoints);
  18555. _this.responsive(_this.options_.responsive);
  18556. _this.changingSrc_ = false;
  18557. _this.playWaitingForReady_ = false;
  18558. _this.playOnLoadstart_ = null;
  18559. return _this;
  18560. }
  18561. /**
  18562. * Destroys the video player and does any necessary cleanup.
  18563. *
  18564. * This is especially helpful if you are dynamically adding and removing videos
  18565. * to/from the DOM.
  18566. *
  18567. * @fires Player#dispose
  18568. */
  18569. Player.prototype.dispose = function dispose() {
  18570. /**
  18571. * Called when the player is being disposed of.
  18572. *
  18573. * @event Player#dispose
  18574. * @type {EventTarget~Event}
  18575. */
  18576. this.trigger('dispose');
  18577. // prevent dispose from being called twice
  18578. this.off('dispose');
  18579. if (this.styleEl_ && this.styleEl_.parentNode) {
  18580. this.styleEl_.parentNode.removeChild(this.styleEl_);
  18581. this.styleEl_ = null;
  18582. }
  18583. // Kill reference to this player
  18584. Player.players[this.id_] = null;
  18585. if (this.tag && this.tag.player) {
  18586. this.tag.player = null;
  18587. }
  18588. if (this.el_ && this.el_.player) {
  18589. this.el_.player = null;
  18590. }
  18591. if (this.tech_) {
  18592. this.tech_.dispose();
  18593. this.isPosterFromTech_ = false;
  18594. this.poster_ = '';
  18595. }
  18596. if (this.playerElIngest_) {
  18597. this.playerElIngest_ = null;
  18598. }
  18599. if (this.tag) {
  18600. this.tag = null;
  18601. }
  18602. clearCacheForPlayer(this);
  18603. // the actual .el_ is removed here
  18604. _Component.prototype.dispose.call(this);
  18605. };
  18606. /**
  18607. * Create the `Player`'s DOM element.
  18608. *
  18609. * @return {Element}
  18610. * The DOM element that gets created.
  18611. */
  18612. Player.prototype.createEl = function createEl$$1() {
  18613. var tag = this.tag;
  18614. var el = void 0;
  18615. var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
  18616. var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
  18617. if (playerElIngest) {
  18618. el = this.el_ = tag.parentNode;
  18619. } else if (!divEmbed) {
  18620. el = this.el_ = _Component.prototype.createEl.call(this, 'div');
  18621. }
  18622. // Copy over all the attributes from the tag, including ID and class
  18623. // ID will now reference player box, not the video tag
  18624. var attrs = getAttributes(tag);
  18625. if (divEmbed) {
  18626. el = this.el_ = tag;
  18627. tag = this.tag = document_1.createElement('video');
  18628. while (el.children.length) {
  18629. tag.appendChild(el.firstChild);
  18630. }
  18631. if (!hasClass(el, 'video-js')) {
  18632. addClass(el, 'video-js');
  18633. }
  18634. el.appendChild(tag);
  18635. playerElIngest = this.playerElIngest_ = el;
  18636. // copy over properties from the video-js element
  18637. // ie8 doesn't support Object.keys nor hasOwnProperty
  18638. // on dom elements so we have to specify properties individually
  18639. ['autoplay', 'controls', 'crossOrigin', 'defaultMuted', 'defaultPlaybackRate', 'loop', 'muted', 'playbackRate', 'src', 'volume'].forEach(function (prop) {
  18640. if (typeof el[prop] !== 'undefined') {
  18641. tag[prop] = el[prop];
  18642. }
  18643. });
  18644. }
  18645. // set tabindex to -1 to remove the video element from the focus order
  18646. tag.setAttribute('tabindex', '-1');
  18647. attrs.tabindex = '-1';
  18648. // Workaround for #4583 (JAWS+IE doesn't announce BPB or play button)
  18649. // See https://github.com/FreedomScientific/VFO-standards-support/issues/78
  18650. // Note that we can't detect if JAWS is being used, but this ARIA attribute
  18651. // doesn't change behavior of IE11 if JAWS is not being used
  18652. if (IE_VERSION) {
  18653. tag.setAttribute('role', 'application');
  18654. attrs.role = 'application';
  18655. }
  18656. // Remove width/height attrs from tag so CSS can make it 100% width/height
  18657. tag.removeAttribute('width');
  18658. tag.removeAttribute('height');
  18659. if ('width' in attrs) {
  18660. delete attrs.width;
  18661. }
  18662. if ('height' in attrs) {
  18663. delete attrs.height;
  18664. }
  18665. Object.getOwnPropertyNames(attrs).forEach(function (attr) {
  18666. // workaround so we don't totally break IE7
  18667. // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
  18668. if (attr === 'class') {
  18669. el.className += ' ' + attrs[attr];
  18670. if (divEmbed) {
  18671. tag.className += ' ' + attrs[attr];
  18672. }
  18673. } else {
  18674. el.setAttribute(attr, attrs[attr]);
  18675. if (divEmbed) {
  18676. tag.setAttribute(attr, attrs[attr]);
  18677. }
  18678. }
  18679. });
  18680. // Update tag id/class for use as HTML5 playback tech
  18681. // Might think we should do this after embedding in container so .vjs-tech class
  18682. // doesn't flash 100% width/height, but class only applies with .video-js parent
  18683. tag.playerId = tag.id;
  18684. tag.id += '_html5_api';
  18685. tag.className = 'vjs-tech';
  18686. // Make player findable on elements
  18687. tag.player = el.player = this;
  18688. // Default state of video is paused
  18689. this.addClass('vjs-paused');
  18690. // Add a style element in the player that we'll use to set the width/height
  18691. // of the player in a way that's still overrideable by CSS, just like the
  18692. // video element
  18693. if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
  18694. this.styleEl_ = createStyleElement('vjs-styles-dimensions');
  18695. var defaultsStyleEl = $('.vjs-styles-defaults');
  18696. var head = $('head');
  18697. head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
  18698. }
  18699. this.fill_ = false;
  18700. this.fluid_ = false;
  18701. // Pass in the width/height/aspectRatio options which will update the style el
  18702. this.width(this.options_.width);
  18703. this.height(this.options_.height);
  18704. this.fill(this.options_.fill);
  18705. this.fluid(this.options_.fluid);
  18706. this.aspectRatio(this.options_.aspectRatio);
  18707. // Hide any links within the video/audio tag, because IE doesn't hide them completely.
  18708. var links = tag.getElementsByTagName('a');
  18709. for (var i = 0; i < links.length; i++) {
  18710. var linkEl = links.item(i);
  18711. addClass(linkEl, 'vjs-hidden');
  18712. linkEl.setAttribute('hidden', 'hidden');
  18713. }
  18714. // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
  18715. // keep track of the original for later so we can know if the source originally failed
  18716. tag.initNetworkState_ = tag.networkState;
  18717. // Wrap video tag in div (el/box) container
  18718. if (tag.parentNode && !playerElIngest) {
  18719. tag.parentNode.insertBefore(el, tag);
  18720. }
  18721. // insert the tag as the first child of the player element
  18722. // then manually add it to the children array so that this.addChild
  18723. // will work properly for other components
  18724. //
  18725. // Breaks iPhone, fixed in HTML5 setup.
  18726. prependTo(tag, el);
  18727. this.children_.unshift(tag);
  18728. // Set lang attr on player to ensure CSS :lang() in consistent with player
  18729. // if it's been set to something different to the doc
  18730. this.el_.setAttribute('lang', this.language_);
  18731. this.el_ = el;
  18732. return el;
  18733. };
  18734. /**
  18735. * A getter/setter for the `Player`'s width. Returns the player's configured value.
  18736. * To get the current width use `currentWidth()`.
  18737. *
  18738. * @param {number} [value]
  18739. * The value to set the `Player`'s width to.
  18740. *
  18741. * @return {number}
  18742. * The current width of the `Player` when getting.
  18743. */
  18744. Player.prototype.width = function width(value) {
  18745. return this.dimension('width', value);
  18746. };
  18747. /**
  18748. * A getter/setter for the `Player`'s height. Returns the player's configured value.
  18749. * To get the current height use `currentheight()`.
  18750. *
  18751. * @param {number} [value]
  18752. * The value to set the `Player`'s heigth to.
  18753. *
  18754. * @return {number}
  18755. * The current height of the `Player` when getting.
  18756. */
  18757. Player.prototype.height = function height(value) {
  18758. return this.dimension('height', value);
  18759. };
  18760. /**
  18761. * A getter/setter for the `Player`'s width & height.
  18762. *
  18763. * @param {string} dimension
  18764. * This string can be:
  18765. * - 'width'
  18766. * - 'height'
  18767. *
  18768. * @param {number} [value]
  18769. * Value for dimension specified in the first argument.
  18770. *
  18771. * @return {number}
  18772. * The dimension arguments value when getting (width/height).
  18773. */
  18774. Player.prototype.dimension = function dimension(_dimension, value) {
  18775. var privDimension = _dimension + '_';
  18776. if (value === undefined) {
  18777. return this[privDimension] || 0;
  18778. }
  18779. if (value === '') {
  18780. // If an empty string is given, reset the dimension to be automatic
  18781. this[privDimension] = undefined;
  18782. this.updateStyleEl_();
  18783. return;
  18784. }
  18785. var parsedVal = parseFloat(value);
  18786. if (isNaN(parsedVal)) {
  18787. log.error('Improper value "' + value + '" supplied for for ' + _dimension);
  18788. return;
  18789. }
  18790. this[privDimension] = parsedVal;
  18791. this.updateStyleEl_();
  18792. };
  18793. /**
  18794. * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
  18795. *
  18796. * Turning this on will turn off fill mode.
  18797. *
  18798. * @param {boolean} [bool]
  18799. * - A value of true adds the class.
  18800. * - A value of false removes the class.
  18801. * - No value will be a getter.
  18802. *
  18803. * @return {boolean|undefined}
  18804. * - The value of fluid when getting.
  18805. * - `undefined` when setting.
  18806. */
  18807. Player.prototype.fluid = function fluid(bool) {
  18808. if (bool === undefined) {
  18809. return !!this.fluid_;
  18810. }
  18811. this.fluid_ = !!bool;
  18812. if (bool) {
  18813. this.addClass('vjs-fluid');
  18814. this.fill(false);
  18815. } else {
  18816. this.removeClass('vjs-fluid');
  18817. }
  18818. this.updateStyleEl_();
  18819. };
  18820. /**
  18821. * A getter/setter/toggler for the vjs-fill `className` on the `Player`.
  18822. *
  18823. * Turning this on will turn off fluid mode.
  18824. *
  18825. * @param {boolean} [bool]
  18826. * - A value of true adds the class.
  18827. * - A value of false removes the class.
  18828. * - No value will be a getter.
  18829. *
  18830. * @return {boolean|undefined}
  18831. * - The value of fluid when getting.
  18832. * - `undefined` when setting.
  18833. */
  18834. Player.prototype.fill = function fill(bool) {
  18835. if (bool === undefined) {
  18836. return !!this.fill_;
  18837. }
  18838. this.fill_ = !!bool;
  18839. if (bool) {
  18840. this.addClass('vjs-fill');
  18841. this.fluid(false);
  18842. } else {
  18843. this.removeClass('vjs-fill');
  18844. }
  18845. };
  18846. /**
  18847. * Get/Set the aspect ratio
  18848. *
  18849. * @param {string} [ratio]
  18850. * Aspect ratio for player
  18851. *
  18852. * @return {string|undefined}
  18853. * returns the current aspect ratio when getting
  18854. */
  18855. /**
  18856. * A getter/setter for the `Player`'s aspect ratio.
  18857. *
  18858. * @param {string} [ratio]
  18859. * The value to set the `Player's aspect ratio to.
  18860. *
  18861. * @return {string|undefined}
  18862. * - The current aspect ratio of the `Player` when getting.
  18863. * - undefined when setting
  18864. */
  18865. Player.prototype.aspectRatio = function aspectRatio(ratio) {
  18866. if (ratio === undefined) {
  18867. return this.aspectRatio_;
  18868. }
  18869. // Check for width:height format
  18870. if (!/^\d+\:\d+$/.test(ratio)) {
  18871. throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
  18872. }
  18873. this.aspectRatio_ = ratio;
  18874. // We're assuming if you set an aspect ratio you want fluid mode,
  18875. // because in fixed mode you could calculate width and height yourself.
  18876. this.fluid(true);
  18877. this.updateStyleEl_();
  18878. };
  18879. /**
  18880. * Update styles of the `Player` element (height, width and aspect ratio).
  18881. *
  18882. * @private
  18883. * @listens Tech#loadedmetadata
  18884. */
  18885. Player.prototype.updateStyleEl_ = function updateStyleEl_() {
  18886. if (window_1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
  18887. var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
  18888. var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
  18889. var techEl = this.tech_ && this.tech_.el();
  18890. if (techEl) {
  18891. if (_width >= 0) {
  18892. techEl.width = _width;
  18893. }
  18894. if (_height >= 0) {
  18895. techEl.height = _height;
  18896. }
  18897. }
  18898. return;
  18899. }
  18900. var width = void 0;
  18901. var height = void 0;
  18902. var aspectRatio = void 0;
  18903. var idClass = void 0;
  18904. // The aspect ratio is either used directly or to calculate width and height.
  18905. if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
  18906. // Use any aspectRatio that's been specifically set
  18907. aspectRatio = this.aspectRatio_;
  18908. } else if (this.videoWidth() > 0) {
  18909. // Otherwise try to get the aspect ratio from the video metadata
  18910. aspectRatio = this.videoWidth() + ':' + this.videoHeight();
  18911. } else {
  18912. // Or use a default. The video element's is 2:1, but 16:9 is more common.
  18913. aspectRatio = '16:9';
  18914. }
  18915. // Get the ratio as a decimal we can use to calculate dimensions
  18916. var ratioParts = aspectRatio.split(':');
  18917. var ratioMultiplier = ratioParts[1] / ratioParts[0];
  18918. if (this.width_ !== undefined) {
  18919. // Use any width that's been specifically set
  18920. width = this.width_;
  18921. } else if (this.height_ !== undefined) {
  18922. // Or calulate the width from the aspect ratio if a height has been set
  18923. width = this.height_ / ratioMultiplier;
  18924. } else {
  18925. // Or use the video's metadata, or use the video el's default of 300
  18926. width = this.videoWidth() || 300;
  18927. }
  18928. if (this.height_ !== undefined) {
  18929. // Use any height that's been specifically set
  18930. height = this.height_;
  18931. } else {
  18932. // Otherwise calculate the height from the ratio and the width
  18933. height = width * ratioMultiplier;
  18934. }
  18935. // Ensure the CSS class is valid by starting with an alpha character
  18936. if (/^[^a-zA-Z]/.test(this.id())) {
  18937. idClass = 'dimensions-' + this.id();
  18938. } else {
  18939. idClass = this.id() + '-dimensions';
  18940. }
  18941. // Ensure the right class is still on the player for the style element
  18942. this.addClass(idClass);
  18943. 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 ');
  18944. };
  18945. /**
  18946. * Load/Create an instance of playback {@link Tech} including element
  18947. * and API methods. Then append the `Tech` element in `Player` as a child.
  18948. *
  18949. * @param {string} techName
  18950. * name of the playback technology
  18951. *
  18952. * @param {string} source
  18953. * video source
  18954. *
  18955. * @private
  18956. */
  18957. Player.prototype.loadTech_ = function loadTech_(techName, source) {
  18958. var _this2 = this;
  18959. // Pause and remove current playback technology
  18960. if (this.tech_) {
  18961. this.unloadTech_();
  18962. }
  18963. var titleTechName = toTitleCase(techName);
  18964. var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
  18965. // get rid of the HTML5 video tag as soon as we are using another tech
  18966. if (titleTechName !== 'Html5' && this.tag) {
  18967. Tech.getTech('Html5').disposeMediaElement(this.tag);
  18968. this.tag.player = null;
  18969. this.tag = null;
  18970. }
  18971. this.techName_ = titleTechName;
  18972. // Turn off API access because we're loading a new tech that might load asynchronously
  18973. this.isReady_ = false;
  18974. // if autoplay is a string we pass false to the tech
  18975. // because the player is going to handle autoplay on `loadstart`
  18976. var autoplay = typeof this.autoplay() === 'string' ? false : this.autoplay();
  18977. // Grab tech-specific options from player options and add source and parent element to use.
  18978. var techOptions = {
  18979. source: source,
  18980. autoplay: autoplay,
  18981. 'nativeControlsForTouch': this.options_.nativeControlsForTouch,
  18982. 'playerId': this.id(),
  18983. 'techId': this.id() + '_' + camelTechName + '_api',
  18984. 'playsinline': this.options_.playsinline,
  18985. 'preload': this.options_.preload,
  18986. 'loop': this.options_.loop,
  18987. 'muted': this.options_.muted,
  18988. 'poster': this.poster(),
  18989. 'language': this.language(),
  18990. 'playerElIngest': this.playerElIngest_ || false,
  18991. 'vtt.js': this.options_['vtt.js'],
  18992. 'canOverridePoster': !!this.options_.techCanOverridePoster,
  18993. 'enableSourceset': this.options_.enableSourceset
  18994. };
  18995. ALL.names.forEach(function (name$$1) {
  18996. var props = ALL[name$$1];
  18997. techOptions[props.getterName] = _this2[props.privateName];
  18998. });
  18999. assign(techOptions, this.options_[titleTechName]);
  19000. assign(techOptions, this.options_[camelTechName]);
  19001. assign(techOptions, this.options_[techName.toLowerCase()]);
  19002. if (this.tag) {
  19003. techOptions.tag = this.tag;
  19004. }
  19005. if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
  19006. techOptions.startTime = this.cache_.currentTime;
  19007. }
  19008. // Initialize tech instance
  19009. var TechClass = Tech.getTech(techName);
  19010. if (!TechClass) {
  19011. throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\'');
  19012. }
  19013. this.tech_ = new TechClass(techOptions);
  19014. // player.triggerReady is always async, so don't need this to be async
  19015. this.tech_.ready(bind(this, this.handleTechReady_), true);
  19016. textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
  19017. // Listen to all HTML5-defined events and trigger them on the player
  19018. TECH_EVENTS_RETRIGGER.forEach(function (event) {
  19019. _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']);
  19020. });
  19021. Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
  19022. _this2.on(_this2.tech_, event, function (eventObj) {
  19023. if (_this2.tech_.playbackRate() === 0 && _this2.tech_.seeking()) {
  19024. _this2.queuedCallbacks_.push({
  19025. callback: _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'].bind(_this2),
  19026. event: eventObj
  19027. });
  19028. return;
  19029. }
  19030. _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'](eventObj);
  19031. });
  19032. });
  19033. this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
  19034. this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
  19035. this.on(this.tech_, 'waiting', this.handleTechWaiting_);
  19036. this.on(this.tech_, 'ended', this.handleTechEnded_);
  19037. this.on(this.tech_, 'seeking', this.handleTechSeeking_);
  19038. this.on(this.tech_, 'play', this.handleTechPlay_);
  19039. this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
  19040. this.on(this.tech_, 'pause', this.handleTechPause_);
  19041. this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
  19042. this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
  19043. this.on(this.tech_, 'error', this.handleTechError_);
  19044. this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
  19045. this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
  19046. this.on(this.tech_, 'textdata', this.handleTechTextData_);
  19047. this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
  19048. this.usingNativeControls(this.techGet_('controls'));
  19049. if (this.controls() && !this.usingNativeControls()) {
  19050. this.addTechControlsListeners_();
  19051. }
  19052. // Add the tech element in the DOM if it was not already there
  19053. // Make sure to not insert the original video element if using Html5
  19054. if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
  19055. prependTo(this.tech_.el(), this.el());
  19056. }
  19057. // Get rid of the original video tag reference after the first tech is loaded
  19058. if (this.tag) {
  19059. this.tag.player = null;
  19060. this.tag = null;
  19061. }
  19062. };
  19063. /**
  19064. * Unload and dispose of the current playback {@link Tech}.
  19065. *
  19066. * @private
  19067. */
  19068. Player.prototype.unloadTech_ = function unloadTech_() {
  19069. var _this3 = this;
  19070. // Save the current text tracks so that we can reuse the same text tracks with the next tech
  19071. ALL.names.forEach(function (name$$1) {
  19072. var props = ALL[name$$1];
  19073. _this3[props.privateName] = _this3[props.getterName]();
  19074. });
  19075. this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
  19076. this.isReady_ = false;
  19077. this.tech_.dispose();
  19078. this.tech_ = false;
  19079. if (this.isPosterFromTech_) {
  19080. this.poster_ = '';
  19081. this.trigger('posterchange');
  19082. }
  19083. this.isPosterFromTech_ = false;
  19084. };
  19085. /**
  19086. * Return a reference to the current {@link Tech}.
  19087. * It will print a warning by default about the danger of using the tech directly
  19088. * but any argument that is passed in will silence the warning.
  19089. *
  19090. * @param {*} [safety]
  19091. * Anything passed in to silence the warning
  19092. *
  19093. * @return {Tech}
  19094. * The Tech
  19095. */
  19096. Player.prototype.tech = function tech(safety) {
  19097. if (safety === undefined) {
  19098. log.warn(tsml(_templateObject$1));
  19099. }
  19100. return this.tech_;
  19101. };
  19102. /**
  19103. * Set up click and touch listeners for the playback element
  19104. *
  19105. * - On desktops: a click on the video itself will toggle playback
  19106. * - On mobile devices: a click on the video toggles controls
  19107. * which is done by toggling the user state between active and
  19108. * inactive
  19109. * - A tap can signal that a user has become active or has become inactive
  19110. * e.g. a quick tap on an iPhone movie should reveal the controls. Another
  19111. * quick tap should hide them again (signaling the user is in an inactive
  19112. * viewing state)
  19113. * - In addition to this, we still want the user to be considered inactive after
  19114. * a few seconds of inactivity.
  19115. *
  19116. * > Note: the only part of iOS interaction we can't mimic with this setup
  19117. * is a touch and hold on the video element counting as activity in order to
  19118. * keep the controls showing, but that shouldn't be an issue. A touch and hold
  19119. * on any controls will still keep the user active
  19120. *
  19121. * @private
  19122. */
  19123. Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
  19124. // Make sure to remove all the previous listeners in case we are called multiple times.
  19125. this.removeTechControlsListeners_();
  19126. // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
  19127. // trigger mousedown/up.
  19128. // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
  19129. // Any touch events are set to block the mousedown event from happening
  19130. this.on(this.tech_, 'mousedown', this.handleTechClick_);
  19131. // If the controls were hidden we don't want that to change without a tap event
  19132. // so we'll check if the controls were already showing before reporting user
  19133. // activity
  19134. this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
  19135. this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
  19136. this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
  19137. // The tap listener needs to come after the touchend listener because the tap
  19138. // listener cancels out any reportedUserActivity when setting userActive(false)
  19139. this.on(this.tech_, 'tap', this.handleTechTap_);
  19140. };
  19141. /**
  19142. * Remove the listeners used for click and tap controls. This is needed for
  19143. * toggling to controls disabled, where a tap/touch should do nothing.
  19144. *
  19145. * @private
  19146. */
  19147. Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
  19148. // We don't want to just use `this.off()` because there might be other needed
  19149. // listeners added by techs that extend this.
  19150. this.off(this.tech_, 'tap', this.handleTechTap_);
  19151. this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
  19152. this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
  19153. this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
  19154. this.off(this.tech_, 'mousedown', this.handleTechClick_);
  19155. };
  19156. /**
  19157. * Player waits for the tech to be ready
  19158. *
  19159. * @private
  19160. */
  19161. Player.prototype.handleTechReady_ = function handleTechReady_() {
  19162. this.triggerReady();
  19163. // Keep the same volume as before
  19164. if (this.cache_.volume) {
  19165. this.techCall_('setVolume', this.cache_.volume);
  19166. }
  19167. // Look if the tech found a higher resolution poster while loading
  19168. this.handleTechPosterChange_();
  19169. // Update the duration if available
  19170. this.handleTechDurationChange_();
  19171. // Chrome and Safari both have issues with autoplay.
  19172. // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
  19173. // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
  19174. // This fixes both issues. Need to wait for API, so it updates displays correctly
  19175. if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
  19176. try {
  19177. // Chrome Fix. Fixed in Chrome v16.
  19178. delete this.tag.poster;
  19179. } catch (e) {
  19180. log('deleting tag.poster throws in some browsers', e);
  19181. }
  19182. }
  19183. };
  19184. /**
  19185. * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
  19186. * function will also trigger {@link Player#firstplay} if it is the first loadstart
  19187. * for a video.
  19188. *
  19189. * @fires Player#loadstart
  19190. * @fires Player#firstplay
  19191. * @listens Tech#loadstart
  19192. * @private
  19193. */
  19194. Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
  19195. // TODO: Update to use `emptied` event instead. See #1277.
  19196. this.removeClass('vjs-ended');
  19197. this.removeClass('vjs-seeking');
  19198. // reset the error state
  19199. this.error(null);
  19200. // If it's already playing we want to trigger a firstplay event now.
  19201. // The firstplay event relies on both the play and loadstart events
  19202. // which can happen in any order for a new source
  19203. if (!this.paused()) {
  19204. /**
  19205. * Fired when the user agent begins looking for media data
  19206. *
  19207. * @event Player#loadstart
  19208. * @type {EventTarget~Event}
  19209. */
  19210. this.trigger('loadstart');
  19211. this.trigger('firstplay');
  19212. } else {
  19213. // reset the hasStarted state
  19214. this.hasStarted(false);
  19215. this.trigger('loadstart');
  19216. }
  19217. // autoplay happens after loadstart for the browser,
  19218. // so we mimic that behavior
  19219. this.manualAutoplay_(this.autoplay());
  19220. };
  19221. /**
  19222. * Handle autoplay string values, rather than the typical boolean
  19223. * values that should be handled by the tech. Note that this is not
  19224. * part of any specification. Valid values and what they do can be
  19225. * found on the autoplay getter at Player#autoplay()
  19226. */
  19227. Player.prototype.manualAutoplay_ = function manualAutoplay_(type) {
  19228. var _this4 = this;
  19229. if (!this.tech_ || typeof type !== 'string') {
  19230. return;
  19231. }
  19232. var muted = function muted() {
  19233. var previouslyMuted = _this4.muted();
  19234. _this4.muted(true);
  19235. var playPromise = _this4.play();
  19236. if (!playPromise || !playPromise.then || !playPromise['catch']) {
  19237. return;
  19238. }
  19239. return playPromise['catch'](function (e) {
  19240. // restore old value of muted on failure
  19241. _this4.muted(previouslyMuted);
  19242. });
  19243. };
  19244. var promise = void 0;
  19245. if (type === 'any') {
  19246. promise = this.play();
  19247. if (promise && promise.then && promise['catch']) {
  19248. promise['catch'](function () {
  19249. return muted();
  19250. });
  19251. }
  19252. } else if (type === 'muted') {
  19253. promise = muted();
  19254. } else {
  19255. promise = this.play();
  19256. }
  19257. if (!promise || !promise.then || !promise['catch']) {
  19258. return;
  19259. }
  19260. return promise.then(function () {
  19261. _this4.trigger({ type: 'autoplay-success', autoplay: type });
  19262. })['catch'](function (e) {
  19263. _this4.trigger({ type: 'autoplay-failure', autoplay: type });
  19264. });
  19265. };
  19266. /**
  19267. * Update the internal source caches so that we return the correct source from
  19268. * `src()`, `currentSource()`, and `currentSources()`.
  19269. *
  19270. * > Note: `currentSources` will not be updated if the source that is passed in exists
  19271. * in the current `currentSources` cache.
  19272. *
  19273. *
  19274. * @param {Tech~SourceObject} srcObj
  19275. * A string or object source to update our caches to.
  19276. */
  19277. Player.prototype.updateSourceCaches_ = function updateSourceCaches_() {
  19278. var srcObj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  19279. var src = srcObj;
  19280. var type = '';
  19281. if (typeof src !== 'string') {
  19282. src = srcObj.src;
  19283. type = srcObj.type;
  19284. }
  19285. // make sure all the caches are set to default values
  19286. // to prevent null checking
  19287. this.cache_.source = this.cache_.source || {};
  19288. this.cache_.sources = this.cache_.sources || [];
  19289. // try to get the type of the src that was passed in
  19290. if (src && !type) {
  19291. type = findMimetype(this, src);
  19292. }
  19293. // update `currentSource` cache always
  19294. this.cache_.source = mergeOptions({}, srcObj, { src: src, type: type });
  19295. var matchingSources = this.cache_.sources.filter(function (s) {
  19296. return s.src && s.src === src;
  19297. });
  19298. var sourceElSources = [];
  19299. var sourceEls = this.$$('source');
  19300. var matchingSourceEls = [];
  19301. for (var i = 0; i < sourceEls.length; i++) {
  19302. var sourceObj = getAttributes(sourceEls[i]);
  19303. sourceElSources.push(sourceObj);
  19304. if (sourceObj.src && sourceObj.src === src) {
  19305. matchingSourceEls.push(sourceObj.src);
  19306. }
  19307. }
  19308. // if we have matching source els but not matching sources
  19309. // the current source cache is not up to date
  19310. if (matchingSourceEls.length && !matchingSources.length) {
  19311. this.cache_.sources = sourceElSources;
  19312. // if we don't have matching source or source els set the
  19313. // sources cache to the `currentSource` cache
  19314. } else if (!matchingSources.length) {
  19315. this.cache_.sources = [this.cache_.source];
  19316. }
  19317. // update the tech `src` cache
  19318. this.cache_.src = src;
  19319. };
  19320. /**
  19321. * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
  19322. * causing the media element to reload.
  19323. *
  19324. * It will fire for the initial source and each subsequent source.
  19325. * This event is a custom event from Video.js and is triggered by the {@link Tech}.
  19326. *
  19327. * The event object for this event contains a `src` property that will contain the source
  19328. * that was available when the event was triggered. This is generally only necessary if Video.js
  19329. * is switching techs while the source was being changed.
  19330. *
  19331. * It is also fired when `load` is called on the player (or media element)
  19332. * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
  19333. * says that the resource selection algorithm needs to be aborted and restarted.
  19334. * In this case, it is very likely that the `src` property will be set to the
  19335. * empty string `""` to indicate we do not know what the source will be but
  19336. * that it is changing.
  19337. *
  19338. * *This event is currently still experimental and may change in minor releases.*
  19339. * __To use this, pass `enableSourceset` option to the player.__
  19340. *
  19341. * @event Player#sourceset
  19342. * @type {EventTarget~Event}
  19343. * @prop {string} src
  19344. * The source url available when the `sourceset` was triggered.
  19345. * It will be an empty string if we cannot know what the source is
  19346. * but know that the source will change.
  19347. */
  19348. /**
  19349. * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
  19350. *
  19351. * @fires Player#sourceset
  19352. * @listens Tech#sourceset
  19353. * @private
  19354. */
  19355. Player.prototype.handleTechSourceset_ = function handleTechSourceset_(event) {
  19356. var _this5 = this;
  19357. // only update the source cache when the source
  19358. // was not updated using the player api
  19359. if (!this.changingSrc_) {
  19360. var updateSourceCaches = function updateSourceCaches(src) {
  19361. return _this5.updateSourceCaches_(src);
  19362. };
  19363. var playerSrc = this.currentSource().src;
  19364. var eventSrc = event.src;
  19365. // if we have a playerSrc that is not a blob, and a tech src that is a blob
  19366. if (playerSrc && !/^blob:/.test(playerSrc) && /^blob:/.test(eventSrc)) {
  19367. // if both the tech source and the player source were updated we assume
  19368. // something like @videojs/http-streaming did the sourceset and skip updating the source cache.
  19369. if (!this.lastSource_ || this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc) {
  19370. updateSourceCaches = function updateSourceCaches() {};
  19371. }
  19372. }
  19373. // update the source to the intial source right away
  19374. // in some cases this will be empty string
  19375. updateSourceCaches(eventSrc);
  19376. // if the `sourceset` `src` was an empty string
  19377. // wait for a `loadstart` to update the cache to `currentSrc`.
  19378. // If a sourceset happens before a `loadstart`, we reset the state
  19379. // as this function will be called again.
  19380. if (!event.src) {
  19381. var updateCache = function updateCache(e) {
  19382. if (e.type !== 'sourceset') {
  19383. var techSrc = _this5.techGet('currentSrc');
  19384. _this5.lastSource_.tech = techSrc;
  19385. _this5.updateSourceCaches_(techSrc);
  19386. }
  19387. _this5.tech_.off(['sourceset', 'loadstart'], updateCache);
  19388. };
  19389. this.tech_.one(['sourceset', 'loadstart'], updateCache);
  19390. }
  19391. }
  19392. this.lastSource_ = { player: this.currentSource().src, tech: event.src };
  19393. this.trigger({
  19394. src: event.src,
  19395. type: 'sourceset'
  19396. });
  19397. };
  19398. /**
  19399. * Add/remove the vjs-has-started class
  19400. *
  19401. * @fires Player#firstplay
  19402. *
  19403. * @param {boolean} request
  19404. * - true: adds the class
  19405. * - false: remove the class
  19406. *
  19407. * @return {boolean}
  19408. * the boolean value of hasStarted_
  19409. */
  19410. Player.prototype.hasStarted = function hasStarted(request) {
  19411. if (request === undefined) {
  19412. // act as getter, if we have no request to change
  19413. return this.hasStarted_;
  19414. }
  19415. if (request === this.hasStarted_) {
  19416. return;
  19417. }
  19418. this.hasStarted_ = request;
  19419. if (this.hasStarted_) {
  19420. this.addClass('vjs-has-started');
  19421. this.trigger('firstplay');
  19422. } else {
  19423. this.removeClass('vjs-has-started');
  19424. }
  19425. };
  19426. /**
  19427. * Fired whenever the media begins or resumes playback
  19428. *
  19429. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
  19430. * @fires Player#play
  19431. * @listens Tech#play
  19432. * @private
  19433. */
  19434. Player.prototype.handleTechPlay_ = function handleTechPlay_() {
  19435. this.removeClass('vjs-ended');
  19436. this.removeClass('vjs-paused');
  19437. this.addClass('vjs-playing');
  19438. // hide the poster when the user hits play
  19439. this.hasStarted(true);
  19440. /**
  19441. * Triggered whenever an {@link Tech#play} event happens. Indicates that
  19442. * playback has started or resumed.
  19443. *
  19444. * @event Player#play
  19445. * @type {EventTarget~Event}
  19446. */
  19447. this.trigger('play');
  19448. };
  19449. /**
  19450. * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
  19451. *
  19452. * If there were any events queued while the playback rate was zero, fire
  19453. * those events now.
  19454. *
  19455. * @private
  19456. * @method Player#handleTechRateChange_
  19457. * @fires Player#ratechange
  19458. * @listens Tech#ratechange
  19459. */
  19460. Player.prototype.handleTechRateChange_ = function handleTechRateChange_() {
  19461. if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
  19462. this.queuedCallbacks_.forEach(function (queued) {
  19463. return queued.callback(queued.event);
  19464. });
  19465. this.queuedCallbacks_ = [];
  19466. }
  19467. this.cache_.lastPlaybackRate = this.tech_.playbackRate();
  19468. /**
  19469. * Fires when the playing speed of the audio/video is changed
  19470. *
  19471. * @event Player#ratechange
  19472. * @type {event}
  19473. */
  19474. this.trigger('ratechange');
  19475. };
  19476. /**
  19477. * Retrigger the `waiting` event that was triggered by the {@link Tech}.
  19478. *
  19479. * @fires Player#waiting
  19480. * @listens Tech#waiting
  19481. * @private
  19482. */
  19483. Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
  19484. var _this6 = this;
  19485. this.addClass('vjs-waiting');
  19486. /**
  19487. * A readyState change on the DOM element has caused playback to stop.
  19488. *
  19489. * @event Player#waiting
  19490. * @type {EventTarget~Event}
  19491. */
  19492. this.trigger('waiting');
  19493. this.one('timeupdate', function () {
  19494. return _this6.removeClass('vjs-waiting');
  19495. });
  19496. };
  19497. /**
  19498. * Retrigger the `canplay` event that was triggered by the {@link Tech}.
  19499. * > Note: This is not consistent between browsers. See #1351
  19500. *
  19501. * @fires Player#canplay
  19502. * @listens Tech#canplay
  19503. * @private
  19504. */
  19505. Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
  19506. this.removeClass('vjs-waiting');
  19507. /**
  19508. * The media has a readyState of HAVE_FUTURE_DATA or greater.
  19509. *
  19510. * @event Player#canplay
  19511. * @type {EventTarget~Event}
  19512. */
  19513. this.trigger('canplay');
  19514. };
  19515. /**
  19516. * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
  19517. *
  19518. * @fires Player#canplaythrough
  19519. * @listens Tech#canplaythrough
  19520. * @private
  19521. */
  19522. Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
  19523. this.removeClass('vjs-waiting');
  19524. /**
  19525. * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
  19526. * entire media file can be played without buffering.
  19527. *
  19528. * @event Player#canplaythrough
  19529. * @type {EventTarget~Event}
  19530. */
  19531. this.trigger('canplaythrough');
  19532. };
  19533. /**
  19534. * Retrigger the `playing` event that was triggered by the {@link Tech}.
  19535. *
  19536. * @fires Player#playing
  19537. * @listens Tech#playing
  19538. * @private
  19539. */
  19540. Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
  19541. this.removeClass('vjs-waiting');
  19542. /**
  19543. * The media is no longer blocked from playback, and has started playing.
  19544. *
  19545. * @event Player#playing
  19546. * @type {EventTarget~Event}
  19547. */
  19548. this.trigger('playing');
  19549. };
  19550. /**
  19551. * Retrigger the `seeking` event that was triggered by the {@link Tech}.
  19552. *
  19553. * @fires Player#seeking
  19554. * @listens Tech#seeking
  19555. * @private
  19556. */
  19557. Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
  19558. this.addClass('vjs-seeking');
  19559. /**
  19560. * Fired whenever the player is jumping to a new time
  19561. *
  19562. * @event Player#seeking
  19563. * @type {EventTarget~Event}
  19564. */
  19565. this.trigger('seeking');
  19566. };
  19567. /**
  19568. * Retrigger the `seeked` event that was triggered by the {@link Tech}.
  19569. *
  19570. * @fires Player#seeked
  19571. * @listens Tech#seeked
  19572. * @private
  19573. */
  19574. Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
  19575. this.removeClass('vjs-seeking');
  19576. /**
  19577. * Fired when the player has finished jumping to a new time
  19578. *
  19579. * @event Player#seeked
  19580. * @type {EventTarget~Event}
  19581. */
  19582. this.trigger('seeked');
  19583. };
  19584. /**
  19585. * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
  19586. *
  19587. * @fires Player#firstplay
  19588. * @listens Tech#firstplay
  19589. * @deprecated As of 6.0 firstplay event is deprecated.
  19590. * As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
  19591. * @private
  19592. */
  19593. Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
  19594. // If the first starttime attribute is specified
  19595. // then we will start at the given offset in seconds
  19596. if (this.options_.starttime) {
  19597. log.warn('Passing the `starttime` option to the player will be deprecated in 6.0');
  19598. this.currentTime(this.options_.starttime);
  19599. }
  19600. this.addClass('vjs-has-started');
  19601. /**
  19602. * Fired the first time a video is played. Not part of the HLS spec, and this is
  19603. * probably not the best implementation yet, so use sparingly. If you don't have a
  19604. * reason to prevent playback, use `myPlayer.one('play');` instead.
  19605. *
  19606. * @event Player#firstplay
  19607. * @deprecated As of 6.0 firstplay event is deprecated.
  19608. * @type {EventTarget~Event}
  19609. */
  19610. this.trigger('firstplay');
  19611. };
  19612. /**
  19613. * Retrigger the `pause` event that was triggered by the {@link Tech}.
  19614. *
  19615. * @fires Player#pause
  19616. * @listens Tech#pause
  19617. * @private
  19618. */
  19619. Player.prototype.handleTechPause_ = function handleTechPause_() {
  19620. this.removeClass('vjs-playing');
  19621. this.addClass('vjs-paused');
  19622. /**
  19623. * Fired whenever the media has been paused
  19624. *
  19625. * @event Player#pause
  19626. * @type {EventTarget~Event}
  19627. */
  19628. this.trigger('pause');
  19629. };
  19630. /**
  19631. * Retrigger the `ended` event that was triggered by the {@link Tech}.
  19632. *
  19633. * @fires Player#ended
  19634. * @listens Tech#ended
  19635. * @private
  19636. */
  19637. Player.prototype.handleTechEnded_ = function handleTechEnded_() {
  19638. this.addClass('vjs-ended');
  19639. if (this.options_.loop) {
  19640. this.currentTime(0);
  19641. this.play();
  19642. } else if (!this.paused()) {
  19643. this.pause();
  19644. }
  19645. /**
  19646. * Fired when the end of the media resource is reached (currentTime == duration)
  19647. *
  19648. * @event Player#ended
  19649. * @type {EventTarget~Event}
  19650. */
  19651. this.trigger('ended');
  19652. };
  19653. /**
  19654. * Fired when the duration of the media resource is first known or changed
  19655. *
  19656. * @listens Tech#durationchange
  19657. * @private
  19658. */
  19659. Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
  19660. this.duration(this.techGet_('duration'));
  19661. };
  19662. /**
  19663. * Handle a click on the media element to play/pause
  19664. *
  19665. * @param {EventTarget~Event} event
  19666. * the event that caused this function to trigger
  19667. *
  19668. * @listens Tech#mousedown
  19669. * @private
  19670. */
  19671. Player.prototype.handleTechClick_ = function handleTechClick_(event) {
  19672. if (!isSingleLeftClick(event)) {
  19673. return;
  19674. }
  19675. // When controls are disabled a click should not toggle playback because
  19676. // the click is considered a control
  19677. if (!this.controls_) {
  19678. return;
  19679. }
  19680. if (this.paused()) {
  19681. silencePromise(this.play());
  19682. } else {
  19683. this.pause();
  19684. }
  19685. };
  19686. /**
  19687. * Handle a tap on the media element. It will toggle the user
  19688. * activity state, which hides and shows the controls.
  19689. *
  19690. * @listens Tech#tap
  19691. * @private
  19692. */
  19693. Player.prototype.handleTechTap_ = function handleTechTap_() {
  19694. this.userActive(!this.userActive());
  19695. };
  19696. /**
  19697. * Handle touch to start
  19698. *
  19699. * @listens Tech#touchstart
  19700. * @private
  19701. */
  19702. Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
  19703. this.userWasActive = this.userActive();
  19704. };
  19705. /**
  19706. * Handle touch to move
  19707. *
  19708. * @listens Tech#touchmove
  19709. * @private
  19710. */
  19711. Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
  19712. if (this.userWasActive) {
  19713. this.reportUserActivity();
  19714. }
  19715. };
  19716. /**
  19717. * Handle touch to end
  19718. *
  19719. * @param {EventTarget~Event} event
  19720. * the touchend event that triggered
  19721. * this function
  19722. *
  19723. * @listens Tech#touchend
  19724. * @private
  19725. */
  19726. Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
  19727. // Stop the mouse events from also happening
  19728. event.preventDefault();
  19729. };
  19730. /**
  19731. * Fired when the player switches in or out of fullscreen mode
  19732. *
  19733. * @private
  19734. * @listens Player#fullscreenchange
  19735. */
  19736. Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
  19737. if (this.isFullscreen()) {
  19738. this.addClass('vjs-fullscreen');
  19739. } else {
  19740. this.removeClass('vjs-fullscreen');
  19741. }
  19742. };
  19743. /**
  19744. * native click events on the SWF aren't triggered on IE11, Win8.1RT
  19745. * use stageclick events triggered from inside the SWF instead
  19746. *
  19747. * @private
  19748. * @listens stageclick
  19749. */
  19750. Player.prototype.handleStageClick_ = function handleStageClick_() {
  19751. this.reportUserActivity();
  19752. };
  19753. /**
  19754. * Handle Tech Fullscreen Change
  19755. *
  19756. * @param {EventTarget~Event} event
  19757. * the fullscreenchange event that triggered this function
  19758. *
  19759. * @param {Object} data
  19760. * the data that was sent with the event
  19761. *
  19762. * @private
  19763. * @listens Tech#fullscreenchange
  19764. * @fires Player#fullscreenchange
  19765. */
  19766. Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
  19767. if (data) {
  19768. this.isFullscreen(data.isFullscreen);
  19769. }
  19770. /**
  19771. * Fired when going in and out of fullscreen.
  19772. *
  19773. * @event Player#fullscreenchange
  19774. * @type {EventTarget~Event}
  19775. */
  19776. this.trigger('fullscreenchange');
  19777. };
  19778. /**
  19779. * Fires when an error occurred during the loading of an audio/video.
  19780. *
  19781. * @private
  19782. * @listens Tech#error
  19783. */
  19784. Player.prototype.handleTechError_ = function handleTechError_() {
  19785. var error = this.tech_.error();
  19786. this.error(error);
  19787. };
  19788. /**
  19789. * Retrigger the `textdata` event that was triggered by the {@link Tech}.
  19790. *
  19791. * @fires Player#textdata
  19792. * @listens Tech#textdata
  19793. * @private
  19794. */
  19795. Player.prototype.handleTechTextData_ = function handleTechTextData_() {
  19796. var data = null;
  19797. if (arguments.length > 1) {
  19798. data = arguments[1];
  19799. }
  19800. /**
  19801. * Fires when we get a textdata event from tech
  19802. *
  19803. * @event Player#textdata
  19804. * @type {EventTarget~Event}
  19805. */
  19806. this.trigger('textdata', data);
  19807. };
  19808. /**
  19809. * Get object for cached values.
  19810. *
  19811. * @return {Object}
  19812. * get the current object cache
  19813. */
  19814. Player.prototype.getCache = function getCache() {
  19815. return this.cache_;
  19816. };
  19817. /**
  19818. * Pass values to the playback tech
  19819. *
  19820. * @param {string} [method]
  19821. * the method to call
  19822. *
  19823. * @param {Object} arg
  19824. * the argument to pass
  19825. *
  19826. * @private
  19827. */
  19828. Player.prototype.techCall_ = function techCall_(method, arg) {
  19829. // If it's not ready yet, call method when it is
  19830. this.ready(function () {
  19831. if (method in allowedSetters) {
  19832. return set$1(this.middleware_, this.tech_, method, arg);
  19833. } else if (method in allowedMediators) {
  19834. return mediate(this.middleware_, this.tech_, method, arg);
  19835. }
  19836. try {
  19837. if (this.tech_) {
  19838. this.tech_[method](arg);
  19839. }
  19840. } catch (e) {
  19841. log(e);
  19842. throw e;
  19843. }
  19844. }, true);
  19845. };
  19846. /**
  19847. * Get calls can't wait for the tech, and sometimes don't need to.
  19848. *
  19849. * @param {string} method
  19850. * Tech method
  19851. *
  19852. * @return {Function|undefined}
  19853. * the method or undefined
  19854. *
  19855. * @private
  19856. */
  19857. Player.prototype.techGet_ = function techGet_(method) {
  19858. if (!this.tech_ || !this.tech_.isReady_) {
  19859. return;
  19860. }
  19861. if (method in allowedGetters) {
  19862. return get$1(this.middleware_, this.tech_, method);
  19863. } else if (method in allowedMediators) {
  19864. return mediate(this.middleware_, this.tech_, method);
  19865. }
  19866. // Flash likes to die and reload when you hide or reposition it.
  19867. // In these cases the object methods go away and we get errors.
  19868. // When that happens we'll catch the errors and inform tech that it's not ready any more.
  19869. try {
  19870. return this.tech_[method]();
  19871. } catch (e) {
  19872. // When building additional tech libs, an expected method may not be defined yet
  19873. if (this.tech_[method] === undefined) {
  19874. log('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
  19875. throw e;
  19876. }
  19877. // When a method isn't available on the object it throws a TypeError
  19878. if (e.name === 'TypeError') {
  19879. log('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
  19880. this.tech_.isReady_ = false;
  19881. throw e;
  19882. }
  19883. // If error unknown, just log and throw
  19884. log(e);
  19885. throw e;
  19886. }
  19887. };
  19888. /**
  19889. * Attempt to begin playback at the first opportunity.
  19890. *
  19891. * @return {Promise|undefined}
  19892. * Returns a promise if the browser supports Promises (or one
  19893. * was passed in as an option). This promise will be resolved on
  19894. * the return value of play. If this is undefined it will fulfill the
  19895. * promise chain otherwise the promise chain will be fulfilled when
  19896. * the promise from play is fulfilled.
  19897. */
  19898. Player.prototype.play = function play() {
  19899. var _this7 = this;
  19900. var PromiseClass = this.options_.Promise || window_1.Promise;
  19901. if (PromiseClass) {
  19902. return new PromiseClass(function (resolve) {
  19903. _this7.play_(resolve);
  19904. });
  19905. }
  19906. return this.play_();
  19907. };
  19908. /**
  19909. * The actual logic for play, takes a callback that will be resolved on the
  19910. * return value of play. This allows us to resolve to the play promise if there
  19911. * is one on modern browsers.
  19912. *
  19913. * @private
  19914. * @param {Function} [callback]
  19915. * The callback that should be called when the techs play is actually called
  19916. */
  19917. Player.prototype.play_ = function play_() {
  19918. var _this8 = this;
  19919. var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : silencePromise;
  19920. // If this is called while we have a play queued up on a loadstart, remove
  19921. // that listener to avoid getting in a potentially bad state.
  19922. if (this.playOnLoadstart_) {
  19923. this.off('loadstart', this.playOnLoadstart_);
  19924. }
  19925. // If the player/tech is not ready, queue up another call to `play()` for
  19926. // when it is. This will loop back into this method for another attempt at
  19927. // playback when the tech is ready.
  19928. if (!this.isReady_) {
  19929. // Bail out if we're already waiting for `ready`!
  19930. if (this.playWaitingForReady_) {
  19931. return;
  19932. }
  19933. this.playWaitingForReady_ = true;
  19934. this.ready(function () {
  19935. _this8.playWaitingForReady_ = false;
  19936. callback(_this8.play());
  19937. });
  19938. // If the player/tech is ready and we have a source, we can attempt playback.
  19939. } else if (!this.changingSrc_ && (this.src() || this.currentSrc())) {
  19940. callback(this.techGet_('play'));
  19941. return;
  19942. // If the tech is ready, but we do not have a source, we'll need to wait
  19943. // for both the `ready` and a `loadstart` when the source is finally
  19944. // resolved by middleware and set on the player.
  19945. //
  19946. // This can happen if `play()` is called while changing sources or before
  19947. // one has been set on the player.
  19948. } else {
  19949. this.playOnLoadstart_ = function () {
  19950. _this8.playOnLoadstart_ = null;
  19951. callback(_this8.play());
  19952. };
  19953. this.one('loadstart', this.playOnLoadstart_);
  19954. }
  19955. };
  19956. /**
  19957. * Pause the video playback
  19958. *
  19959. * @return {Player}
  19960. * A reference to the player object this function was called on
  19961. */
  19962. Player.prototype.pause = function pause() {
  19963. this.techCall_('pause');
  19964. };
  19965. /**
  19966. * Check if the player is paused or has yet to play
  19967. *
  19968. * @return {boolean}
  19969. * - false: if the media is currently playing
  19970. * - true: if media is not currently playing
  19971. */
  19972. Player.prototype.paused = function paused() {
  19973. // The initial state of paused should be true (in Safari it's actually false)
  19974. return this.techGet_('paused') === false ? false : true;
  19975. };
  19976. /**
  19977. * Get a TimeRange object representing the current ranges of time that the user
  19978. * has played.
  19979. *
  19980. * @return {TimeRange}
  19981. * A time range object that represents all the increments of time that have
  19982. * been played.
  19983. */
  19984. Player.prototype.played = function played() {
  19985. return this.techGet_('played') || createTimeRanges(0, 0);
  19986. };
  19987. /**
  19988. * Returns whether or not the user is "scrubbing". Scrubbing is
  19989. * when the user has clicked the progress bar handle and is
  19990. * dragging it along the progress bar.
  19991. *
  19992. * @param {boolean} [isScrubbing]
  19993. * wether the user is or is not scrubbing
  19994. *
  19995. * @return {boolean}
  19996. * The value of scrubbing when getting
  19997. */
  19998. Player.prototype.scrubbing = function scrubbing(isScrubbing) {
  19999. if (typeof isScrubbing === 'undefined') {
  20000. return this.scrubbing_;
  20001. }
  20002. this.scrubbing_ = !!isScrubbing;
  20003. if (isScrubbing) {
  20004. this.addClass('vjs-scrubbing');
  20005. } else {
  20006. this.removeClass('vjs-scrubbing');
  20007. }
  20008. };
  20009. /**
  20010. * Get or set the current time (in seconds)
  20011. *
  20012. * @param {number|string} [seconds]
  20013. * The time to seek to in seconds
  20014. *
  20015. * @return {number}
  20016. * - the current time in seconds when getting
  20017. */
  20018. Player.prototype.currentTime = function currentTime(seconds) {
  20019. if (typeof seconds !== 'undefined') {
  20020. if (seconds < 0) {
  20021. seconds = 0;
  20022. }
  20023. this.techCall_('setCurrentTime', seconds);
  20024. return;
  20025. }
  20026. // cache last currentTime and return. default to 0 seconds
  20027. //
  20028. // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
  20029. // currentTime when scrubbing, but may not provide much performance benefit afterall.
  20030. // Should be tested. Also something has to read the actual current time or the cache will
  20031. // never get updated.
  20032. this.cache_.currentTime = this.techGet_('currentTime') || 0;
  20033. return this.cache_.currentTime;
  20034. };
  20035. /**
  20036. * Normally gets the length in time of the video in seconds;
  20037. * in all but the rarest use cases an argument will NOT be passed to the method
  20038. *
  20039. * > **NOTE**: The video must have started loading before the duration can be
  20040. * known, and in the case of Flash, may not be known until the video starts
  20041. * playing.
  20042. *
  20043. * @fires Player#durationchange
  20044. *
  20045. * @param {number} [seconds]
  20046. * The duration of the video to set in seconds
  20047. *
  20048. * @return {number}
  20049. * - The duration of the video in seconds when getting
  20050. */
  20051. Player.prototype.duration = function duration(seconds) {
  20052. if (seconds === undefined) {
  20053. // return NaN if the duration is not known
  20054. return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
  20055. }
  20056. seconds = parseFloat(seconds);
  20057. // Standardize on Inifity for signaling video is live
  20058. if (seconds < 0) {
  20059. seconds = Infinity;
  20060. }
  20061. if (seconds !== this.cache_.duration) {
  20062. // Cache the last set value for optimized scrubbing (esp. Flash)
  20063. this.cache_.duration = seconds;
  20064. if (seconds === Infinity) {
  20065. this.addClass('vjs-live');
  20066. } else {
  20067. this.removeClass('vjs-live');
  20068. }
  20069. /**
  20070. * @event Player#durationchange
  20071. * @type {EventTarget~Event}
  20072. */
  20073. this.trigger('durationchange');
  20074. }
  20075. };
  20076. /**
  20077. * Calculates how much time is left in the video. Not part
  20078. * of the native video API.
  20079. *
  20080. * @return {number}
  20081. * The time remaining in seconds
  20082. */
  20083. Player.prototype.remainingTime = function remainingTime() {
  20084. return this.duration() - this.currentTime();
  20085. };
  20086. /**
  20087. * A remaining time function that is intented to be used when
  20088. * the time is to be displayed directly to the user.
  20089. *
  20090. * @return {number}
  20091. * The rounded time remaining in seconds
  20092. */
  20093. Player.prototype.remainingTimeDisplay = function remainingTimeDisplay() {
  20094. return Math.floor(this.duration()) - Math.floor(this.currentTime());
  20095. };
  20096. //
  20097. // Kind of like an array of portions of the video that have been downloaded.
  20098. /**
  20099. * Get a TimeRange object with an array of the times of the video
  20100. * that have been downloaded. If you just want the percent of the
  20101. * video that's been downloaded, use bufferedPercent.
  20102. *
  20103. * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
  20104. *
  20105. * @return {TimeRange}
  20106. * A mock TimeRange object (following HTML spec)
  20107. */
  20108. Player.prototype.buffered = function buffered() {
  20109. var buffered = this.techGet_('buffered');
  20110. if (!buffered || !buffered.length) {
  20111. buffered = createTimeRanges(0, 0);
  20112. }
  20113. return buffered;
  20114. };
  20115. /**
  20116. * Get the percent (as a decimal) of the video that's been downloaded.
  20117. * This method is not a part of the native HTML video API.
  20118. *
  20119. * @return {number}
  20120. * A decimal between 0 and 1 representing the percent
  20121. * that is bufferred 0 being 0% and 1 being 100%
  20122. */
  20123. Player.prototype.bufferedPercent = function bufferedPercent$$1() {
  20124. return bufferedPercent(this.buffered(), this.duration());
  20125. };
  20126. /**
  20127. * Get the ending time of the last buffered time range
  20128. * This is used in the progress bar to encapsulate all time ranges.
  20129. *
  20130. * @return {number}
  20131. * The end of the last buffered time range
  20132. */
  20133. Player.prototype.bufferedEnd = function bufferedEnd() {
  20134. var buffered = this.buffered();
  20135. var duration = this.duration();
  20136. var end = buffered.end(buffered.length - 1);
  20137. if (end > duration) {
  20138. end = duration;
  20139. }
  20140. return end;
  20141. };
  20142. /**
  20143. * Get or set the current volume of the media
  20144. *
  20145. * @param {number} [percentAsDecimal]
  20146. * The new volume as a decimal percent:
  20147. * - 0 is muted/0%/off
  20148. * - 1.0 is 100%/full
  20149. * - 0.5 is half volume or 50%
  20150. *
  20151. * @return {number}
  20152. * The current volume as a percent when getting
  20153. */
  20154. Player.prototype.volume = function volume(percentAsDecimal) {
  20155. var vol = void 0;
  20156. if (percentAsDecimal !== undefined) {
  20157. // Force value to between 0 and 1
  20158. vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
  20159. this.cache_.volume = vol;
  20160. this.techCall_('setVolume', vol);
  20161. if (vol > 0) {
  20162. this.lastVolume_(vol);
  20163. }
  20164. return;
  20165. }
  20166. // Default to 1 when returning current volume.
  20167. vol = parseFloat(this.techGet_('volume'));
  20168. return isNaN(vol) ? 1 : vol;
  20169. };
  20170. /**
  20171. * Get the current muted state, or turn mute on or off
  20172. *
  20173. * @param {boolean} [muted]
  20174. * - true to mute
  20175. * - false to unmute
  20176. *
  20177. * @return {boolean}
  20178. * - true if mute is on and getting
  20179. * - false if mute is off and getting
  20180. */
  20181. Player.prototype.muted = function muted(_muted) {
  20182. if (_muted !== undefined) {
  20183. this.techCall_('setMuted', _muted);
  20184. return;
  20185. }
  20186. return this.techGet_('muted') || false;
  20187. };
  20188. /**
  20189. * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
  20190. * indicates the state of muted on intial playback.
  20191. *
  20192. * ```js
  20193. * var myPlayer = videojs('some-player-id');
  20194. *
  20195. * myPlayer.src("http://www.example.com/path/to/video.mp4");
  20196. *
  20197. * // get, should be false
  20198. * console.log(myPlayer.defaultMuted());
  20199. * // set to true
  20200. * myPlayer.defaultMuted(true);
  20201. * // get should be true
  20202. * console.log(myPlayer.defaultMuted());
  20203. * ```
  20204. *
  20205. * @param {boolean} [defaultMuted]
  20206. * - true to mute
  20207. * - false to unmute
  20208. *
  20209. * @return {boolean|Player}
  20210. * - true if defaultMuted is on and getting
  20211. * - false if defaultMuted is off and getting
  20212. * - A reference to the current player when setting
  20213. */
  20214. Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) {
  20215. if (_defaultMuted !== undefined) {
  20216. return this.techCall_('setDefaultMuted', _defaultMuted);
  20217. }
  20218. return this.techGet_('defaultMuted') || false;
  20219. };
  20220. /**
  20221. * Get the last volume, or set it
  20222. *
  20223. * @param {number} [percentAsDecimal]
  20224. * The new last volume as a decimal percent:
  20225. * - 0 is muted/0%/off
  20226. * - 1.0 is 100%/full
  20227. * - 0.5 is half volume or 50%
  20228. *
  20229. * @return {number}
  20230. * the current value of lastVolume as a percent when getting
  20231. *
  20232. * @private
  20233. */
  20234. Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) {
  20235. if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
  20236. this.cache_.lastVolume = percentAsDecimal;
  20237. return;
  20238. }
  20239. return this.cache_.lastVolume;
  20240. };
  20241. /**
  20242. * Check if current tech can support native fullscreen
  20243. * (e.g. with built in controls like iOS, so not our flash swf)
  20244. *
  20245. * @return {boolean}
  20246. * if native fullscreen is supported
  20247. */
  20248. Player.prototype.supportsFullScreen = function supportsFullScreen() {
  20249. return this.techGet_('supportsFullScreen') || false;
  20250. };
  20251. /**
  20252. * Check if the player is in fullscreen mode or tell the player that it
  20253. * is or is not in fullscreen mode.
  20254. *
  20255. * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
  20256. * property and instead document.fullscreenElement is used. But isFullscreen is
  20257. * still a valuable property for internal player workings.
  20258. *
  20259. * @param {boolean} [isFS]
  20260. * Set the players current fullscreen state
  20261. *
  20262. * @return {boolean}
  20263. * - true if fullscreen is on and getting
  20264. * - false if fullscreen is off and getting
  20265. */
  20266. Player.prototype.isFullscreen = function isFullscreen(isFS) {
  20267. if (isFS !== undefined) {
  20268. this.isFullscreen_ = !!isFS;
  20269. return;
  20270. }
  20271. return !!this.isFullscreen_;
  20272. };
  20273. /**
  20274. * Increase the size of the video to full screen
  20275. * In some browsers, full screen is not supported natively, so it enters
  20276. * "full window mode", where the video fills the browser window.
  20277. * In browsers and devices that support native full screen, sometimes the
  20278. * browser's default controls will be shown, and not the Video.js custom skin.
  20279. * This includes most mobile devices (iOS, Android) and older versions of
  20280. * Safari.
  20281. *
  20282. * @fires Player#fullscreenchange
  20283. */
  20284. Player.prototype.requestFullscreen = function requestFullscreen() {
  20285. var fsApi = FullscreenApi;
  20286. this.isFullscreen(true);
  20287. if (fsApi.requestFullscreen) {
  20288. // the browser supports going fullscreen at the element level so we can
  20289. // take the controls fullscreen as well as the video
  20290. // Trigger fullscreenchange event after change
  20291. // We have to specifically add this each time, and remove
  20292. // when canceling fullscreen. Otherwise if there's multiple
  20293. // players on a page, they would all be reacting to the same fullscreen
  20294. // events
  20295. on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) {
  20296. this.isFullscreen(document_1[fsApi.fullscreenElement]);
  20297. // If cancelling fullscreen, remove event listener.
  20298. if (this.isFullscreen() === false) {
  20299. off(document_1, fsApi.fullscreenchange, documentFullscreenChange);
  20300. }
  20301. /**
  20302. * @event Player#fullscreenchange
  20303. * @type {EventTarget~Event}
  20304. */
  20305. this.trigger('fullscreenchange');
  20306. }));
  20307. this.el_[fsApi.requestFullscreen]();
  20308. } else if (this.tech_.supportsFullScreen()) {
  20309. // we can't take the video.js controls fullscreen but we can go fullscreen
  20310. // with native controls
  20311. this.techCall_('enterFullScreen');
  20312. } else {
  20313. // fullscreen isn't supported so we'll just stretch the video element to
  20314. // fill the viewport
  20315. this.enterFullWindow();
  20316. /**
  20317. * @event Player#fullscreenchange
  20318. * @type {EventTarget~Event}
  20319. */
  20320. this.trigger('fullscreenchange');
  20321. }
  20322. };
  20323. /**
  20324. * Return the video to its normal size after having been in full screen mode
  20325. *
  20326. * @fires Player#fullscreenchange
  20327. */
  20328. Player.prototype.exitFullscreen = function exitFullscreen() {
  20329. var fsApi = FullscreenApi;
  20330. this.isFullscreen(false);
  20331. // Check for browser element fullscreen support
  20332. if (fsApi.requestFullscreen) {
  20333. document_1[fsApi.exitFullscreen]();
  20334. } else if (this.tech_.supportsFullScreen()) {
  20335. this.techCall_('exitFullScreen');
  20336. } else {
  20337. this.exitFullWindow();
  20338. /**
  20339. * @event Player#fullscreenchange
  20340. * @type {EventTarget~Event}
  20341. */
  20342. this.trigger('fullscreenchange');
  20343. }
  20344. };
  20345. /**
  20346. * When fullscreen isn't supported we can stretch the
  20347. * video container to as wide as the browser will let us.
  20348. *
  20349. * @fires Player#enterFullWindow
  20350. */
  20351. Player.prototype.enterFullWindow = function enterFullWindow() {
  20352. this.isFullWindow = true;
  20353. // Storing original doc overflow value to return to when fullscreen is off
  20354. this.docOrigOverflow = document_1.documentElement.style.overflow;
  20355. // Add listener for esc key to exit fullscreen
  20356. on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey));
  20357. // Hide any scroll bars
  20358. document_1.documentElement.style.overflow = 'hidden';
  20359. // Apply fullscreen styles
  20360. addClass(document_1.body, 'vjs-full-window');
  20361. /**
  20362. * @event Player#enterFullWindow
  20363. * @type {EventTarget~Event}
  20364. */
  20365. this.trigger('enterFullWindow');
  20366. };
  20367. /**
  20368. * Check for call to either exit full window or
  20369. * full screen on ESC key
  20370. *
  20371. * @param {string} event
  20372. * Event to check for key press
  20373. */
  20374. Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
  20375. if (event.keyCode === 27) {
  20376. if (this.isFullscreen() === true) {
  20377. this.exitFullscreen();
  20378. } else {
  20379. this.exitFullWindow();
  20380. }
  20381. }
  20382. };
  20383. /**
  20384. * Exit full window
  20385. *
  20386. * @fires Player#exitFullWindow
  20387. */
  20388. Player.prototype.exitFullWindow = function exitFullWindow() {
  20389. this.isFullWindow = false;
  20390. off(document_1, 'keydown', this.fullWindowOnEscKey);
  20391. // Unhide scroll bars.
  20392. document_1.documentElement.style.overflow = this.docOrigOverflow;
  20393. // Remove fullscreen styles
  20394. removeClass(document_1.body, 'vjs-full-window');
  20395. // Resize the box, controller, and poster to original sizes
  20396. // this.positionAll();
  20397. /**
  20398. * @event Player#exitFullWindow
  20399. * @type {EventTarget~Event}
  20400. */
  20401. this.trigger('exitFullWindow');
  20402. };
  20403. /**
  20404. * Check whether the player can play a given mimetype
  20405. *
  20406. * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
  20407. *
  20408. * @param {string} type
  20409. * The mimetype to check
  20410. *
  20411. * @return {string}
  20412. * 'probably', 'maybe', or '' (empty string)
  20413. */
  20414. Player.prototype.canPlayType = function canPlayType(type) {
  20415. var can = void 0;
  20416. // Loop through each playback technology in the options order
  20417. for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
  20418. var techName = j[i];
  20419. var tech = Tech.getTech(techName);
  20420. // Support old behavior of techs being registered as components.
  20421. // Remove once that deprecated behavior is removed.
  20422. if (!tech) {
  20423. tech = Component.getComponent(techName);
  20424. }
  20425. // Check if the current tech is defined before continuing
  20426. if (!tech) {
  20427. log.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
  20428. continue;
  20429. }
  20430. // Check if the browser supports this technology
  20431. if (tech.isSupported()) {
  20432. can = tech.canPlayType(type);
  20433. if (can) {
  20434. return can;
  20435. }
  20436. }
  20437. }
  20438. return '';
  20439. };
  20440. /**
  20441. * Select source based on tech-order or source-order
  20442. * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
  20443. * defaults to tech-order selection
  20444. *
  20445. * @param {Array} sources
  20446. * The sources for a media asset
  20447. *
  20448. * @return {Object|boolean}
  20449. * Object of source and tech order or false
  20450. */
  20451. Player.prototype.selectSource = function selectSource(sources) {
  20452. var _this9 = this;
  20453. // Get only the techs specified in `techOrder` that exist and are supported by the
  20454. // current platform
  20455. var techs = this.options_.techOrder.map(function (techName) {
  20456. return [techName, Tech.getTech(techName)];
  20457. }).filter(function (_ref) {
  20458. var techName = _ref[0],
  20459. tech = _ref[1];
  20460. // Check if the current tech is defined before continuing
  20461. if (tech) {
  20462. // Check if the browser supports this technology
  20463. return tech.isSupported();
  20464. }
  20465. log.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
  20466. return false;
  20467. });
  20468. // Iterate over each `innerArray` element once per `outerArray` element and execute
  20469. // `tester` with both. If `tester` returns a non-falsy value, exit early and return
  20470. // that value.
  20471. var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
  20472. var found = void 0;
  20473. outerArray.some(function (outerChoice) {
  20474. return innerArray.some(function (innerChoice) {
  20475. found = tester(outerChoice, innerChoice);
  20476. if (found) {
  20477. return true;
  20478. }
  20479. });
  20480. });
  20481. return found;
  20482. };
  20483. var foundSourceAndTech = void 0;
  20484. var flip = function flip(fn) {
  20485. return function (a, b) {
  20486. return fn(b, a);
  20487. };
  20488. };
  20489. var finder = function finder(_ref2, source) {
  20490. var techName = _ref2[0],
  20491. tech = _ref2[1];
  20492. if (tech.canPlaySource(source, _this9.options_[techName.toLowerCase()])) {
  20493. return { source: source, tech: techName };
  20494. }
  20495. };
  20496. // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
  20497. // to select from them based on their priority.
  20498. if (this.options_.sourceOrder) {
  20499. // Source-first ordering
  20500. foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
  20501. } else {
  20502. // Tech-first ordering
  20503. foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
  20504. }
  20505. return foundSourceAndTech || false;
  20506. };
  20507. /**
  20508. * Get or set the video source.
  20509. *
  20510. * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
  20511. * A SourceObject, an array of SourceObjects, or a string referencing
  20512. * a URL to a media source. It is _highly recommended_ that an object
  20513. * or array of objects is used here, so that source selection
  20514. * algorithms can take the `type` into account.
  20515. *
  20516. * If not provided, this method acts as a getter.
  20517. *
  20518. * @return {string|undefined}
  20519. * If the `source` argument is missing, returns the current source
  20520. * URL. Otherwise, returns nothing/undefined.
  20521. */
  20522. Player.prototype.src = function src(source) {
  20523. var _this10 = this;
  20524. // getter usage
  20525. if (typeof source === 'undefined') {
  20526. return this.cache_.src || '';
  20527. }
  20528. // filter out invalid sources and turn our source into
  20529. // an array of source objects
  20530. var sources = filterSource(source);
  20531. // if a source was passed in then it is invalid because
  20532. // it was filtered to a zero length Array. So we have to
  20533. // show an error
  20534. if (!sources.length) {
  20535. this.setTimeout(function () {
  20536. this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
  20537. }, 0);
  20538. return;
  20539. }
  20540. // intial sources
  20541. this.changingSrc_ = true;
  20542. this.cache_.sources = sources;
  20543. this.updateSourceCaches_(sources[0]);
  20544. // middlewareSource is the source after it has been changed by middleware
  20545. setSource(this, sources[0], function (middlewareSource, mws) {
  20546. _this10.middleware_ = mws;
  20547. // since sourceSet is async we have to update the cache again after we select a source since
  20548. // the source that is selected could be out of order from the cache update above this callback.
  20549. _this10.cache_.sources = sources;
  20550. _this10.updateSourceCaches_(middlewareSource);
  20551. var err = _this10.src_(middlewareSource);
  20552. if (err) {
  20553. if (sources.length > 1) {
  20554. return _this10.src(sources.slice(1));
  20555. }
  20556. _this10.changingSrc_ = false;
  20557. // We need to wrap this in a timeout to give folks a chance to add error event handlers
  20558. _this10.setTimeout(function () {
  20559. this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
  20560. }, 0);
  20561. // we could not find an appropriate tech, but let's still notify the delegate that this is it
  20562. // this needs a better comment about why this is needed
  20563. _this10.triggerReady();
  20564. return;
  20565. }
  20566. setTech(mws, _this10.tech_);
  20567. });
  20568. };
  20569. /**
  20570. * Set the source object on the tech, returns a boolean that indicates wether
  20571. * there is a tech that can play the source or not
  20572. *
  20573. * @param {Tech~SourceObject} source
  20574. * The source object to set on the Tech
  20575. *
  20576. * @return {Boolean}
  20577. * - True if there is no Tech to playback this source
  20578. * - False otherwise
  20579. *
  20580. * @private
  20581. */
  20582. Player.prototype.src_ = function src_(source) {
  20583. var _this11 = this;
  20584. var sourceTech = this.selectSource([source]);
  20585. if (!sourceTech) {
  20586. return true;
  20587. }
  20588. if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
  20589. this.changingSrc_ = true;
  20590. // load this technology with the chosen source
  20591. this.loadTech_(sourceTech.tech, sourceTech.source);
  20592. this.tech_.ready(function () {
  20593. _this11.changingSrc_ = false;
  20594. });
  20595. return false;
  20596. }
  20597. // wait until the tech is ready to set the source
  20598. // and set it synchronously if possible (#2326)
  20599. this.ready(function () {
  20600. // The setSource tech method was added with source handlers
  20601. // so older techs won't support it
  20602. // We need to check the direct prototype for the case where subclasses
  20603. // of the tech do not support source handlers
  20604. if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
  20605. this.techCall_('setSource', source);
  20606. } else {
  20607. this.techCall_('src', source.src);
  20608. }
  20609. this.changingSrc_ = false;
  20610. }, true);
  20611. return false;
  20612. };
  20613. /**
  20614. * Begin loading the src data.
  20615. */
  20616. Player.prototype.load = function load() {
  20617. this.techCall_('load');
  20618. };
  20619. /**
  20620. * Reset the player. Loads the first tech in the techOrder,
  20621. * removes all the text tracks in the existing `tech`,
  20622. * and calls `reset` on the `tech`.
  20623. */
  20624. Player.prototype.reset = function reset() {
  20625. if (this.tech_) {
  20626. this.tech_.clearTracks('text');
  20627. }
  20628. this.loadTech_(this.options_.techOrder[0], null);
  20629. this.techCall_('reset');
  20630. };
  20631. /**
  20632. * Returns all of the current source objects.
  20633. *
  20634. * @return {Tech~SourceObject[]}
  20635. * The current source objects
  20636. */
  20637. Player.prototype.currentSources = function currentSources() {
  20638. var source = this.currentSource();
  20639. var sources = [];
  20640. // assume `{}` or `{ src }`
  20641. if (Object.keys(source).length !== 0) {
  20642. sources.push(source);
  20643. }
  20644. return this.cache_.sources || sources;
  20645. };
  20646. /**
  20647. * Returns the current source object.
  20648. *
  20649. * @return {Tech~SourceObject}
  20650. * The current source object
  20651. */
  20652. Player.prototype.currentSource = function currentSource() {
  20653. return this.cache_.source || {};
  20654. };
  20655. /**
  20656. * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
  20657. * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
  20658. *
  20659. * @return {string}
  20660. * The current source
  20661. */
  20662. Player.prototype.currentSrc = function currentSrc() {
  20663. return this.currentSource() && this.currentSource().src || '';
  20664. };
  20665. /**
  20666. * Get the current source type e.g. video/mp4
  20667. * This can allow you rebuild the current source object so that you could load the same
  20668. * source and tech later
  20669. *
  20670. * @return {string}
  20671. * The source MIME type
  20672. */
  20673. Player.prototype.currentType = function currentType() {
  20674. return this.currentSource() && this.currentSource().type || '';
  20675. };
  20676. /**
  20677. * Get or set the preload attribute
  20678. *
  20679. * @param {boolean} [value]
  20680. * - true means that we should preload
  20681. * - false maens that we should not preload
  20682. *
  20683. * @return {string}
  20684. * The preload attribute value when getting
  20685. */
  20686. Player.prototype.preload = function preload(value) {
  20687. if (value !== undefined) {
  20688. this.techCall_('setPreload', value);
  20689. this.options_.preload = value;
  20690. return;
  20691. }
  20692. return this.techGet_('preload');
  20693. };
  20694. /**
  20695. * Get or set the autoplay option. When this is a boolean it will
  20696. * modify the attribute on the tech. When this is a string the attribute on
  20697. * the tech will be removed and `Player` will handle autoplay on loadstarts.
  20698. *
  20699. * @param {boolean|string} [value]
  20700. * - true: autoplay using the browser behavior
  20701. * - false: do not autoplay
  20702. * - 'play': call play() on every loadstart
  20703. * - 'muted': call muted() then play() on every loadstart
  20704. * - 'any': call play() on every loadstart. if that fails call muted() then play().
  20705. * - *: values other than those listed here will be set `autoplay` to true
  20706. *
  20707. * @return {boolean|string}
  20708. * The current value of autoplay when getting
  20709. */
  20710. Player.prototype.autoplay = function autoplay(value) {
  20711. // getter usage
  20712. if (value === undefined) {
  20713. return this.options_.autoplay || false;
  20714. }
  20715. var techAutoplay = void 0;
  20716. // if the value is a valid string set it to that
  20717. if (typeof value === 'string' && /(any|play|muted)/.test(value)) {
  20718. this.options_.autoplay = value;
  20719. this.manualAutoplay_(value);
  20720. techAutoplay = false;
  20721. // any falsy value sets autoplay to false in the browser,
  20722. // lets do the same
  20723. } else if (!value) {
  20724. this.options_.autoplay = false;
  20725. // any other value (ie truthy) sets autoplay to true
  20726. } else {
  20727. this.options_.autoplay = true;
  20728. }
  20729. techAutoplay = techAutoplay || this.options_.autoplay;
  20730. // if we don't have a tech then we do not queue up
  20731. // a setAutoplay call on tech ready. We do this because the
  20732. // autoplay option will be passed in the constructor and we
  20733. // do not need to set it twice
  20734. if (this.tech_) {
  20735. this.techCall_('setAutoplay', techAutoplay);
  20736. }
  20737. };
  20738. /**
  20739. * Set or unset the playsinline attribute.
  20740. * Playsinline tells the browser that non-fullscreen playback is preferred.
  20741. *
  20742. * @param {boolean} [value]
  20743. * - true means that we should try to play inline by default
  20744. * - false means that we should use the browser's default playback mode,
  20745. * which in most cases is inline. iOS Safari is a notable exception
  20746. * and plays fullscreen by default.
  20747. *
  20748. * @return {string|Player}
  20749. * - the current value of playsinline
  20750. * - the player when setting
  20751. *
  20752. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  20753. */
  20754. Player.prototype.playsinline = function playsinline(value) {
  20755. if (value !== undefined) {
  20756. this.techCall_('setPlaysinline', value);
  20757. this.options_.playsinline = value;
  20758. return this;
  20759. }
  20760. return this.techGet_('playsinline');
  20761. };
  20762. /**
  20763. * Get or set the loop attribute on the video element.
  20764. *
  20765. * @param {boolean} [value]
  20766. * - true means that we should loop the video
  20767. * - false means that we should not loop the video
  20768. *
  20769. * @return {string}
  20770. * The current value of loop when getting
  20771. */
  20772. Player.prototype.loop = function loop(value) {
  20773. if (value !== undefined) {
  20774. this.techCall_('setLoop', value);
  20775. this.options_.loop = value;
  20776. return;
  20777. }
  20778. return this.techGet_('loop');
  20779. };
  20780. /**
  20781. * Get or set the poster image source url
  20782. *
  20783. * @fires Player#posterchange
  20784. *
  20785. * @param {string} [src]
  20786. * Poster image source URL
  20787. *
  20788. * @return {string}
  20789. * The current value of poster when getting
  20790. */
  20791. Player.prototype.poster = function poster(src) {
  20792. if (src === undefined) {
  20793. return this.poster_;
  20794. }
  20795. // The correct way to remove a poster is to set as an empty string
  20796. // other falsey values will throw errors
  20797. if (!src) {
  20798. src = '';
  20799. }
  20800. if (src === this.poster_) {
  20801. return;
  20802. }
  20803. // update the internal poster variable
  20804. this.poster_ = src;
  20805. // update the tech's poster
  20806. this.techCall_('setPoster', src);
  20807. this.isPosterFromTech_ = false;
  20808. // alert components that the poster has been set
  20809. /**
  20810. * This event fires when the poster image is changed on the player.
  20811. *
  20812. * @event Player#posterchange
  20813. * @type {EventTarget~Event}
  20814. */
  20815. this.trigger('posterchange');
  20816. };
  20817. /**
  20818. * Some techs (e.g. YouTube) can provide a poster source in an
  20819. * asynchronous way. We want the poster component to use this
  20820. * poster source so that it covers up the tech's controls.
  20821. * (YouTube's play button). However we only want to use this
  20822. * source if the player user hasn't set a poster through
  20823. * the normal APIs.
  20824. *
  20825. * @fires Player#posterchange
  20826. * @listens Tech#posterchange
  20827. * @private
  20828. */
  20829. Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
  20830. if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
  20831. var newPoster = this.tech_.poster() || '';
  20832. if (newPoster !== this.poster_) {
  20833. this.poster_ = newPoster;
  20834. this.isPosterFromTech_ = true;
  20835. // Let components know the poster has changed
  20836. this.trigger('posterchange');
  20837. }
  20838. }
  20839. };
  20840. /**
  20841. * Get or set whether or not the controls are showing.
  20842. *
  20843. * @fires Player#controlsenabled
  20844. *
  20845. * @param {boolean} [bool]
  20846. * - true to turn controls on
  20847. * - false to turn controls off
  20848. *
  20849. * @return {boolean}
  20850. * The current value of controls when getting
  20851. */
  20852. Player.prototype.controls = function controls(bool) {
  20853. if (bool === undefined) {
  20854. return !!this.controls_;
  20855. }
  20856. bool = !!bool;
  20857. // Don't trigger a change event unless it actually changed
  20858. if (this.controls_ === bool) {
  20859. return;
  20860. }
  20861. this.controls_ = bool;
  20862. if (this.usingNativeControls()) {
  20863. this.techCall_('setControls', bool);
  20864. }
  20865. if (this.controls_) {
  20866. this.removeClass('vjs-controls-disabled');
  20867. this.addClass('vjs-controls-enabled');
  20868. /**
  20869. * @event Player#controlsenabled
  20870. * @type {EventTarget~Event}
  20871. */
  20872. this.trigger('controlsenabled');
  20873. if (!this.usingNativeControls()) {
  20874. this.addTechControlsListeners_();
  20875. }
  20876. } else {
  20877. this.removeClass('vjs-controls-enabled');
  20878. this.addClass('vjs-controls-disabled');
  20879. /**
  20880. * @event Player#controlsdisabled
  20881. * @type {EventTarget~Event}
  20882. */
  20883. this.trigger('controlsdisabled');
  20884. if (!this.usingNativeControls()) {
  20885. this.removeTechControlsListeners_();
  20886. }
  20887. }
  20888. };
  20889. /**
  20890. * Toggle native controls on/off. Native controls are the controls built into
  20891. * devices (e.g. default iPhone controls), Flash, or other techs
  20892. * (e.g. Vimeo Controls)
  20893. * **This should only be set by the current tech, because only the tech knows
  20894. * if it can support native controls**
  20895. *
  20896. * @fires Player#usingnativecontrols
  20897. * @fires Player#usingcustomcontrols
  20898. *
  20899. * @param {boolean} [bool]
  20900. * - true to turn native controls on
  20901. * - false to turn native controls off
  20902. *
  20903. * @return {boolean}
  20904. * The current value of native controls when getting
  20905. */
  20906. Player.prototype.usingNativeControls = function usingNativeControls(bool) {
  20907. if (bool === undefined) {
  20908. return !!this.usingNativeControls_;
  20909. }
  20910. bool = !!bool;
  20911. // Don't trigger a change event unless it actually changed
  20912. if (this.usingNativeControls_ === bool) {
  20913. return;
  20914. }
  20915. this.usingNativeControls_ = bool;
  20916. if (this.usingNativeControls_) {
  20917. this.addClass('vjs-using-native-controls');
  20918. /**
  20919. * player is using the native device controls
  20920. *
  20921. * @event Player#usingnativecontrols
  20922. * @type {EventTarget~Event}
  20923. */
  20924. this.trigger('usingnativecontrols');
  20925. } else {
  20926. this.removeClass('vjs-using-native-controls');
  20927. /**
  20928. * player is using the custom HTML controls
  20929. *
  20930. * @event Player#usingcustomcontrols
  20931. * @type {EventTarget~Event}
  20932. */
  20933. this.trigger('usingcustomcontrols');
  20934. }
  20935. };
  20936. /**
  20937. * Set or get the current MediaError
  20938. *
  20939. * @fires Player#error
  20940. *
  20941. * @param {MediaError|string|number} [err]
  20942. * A MediaError or a string/number to be turned
  20943. * into a MediaError
  20944. *
  20945. * @return {MediaError|null}
  20946. * The current MediaError when getting (or null)
  20947. */
  20948. Player.prototype.error = function error(err) {
  20949. if (err === undefined) {
  20950. return this.error_ || null;
  20951. }
  20952. // restoring to default
  20953. if (err === null) {
  20954. this.error_ = err;
  20955. this.removeClass('vjs-error');
  20956. if (this.errorDisplay) {
  20957. this.errorDisplay.close();
  20958. }
  20959. return;
  20960. }
  20961. this.error_ = new MediaError(err);
  20962. // add the vjs-error classname to the player
  20963. this.addClass('vjs-error');
  20964. // log the name of the error type and any message
  20965. // ie8 just logs "[object object]" if you just log the error object
  20966. log.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
  20967. /**
  20968. * @event Player#error
  20969. * @type {EventTarget~Event}
  20970. */
  20971. this.trigger('error');
  20972. return;
  20973. };
  20974. /**
  20975. * Report user activity
  20976. *
  20977. * @param {Object} event
  20978. * Event object
  20979. */
  20980. Player.prototype.reportUserActivity = function reportUserActivity(event) {
  20981. this.userActivity_ = true;
  20982. };
  20983. /**
  20984. * Get/set if user is active
  20985. *
  20986. * @fires Player#useractive
  20987. * @fires Player#userinactive
  20988. *
  20989. * @param {boolean} [bool]
  20990. * - true if the user is active
  20991. * - false if the user is inactive
  20992. *
  20993. * @return {boolean}
  20994. * The current value of userActive when getting
  20995. */
  20996. Player.prototype.userActive = function userActive(bool) {
  20997. if (bool === undefined) {
  20998. return this.userActive_;
  20999. }
  21000. bool = !!bool;
  21001. if (bool === this.userActive_) {
  21002. return;
  21003. }
  21004. this.userActive_ = bool;
  21005. if (this.userActive_) {
  21006. this.userActivity_ = true;
  21007. this.removeClass('vjs-user-inactive');
  21008. this.addClass('vjs-user-active');
  21009. /**
  21010. * @event Player#useractive
  21011. * @type {EventTarget~Event}
  21012. */
  21013. this.trigger('useractive');
  21014. return;
  21015. }
  21016. // Chrome/Safari/IE have bugs where when you change the cursor it can
  21017. // trigger a mousemove event. This causes an issue when you're hiding
  21018. // the cursor when the user is inactive, and a mousemove signals user
  21019. // activity. Making it impossible to go into inactive mode. Specifically
  21020. // this happens in fullscreen when we really need to hide the cursor.
  21021. //
  21022. // When this gets resolved in ALL browsers it can be removed
  21023. // https://code.google.com/p/chromium/issues/detail?id=103041
  21024. if (this.tech_) {
  21025. this.tech_.one('mousemove', function (e) {
  21026. e.stopPropagation();
  21027. e.preventDefault();
  21028. });
  21029. }
  21030. this.userActivity_ = false;
  21031. this.removeClass('vjs-user-active');
  21032. this.addClass('vjs-user-inactive');
  21033. /**
  21034. * @event Player#userinactive
  21035. * @type {EventTarget~Event}
  21036. */
  21037. this.trigger('userinactive');
  21038. };
  21039. /**
  21040. * Listen for user activity based on timeout value
  21041. *
  21042. * @private
  21043. */
  21044. Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
  21045. var mouseInProgress = void 0;
  21046. var lastMoveX = void 0;
  21047. var lastMoveY = void 0;
  21048. var handleActivity = bind(this, this.reportUserActivity);
  21049. var handleMouseMove = function handleMouseMove(e) {
  21050. // #1068 - Prevent mousemove spamming
  21051. // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
  21052. if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
  21053. lastMoveX = e.screenX;
  21054. lastMoveY = e.screenY;
  21055. handleActivity();
  21056. }
  21057. };
  21058. var handleMouseDown = function handleMouseDown() {
  21059. handleActivity();
  21060. // For as long as the they are touching the device or have their mouse down,
  21061. // we consider them active even if they're not moving their finger or mouse.
  21062. // So we want to continue to update that they are active
  21063. this.clearInterval(mouseInProgress);
  21064. // Setting userActivity=true now and setting the interval to the same time
  21065. // as the activityCheck interval (250) should ensure we never miss the
  21066. // next activityCheck
  21067. mouseInProgress = this.setInterval(handleActivity, 250);
  21068. };
  21069. var handleMouseUp = function handleMouseUp(event) {
  21070. handleActivity();
  21071. // Stop the interval that maintains activity if the mouse/touch is down
  21072. this.clearInterval(mouseInProgress);
  21073. };
  21074. // Any mouse movement will be considered user activity
  21075. this.on('mousedown', handleMouseDown);
  21076. this.on('mousemove', handleMouseMove);
  21077. this.on('mouseup', handleMouseUp);
  21078. // Listen for keyboard navigation
  21079. // Shouldn't need to use inProgress interval because of key repeat
  21080. this.on('keydown', handleActivity);
  21081. this.on('keyup', handleActivity);
  21082. // Run an interval every 250 milliseconds instead of stuffing everything into
  21083. // the mousemove/touchmove function itself, to prevent performance degradation.
  21084. // `this.reportUserActivity` simply sets this.userActivity_ to true, which
  21085. // then gets picked up by this loop
  21086. // http://ejohn.org/blog/learning-from-twitter/
  21087. var inactivityTimeout = void 0;
  21088. this.setInterval(function () {
  21089. // Check to see if mouse/touch activity has happened
  21090. if (!this.userActivity_) {
  21091. return;
  21092. }
  21093. // Reset the activity tracker
  21094. this.userActivity_ = false;
  21095. // If the user state was inactive, set the state to active
  21096. this.userActive(true);
  21097. // Clear any existing inactivity timeout to start the timer over
  21098. this.clearTimeout(inactivityTimeout);
  21099. var timeout = this.options_.inactivityTimeout;
  21100. if (timeout <= 0) {
  21101. return;
  21102. }
  21103. // In <timeout> milliseconds, if no more activity has occurred the
  21104. // user will be considered inactive
  21105. inactivityTimeout = this.setTimeout(function () {
  21106. // Protect against the case where the inactivityTimeout can trigger just
  21107. // before the next user activity is picked up by the activity check loop
  21108. // causing a flicker
  21109. if (!this.userActivity_) {
  21110. this.userActive(false);
  21111. }
  21112. }, timeout);
  21113. }, 250);
  21114. };
  21115. /**
  21116. * Gets or sets the current playback rate. A playback rate of
  21117. * 1.0 represents normal speed and 0.5 would indicate half-speed
  21118. * playback, for instance.
  21119. *
  21120. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
  21121. *
  21122. * @param {number} [rate]
  21123. * New playback rate to set.
  21124. *
  21125. * @return {number}
  21126. * The current playback rate when getting or 1.0
  21127. */
  21128. Player.prototype.playbackRate = function playbackRate(rate) {
  21129. if (rate !== undefined) {
  21130. // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
  21131. // that is registered above
  21132. this.techCall_('setPlaybackRate', rate);
  21133. return;
  21134. }
  21135. if (this.tech_ && this.tech_.featuresPlaybackRate) {
  21136. return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
  21137. }
  21138. return 1.0;
  21139. };
  21140. /**
  21141. * Gets or sets the current default playback rate. A default playback rate of
  21142. * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
  21143. * defaultPlaybackRate will only represent what the intial playbackRate of a video was, not
  21144. * not the current playbackRate.
  21145. *
  21146. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
  21147. *
  21148. * @param {number} [rate]
  21149. * New default playback rate to set.
  21150. *
  21151. * @return {number|Player}
  21152. * - The default playback rate when getting or 1.0
  21153. * - the player when setting
  21154. */
  21155. Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) {
  21156. if (rate !== undefined) {
  21157. return this.techCall_('setDefaultPlaybackRate', rate);
  21158. }
  21159. if (this.tech_ && this.tech_.featuresPlaybackRate) {
  21160. return this.techGet_('defaultPlaybackRate');
  21161. }
  21162. return 1.0;
  21163. };
  21164. /**
  21165. * Gets or sets the audio flag
  21166. *
  21167. * @param {boolean} bool
  21168. * - true signals that this is an audio player
  21169. * - false signals that this is not an audio player
  21170. *
  21171. * @return {boolean}
  21172. * The current value of isAudio when getting
  21173. */
  21174. Player.prototype.isAudio = function isAudio(bool) {
  21175. if (bool !== undefined) {
  21176. this.isAudio_ = !!bool;
  21177. return;
  21178. }
  21179. return !!this.isAudio_;
  21180. };
  21181. /**
  21182. * A helper method for adding a {@link TextTrack} to our
  21183. * {@link TextTrackList}.
  21184. *
  21185. * In addition to the W3C settings we allow adding additional info through options.
  21186. *
  21187. * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
  21188. *
  21189. * @param {string} [kind]
  21190. * the kind of TextTrack you are adding
  21191. *
  21192. * @param {string} [label]
  21193. * the label to give the TextTrack label
  21194. *
  21195. * @param {string} [language]
  21196. * the language to set on the TextTrack
  21197. *
  21198. * @return {TextTrack|undefined}
  21199. * the TextTrack that was added or undefined
  21200. * if there is no tech
  21201. */
  21202. Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  21203. if (this.tech_) {
  21204. return this.tech_.addTextTrack(kind, label, language);
  21205. }
  21206. };
  21207. /**
  21208. * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
  21209. * automatically removed from the video element whenever the source changes, unless
  21210. * manualCleanup is set to false.
  21211. *
  21212. * @param {Object} options
  21213. * Options to pass to {@link HTMLTrackElement} during creation. See
  21214. * {@link HTMLTrackElement} for object properties that you should use.
  21215. *
  21216. * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
  21217. *
  21218. * @return {HtmlTrackElement}
  21219. * the HTMLTrackElement that was created and added
  21220. * to the HtmlTrackElementList and the remote
  21221. * TextTrackList
  21222. *
  21223. * @deprecated The default value of the "manualCleanup" parameter will default
  21224. * to "false" in upcoming versions of Video.js
  21225. */
  21226. Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
  21227. if (this.tech_) {
  21228. return this.tech_.addRemoteTextTrack(options, manualCleanup);
  21229. }
  21230. };
  21231. /**
  21232. * Remove a remote {@link TextTrack} from the respective
  21233. * {@link TextTrackList} and {@link HtmlTrackElementList}.
  21234. *
  21235. * @param {Object} track
  21236. * Remote {@link TextTrack} to remove
  21237. *
  21238. * @return {undefined}
  21239. * does not return anything
  21240. */
  21241. Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
  21242. var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
  21243. _ref3$track = _ref3.track,
  21244. track = _ref3$track === undefined ? arguments[0] : _ref3$track;
  21245. // destructure the input into an object with a track argument, defaulting to arguments[0]
  21246. // default the whole argument to an empty object if nothing was passed in
  21247. if (this.tech_) {
  21248. return this.tech_.removeRemoteTextTrack(track);
  21249. }
  21250. };
  21251. /**
  21252. * Gets available media playback quality metrics as specified by the W3C's Media
  21253. * Playback Quality API.
  21254. *
  21255. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  21256. *
  21257. * @return {Object|undefined}
  21258. * An object with supported media playback quality metrics or undefined if there
  21259. * is no tech or the tech does not support it.
  21260. */
  21261. Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  21262. return this.techGet_('getVideoPlaybackQuality');
  21263. };
  21264. /**
  21265. * Get video width
  21266. *
  21267. * @return {number}
  21268. * current video width
  21269. */
  21270. Player.prototype.videoWidth = function videoWidth() {
  21271. return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
  21272. };
  21273. /**
  21274. * Get video height
  21275. *
  21276. * @return {number}
  21277. * current video height
  21278. */
  21279. Player.prototype.videoHeight = function videoHeight() {
  21280. return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
  21281. };
  21282. /**
  21283. * The player's language code
  21284. * NOTE: The language should be set in the player options if you want the
  21285. * the controls to be built with a specific language. Changing the lanugage
  21286. * later will not update controls text.
  21287. *
  21288. * @param {string} [code]
  21289. * the language code to set the player to
  21290. *
  21291. * @return {string}
  21292. * The current language code when getting
  21293. */
  21294. Player.prototype.language = function language(code) {
  21295. if (code === undefined) {
  21296. return this.language_;
  21297. }
  21298. this.language_ = String(code).toLowerCase();
  21299. };
  21300. /**
  21301. * Get the player's language dictionary
  21302. * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
  21303. * Languages specified directly in the player options have precedence
  21304. *
  21305. * @return {Array}
  21306. * An array of of supported languages
  21307. */
  21308. Player.prototype.languages = function languages() {
  21309. return mergeOptions(Player.prototype.options_.languages, this.languages_);
  21310. };
  21311. /**
  21312. * returns a JavaScript object reperesenting the current track
  21313. * information. **DOES not return it as JSON**
  21314. *
  21315. * @return {Object}
  21316. * Object representing the current of track info
  21317. */
  21318. Player.prototype.toJSON = function toJSON() {
  21319. var options = mergeOptions(this.options_);
  21320. var tracks = options.tracks;
  21321. options.tracks = [];
  21322. for (var i = 0; i < tracks.length; i++) {
  21323. var track = tracks[i];
  21324. // deep merge tracks and null out player so no circular references
  21325. track = mergeOptions(track);
  21326. track.player = undefined;
  21327. options.tracks[i] = track;
  21328. }
  21329. return options;
  21330. };
  21331. /**
  21332. * Creates a simple modal dialog (an instance of the {@link ModalDialog}
  21333. * component) that immediately overlays the player with arbitrary
  21334. * content and removes itself when closed.
  21335. *
  21336. * @param {string|Function|Element|Array|null} content
  21337. * Same as {@link ModalDialog#content}'s param of the same name.
  21338. * The most straight-forward usage is to provide a string or DOM
  21339. * element.
  21340. *
  21341. * @param {Object} [options]
  21342. * Extra options which will be passed on to the {@link ModalDialog}.
  21343. *
  21344. * @return {ModalDialog}
  21345. * the {@link ModalDialog} that was created
  21346. */
  21347. Player.prototype.createModal = function createModal(content, options) {
  21348. var _this12 = this;
  21349. options = options || {};
  21350. options.content = content || '';
  21351. var modal = new ModalDialog(this, options);
  21352. this.addChild(modal);
  21353. modal.on('dispose', function () {
  21354. _this12.removeChild(modal);
  21355. });
  21356. modal.open();
  21357. return modal;
  21358. };
  21359. /**
  21360. * Change breakpoint classes when the player resizes.
  21361. *
  21362. * @private
  21363. */
  21364. Player.prototype.updateCurrentBreakpoint_ = function updateCurrentBreakpoint_() {
  21365. if (!this.responsive()) {
  21366. return;
  21367. }
  21368. var currentBreakpoint = this.currentBreakpoint();
  21369. var currentWidth = this.currentWidth();
  21370. for (var i = 0; i < BREAKPOINT_ORDER.length; i++) {
  21371. var candidateBreakpoint = BREAKPOINT_ORDER[i];
  21372. var maxWidth = this.breakpoints_[candidateBreakpoint];
  21373. if (currentWidth <= maxWidth) {
  21374. // The current breakpoint did not change, nothing to do.
  21375. if (currentBreakpoint === candidateBreakpoint) {
  21376. return;
  21377. }
  21378. // Only remove a class if there is a current breakpoint.
  21379. if (currentBreakpoint) {
  21380. this.removeClass(BREAKPOINT_CLASSES[currentBreakpoint]);
  21381. }
  21382. this.addClass(BREAKPOINT_CLASSES[candidateBreakpoint]);
  21383. this.breakpoint_ = candidateBreakpoint;
  21384. break;
  21385. }
  21386. }
  21387. };
  21388. /**
  21389. * Removes the current breakpoint.
  21390. *
  21391. * @private
  21392. */
  21393. Player.prototype.removeCurrentBreakpoint_ = function removeCurrentBreakpoint_() {
  21394. var className = this.currentBreakpointClass();
  21395. this.breakpoint_ = '';
  21396. if (className) {
  21397. this.removeClass(className);
  21398. }
  21399. };
  21400. /**
  21401. * Get or set breakpoints on the player.
  21402. *
  21403. * Calling this method with an object or `true` will remove any previous
  21404. * custom breakpoints and start from the defaults again.
  21405. *
  21406. * @param {Object|boolean} [breakpoints]
  21407. * If an object is given, it can be used to provide custom
  21408. * breakpoints. If `true` is given, will set default breakpoints.
  21409. * If this argument is not given, will simply return the current
  21410. * breakpoints.
  21411. *
  21412. * @param {number} [breakpoints.tiny]
  21413. * The maximum width for the "vjs-layout-tiny" class.
  21414. *
  21415. * @param {number} [breakpoints.xsmall]
  21416. * The maximum width for the "vjs-layout-x-small" class.
  21417. *
  21418. * @param {number} [breakpoints.small]
  21419. * The maximum width for the "vjs-layout-small" class.
  21420. *
  21421. * @param {number} [breakpoints.medium]
  21422. * The maximum width for the "vjs-layout-medium" class.
  21423. *
  21424. * @param {number} [breakpoints.large]
  21425. * The maximum width for the "vjs-layout-large" class.
  21426. *
  21427. * @param {number} [breakpoints.xlarge]
  21428. * The maximum width for the "vjs-layout-x-large" class.
  21429. *
  21430. * @param {number} [breakpoints.huge]
  21431. * The maximum width for the "vjs-layout-huge" class.
  21432. *
  21433. * @return {Object}
  21434. * An object mapping breakpoint names to maximum width values.
  21435. */
  21436. Player.prototype.breakpoints = function breakpoints(_breakpoints) {
  21437. // Used as a getter.
  21438. if (_breakpoints === undefined) {
  21439. return assign(this.breakpoints_);
  21440. }
  21441. this.breakpoint_ = '';
  21442. this.breakpoints_ = assign({}, DEFAULT_BREAKPOINTS, _breakpoints);
  21443. // When breakpoint definitions change, we need to update the currently
  21444. // selected breakpoint.
  21445. this.updateCurrentBreakpoint_();
  21446. // Clone the breakpoints before returning.
  21447. return assign(this.breakpoints_);
  21448. };
  21449. /**
  21450. * Get or set a flag indicating whether or not this player should adjust
  21451. * its UI based on its dimensions.
  21452. *
  21453. * @param {boolean} value
  21454. * Should be `true` if the player should adjust its UI based on its
  21455. * dimensions; otherwise, should be `false`.
  21456. *
  21457. * @return {boolean}
  21458. * Will be `true` if this player should adjust its UI based on its
  21459. * dimensions; otherwise, will be `false`.
  21460. */
  21461. Player.prototype.responsive = function responsive(value) {
  21462. // Used as a getter.
  21463. if (value === undefined) {
  21464. return this.responsive_;
  21465. }
  21466. value = Boolean(value);
  21467. var current = this.responsive_;
  21468. // Nothing changed.
  21469. if (value === current) {
  21470. return;
  21471. }
  21472. // The value actually changed, set it.
  21473. this.responsive_ = value;
  21474. // Start listening for breakpoints and set the initial breakpoint if the
  21475. // player is now responsive.
  21476. if (value) {
  21477. this.on('playerresize', this.updateCurrentBreakpoint_);
  21478. this.updateCurrentBreakpoint_();
  21479. // Stop listening for breakpoints if the player is no longer responsive.
  21480. } else {
  21481. this.off('playerresize', this.updateCurrentBreakpoint_);
  21482. this.removeCurrentBreakpoint_();
  21483. }
  21484. return value;
  21485. };
  21486. /**
  21487. * Get current breakpoint name, if any.
  21488. *
  21489. * @return {string}
  21490. * If there is currently a breakpoint set, returns a the key from the
  21491. * breakpoints object matching it. Otherwise, returns an empty string.
  21492. */
  21493. Player.prototype.currentBreakpoint = function currentBreakpoint() {
  21494. return this.breakpoint_;
  21495. };
  21496. /**
  21497. * Get the current breakpoint class name.
  21498. *
  21499. * @return {string}
  21500. * The matching class name (e.g. `"vjs-layout-tiny"` or
  21501. * `"vjs-layout-large"`) for the current breakpoint. Empty string if
  21502. * there is no current breakpoint.
  21503. */
  21504. Player.prototype.currentBreakpointClass = function currentBreakpointClass() {
  21505. return BREAKPOINT_CLASSES[this.breakpoint_] || '';
  21506. };
  21507. /**
  21508. * Gets tag settings
  21509. *
  21510. * @param {Element} tag
  21511. * The player tag
  21512. *
  21513. * @return {Object}
  21514. * An object containing all of the settings
  21515. * for a player tag
  21516. */
  21517. Player.getTagSettings = function getTagSettings(tag) {
  21518. var baseOptions = {
  21519. sources: [],
  21520. tracks: []
  21521. };
  21522. var tagOptions = getAttributes(tag);
  21523. var dataSetup = tagOptions['data-setup'];
  21524. if (hasClass(tag, 'vjs-fill')) {
  21525. tagOptions.fill = true;
  21526. }
  21527. if (hasClass(tag, 'vjs-fluid')) {
  21528. tagOptions.fluid = true;
  21529. }
  21530. // Check if data-setup attr exists.
  21531. if (dataSetup !== null) {
  21532. // Parse options JSON
  21533. // If empty string, make it a parsable json object.
  21534. var _safeParseTuple = tuple(dataSetup || '{}'),
  21535. err = _safeParseTuple[0],
  21536. data = _safeParseTuple[1];
  21537. if (err) {
  21538. log.error(err);
  21539. }
  21540. assign(tagOptions, data);
  21541. }
  21542. assign(baseOptions, tagOptions);
  21543. // Get tag children settings
  21544. if (tag.hasChildNodes()) {
  21545. var children = tag.childNodes;
  21546. for (var i = 0, j = children.length; i < j; i++) {
  21547. var child = children[i];
  21548. // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
  21549. var childName = child.nodeName.toLowerCase();
  21550. if (childName === 'source') {
  21551. baseOptions.sources.push(getAttributes(child));
  21552. } else if (childName === 'track') {
  21553. baseOptions.tracks.push(getAttributes(child));
  21554. }
  21555. }
  21556. }
  21557. return baseOptions;
  21558. };
  21559. /**
  21560. * Determine wether or not flexbox is supported
  21561. *
  21562. * @return {boolean}
  21563. * - true if flexbox is supported
  21564. * - false if flexbox is not supported
  21565. */
  21566. Player.prototype.flexNotSupported_ = function flexNotSupported_() {
  21567. var elem = document_1.createElement('i');
  21568. // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
  21569. // common flex features that we can rely on when checking for flex support.
  21570. return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
  21571. // IE10-specific (2012 flex spec)
  21572. 'msFlexOrder' in elem.style);
  21573. };
  21574. return Player;
  21575. }(Component);
  21576. /**
  21577. * Get the {@link VideoTrackList}
  21578. * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
  21579. *
  21580. * @return {VideoTrackList}
  21581. * the current video track list
  21582. *
  21583. * @method Player.prototype.videoTracks
  21584. */
  21585. /**
  21586. * Get the {@link AudioTrackList}
  21587. * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
  21588. *
  21589. * @return {AudioTrackList}
  21590. * the current audio track list
  21591. *
  21592. * @method Player.prototype.audioTracks
  21593. */
  21594. /**
  21595. * Get the {@link TextTrackList}
  21596. *
  21597. * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
  21598. *
  21599. * @return {TextTrackList}
  21600. * the current text track list
  21601. *
  21602. * @method Player.prototype.textTracks
  21603. */
  21604. /**
  21605. * Get the remote {@link TextTrackList}
  21606. *
  21607. * @return {TextTrackList}
  21608. * The current remote text track list
  21609. *
  21610. * @method Player.prototype.remoteTextTracks
  21611. */
  21612. /**
  21613. * Get the remote {@link HtmlTrackElementList} tracks.
  21614. *
  21615. * @return {HtmlTrackElementList}
  21616. * The current remote text track element list
  21617. *
  21618. * @method Player.prototype.remoteTextTrackEls
  21619. */
  21620. ALL.names.forEach(function (name$$1) {
  21621. var props = ALL[name$$1];
  21622. Player.prototype[props.getterName] = function () {
  21623. if (this.tech_) {
  21624. return this.tech_[props.getterName]();
  21625. }
  21626. // if we have not yet loadTech_, we create {video,audio,text}Tracks_
  21627. // these will be passed to the tech during loading
  21628. this[props.privateName] = this[props.privateName] || new props.ListClass();
  21629. return this[props.privateName];
  21630. };
  21631. });
  21632. /**
  21633. * Global player list
  21634. *
  21635. * @type {Object}
  21636. */
  21637. Player.players = {};
  21638. var navigator$1 = window_1.navigator;
  21639. /*
  21640. * Player instance options, surfaced using options
  21641. * options = Player.prototype.options_
  21642. * Make changes in options, not here.
  21643. *
  21644. * @type {Object}
  21645. * @private
  21646. */
  21647. Player.prototype.options_ = {
  21648. // Default order of fallback technology
  21649. techOrder: Tech.defaultTechOrder_,
  21650. html5: {},
  21651. flash: {},
  21652. // default inactivity timeout
  21653. inactivityTimeout: 2000,
  21654. // default playback rates
  21655. playbackRates: [],
  21656. // Add playback rate selection by adding rates
  21657. // 'playbackRates': [0.5, 1, 1.5, 2],
  21658. // Included control sets
  21659. children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
  21660. language: navigator$1 && (navigator$1.languages && navigator$1.languages[0] || navigator$1.userLanguage || navigator$1.language) || 'en',
  21661. // locales and their language translations
  21662. languages: {},
  21663. // Default message to show when a video cannot be played.
  21664. notSupportedMessage: 'No compatible source was found for this media.',
  21665. breakpoints: {},
  21666. responsive: false
  21667. };
  21668. if (!IS_IE8) {
  21669. Player.prototype.options_.children.push('resizeManager');
  21670. }
  21671. [
  21672. /**
  21673. * Returns whether or not the player is in the "ended" state.
  21674. *
  21675. * @return {Boolean} True if the player is in the ended state, false if not.
  21676. * @method Player#ended
  21677. */
  21678. 'ended',
  21679. /**
  21680. * Returns whether or not the player is in the "seeking" state.
  21681. *
  21682. * @return {Boolean} True if the player is in the seeking state, false if not.
  21683. * @method Player#seeking
  21684. */
  21685. 'seeking',
  21686. /**
  21687. * Returns the TimeRanges of the media that are currently available
  21688. * for seeking to.
  21689. *
  21690. * @return {TimeRanges} the seekable intervals of the media timeline
  21691. * @method Player#seekable
  21692. */
  21693. 'seekable',
  21694. /**
  21695. * Returns the current state of network activity for the element, from
  21696. * the codes in the list below.
  21697. * - NETWORK_EMPTY (numeric value 0)
  21698. * The element has not yet been initialised. All attributes are in
  21699. * their initial states.
  21700. * - NETWORK_IDLE (numeric value 1)
  21701. * The element's resource selection algorithm is active and has
  21702. * selected a resource, but it is not actually using the network at
  21703. * this time.
  21704. * - NETWORK_LOADING (numeric value 2)
  21705. * The user agent is actively trying to download data.
  21706. * - NETWORK_NO_SOURCE (numeric value 3)
  21707. * The element's resource selection algorithm is active, but it has
  21708. * not yet found a resource to use.
  21709. *
  21710. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
  21711. * @return {number} the current network activity state
  21712. * @method Player#networkState
  21713. */
  21714. 'networkState',
  21715. /**
  21716. * Returns a value that expresses the current state of the element
  21717. * with respect to rendering the current playback position, from the
  21718. * codes in the list below.
  21719. * - HAVE_NOTHING (numeric value 0)
  21720. * No information regarding the media resource is available.
  21721. * - HAVE_METADATA (numeric value 1)
  21722. * Enough of the resource has been obtained that the duration of the
  21723. * resource is available.
  21724. * - HAVE_CURRENT_DATA (numeric value 2)
  21725. * Data for the immediate current playback position is available.
  21726. * - HAVE_FUTURE_DATA (numeric value 3)
  21727. * Data for the immediate current playback position is available, as
  21728. * well as enough data for the user agent to advance the current
  21729. * playback position in the direction of playback.
  21730. * - HAVE_ENOUGH_DATA (numeric value 4)
  21731. * The user agent estimates that enough data is available for
  21732. * playback to proceed uninterrupted.
  21733. *
  21734. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
  21735. * @return {number} the current playback rendering state
  21736. * @method Player#readyState
  21737. */
  21738. 'readyState'].forEach(function (fn) {
  21739. Player.prototype[fn] = function () {
  21740. return this.techGet_(fn);
  21741. };
  21742. });
  21743. TECH_EVENTS_RETRIGGER.forEach(function (event) {
  21744. Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () {
  21745. return this.trigger(event);
  21746. };
  21747. });
  21748. /**
  21749. * Fired when the player has initial duration and dimension information
  21750. *
  21751. * @event Player#loadedmetadata
  21752. * @type {EventTarget~Event}
  21753. */
  21754. /**
  21755. * Fired when the player has downloaded data at the current playback position
  21756. *
  21757. * @event Player#loadeddata
  21758. * @type {EventTarget~Event}
  21759. */
  21760. /**
  21761. * Fired when the current playback position has changed *
  21762. * During playback this is fired every 15-250 milliseconds, depending on the
  21763. * playback technology in use.
  21764. *
  21765. * @event Player#timeupdate
  21766. * @type {EventTarget~Event}
  21767. */
  21768. /**
  21769. * Fired when the volume changes
  21770. *
  21771. * @event Player#volumechange
  21772. * @type {EventTarget~Event}
  21773. */
  21774. /**
  21775. * Reports whether or not a player has a plugin available.
  21776. *
  21777. * This does not report whether or not the plugin has ever been initialized
  21778. * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
  21779. *
  21780. * @method Player#hasPlugin
  21781. * @param {string} name
  21782. * The name of a plugin.
  21783. *
  21784. * @return {boolean}
  21785. * Whether or not this player has the requested plugin available.
  21786. */
  21787. /**
  21788. * Reports whether or not a player is using a plugin by name.
  21789. *
  21790. * For basic plugins, this only reports whether the plugin has _ever_ been
  21791. * initialized on this player.
  21792. *
  21793. * @method Player#usingPlugin
  21794. * @param {string} name
  21795. * The name of a plugin.
  21796. *
  21797. * @return {boolean}
  21798. * Whether or not this player is using the requested plugin.
  21799. */
  21800. Component.registerComponent('Player', Player);
  21801. /**
  21802. * @file plugin.js
  21803. */
  21804. /**
  21805. * The base plugin name.
  21806. *
  21807. * @private
  21808. * @constant
  21809. * @type {string}
  21810. */
  21811. var BASE_PLUGIN_NAME = 'plugin';
  21812. /**
  21813. * The key on which a player's active plugins cache is stored.
  21814. *
  21815. * @private
  21816. * @constant
  21817. * @type {string}
  21818. */
  21819. var PLUGIN_CACHE_KEY = 'activePlugins_';
  21820. /**
  21821. * Stores registered plugins in a private space.
  21822. *
  21823. * @private
  21824. * @type {Object}
  21825. */
  21826. var pluginStorage = {};
  21827. /**
  21828. * Reports whether or not a plugin has been registered.
  21829. *
  21830. * @private
  21831. * @param {string} name
  21832. * The name of a plugin.
  21833. *
  21834. * @returns {boolean}
  21835. * Whether or not the plugin has been registered.
  21836. */
  21837. var pluginExists = function pluginExists(name) {
  21838. return pluginStorage.hasOwnProperty(name);
  21839. };
  21840. /**
  21841. * Get a single registered plugin by name.
  21842. *
  21843. * @private
  21844. * @param {string} name
  21845. * The name of a plugin.
  21846. *
  21847. * @returns {Function|undefined}
  21848. * The plugin (or undefined).
  21849. */
  21850. var getPlugin = function getPlugin(name) {
  21851. return pluginExists(name) ? pluginStorage[name] : undefined;
  21852. };
  21853. /**
  21854. * Marks a plugin as "active" on a player.
  21855. *
  21856. * Also, ensures that the player has an object for tracking active plugins.
  21857. *
  21858. * @private
  21859. * @param {Player} player
  21860. * A Video.js player instance.
  21861. *
  21862. * @param {string} name
  21863. * The name of a plugin.
  21864. */
  21865. var markPluginAsActive = function markPluginAsActive(player, name) {
  21866. player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
  21867. player[PLUGIN_CACHE_KEY][name] = true;
  21868. };
  21869. /**
  21870. * Triggers a pair of plugin setup events.
  21871. *
  21872. * @private
  21873. * @param {Player} player
  21874. * A Video.js player instance.
  21875. *
  21876. * @param {Plugin~PluginEventHash} hash
  21877. * A plugin event hash.
  21878. *
  21879. * @param {Boolean} [before]
  21880. * If true, prefixes the event name with "before". In other words,
  21881. * use this to trigger "beforepluginsetup" instead of "pluginsetup".
  21882. */
  21883. var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
  21884. var eventName = (before ? 'before' : '') + 'pluginsetup';
  21885. player.trigger(eventName, hash);
  21886. player.trigger(eventName + ':' + hash.name, hash);
  21887. };
  21888. /**
  21889. * Takes a basic plugin function and returns a wrapper function which marks
  21890. * on the player that the plugin has been activated.
  21891. *
  21892. * @private
  21893. * @param {string} name
  21894. * The name of the plugin.
  21895. *
  21896. * @param {Function} plugin
  21897. * The basic plugin.
  21898. *
  21899. * @returns {Function}
  21900. * A wrapper function for the given plugin.
  21901. */
  21902. var createBasicPlugin = function createBasicPlugin(name, plugin) {
  21903. var basicPluginWrapper = function basicPluginWrapper() {
  21904. // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
  21905. // regardless, but we want the hash to be consistent with the hash provided
  21906. // for advanced plugins.
  21907. //
  21908. // The only potentially counter-intuitive thing here is the `instance` in
  21909. // the "pluginsetup" event is the value returned by the `plugin` function.
  21910. triggerSetupEvent(this, { name: name, plugin: plugin, instance: null }, true);
  21911. var instance = plugin.apply(this, arguments);
  21912. markPluginAsActive(this, name);
  21913. triggerSetupEvent(this, { name: name, plugin: plugin, instance: instance });
  21914. return instance;
  21915. };
  21916. Object.keys(plugin).forEach(function (prop) {
  21917. basicPluginWrapper[prop] = plugin[prop];
  21918. });
  21919. return basicPluginWrapper;
  21920. };
  21921. /**
  21922. * Takes a plugin sub-class and returns a factory function for generating
  21923. * instances of it.
  21924. *
  21925. * This factory function will replace itself with an instance of the requested
  21926. * sub-class of Plugin.
  21927. *
  21928. * @private
  21929. * @param {string} name
  21930. * The name of the plugin.
  21931. *
  21932. * @param {Plugin} PluginSubClass
  21933. * The advanced plugin.
  21934. *
  21935. * @returns {Function}
  21936. */
  21937. var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
  21938. // Add a `name` property to the plugin prototype so that each plugin can
  21939. // refer to itself by name.
  21940. PluginSubClass.prototype.name = name;
  21941. return function () {
  21942. triggerSetupEvent(this, { name: name, plugin: PluginSubClass, instance: null }, true);
  21943. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  21944. args[_key] = arguments[_key];
  21945. }
  21946. var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))();
  21947. // The plugin is replaced by a function that returns the current instance.
  21948. this[name] = function () {
  21949. return instance;
  21950. };
  21951. triggerSetupEvent(this, instance.getEventHash());
  21952. return instance;
  21953. };
  21954. };
  21955. /**
  21956. * Parent class for all advanced plugins.
  21957. *
  21958. * @mixes module:evented~EventedMixin
  21959. * @mixes module:stateful~StatefulMixin
  21960. * @fires Player#beforepluginsetup
  21961. * @fires Player#beforepluginsetup:$name
  21962. * @fires Player#pluginsetup
  21963. * @fires Player#pluginsetup:$name
  21964. * @listens Player#dispose
  21965. * @throws {Error}
  21966. * If attempting to instantiate the base {@link Plugin} class
  21967. * directly instead of via a sub-class.
  21968. */
  21969. var Plugin = function () {
  21970. /**
  21971. * Creates an instance of this class.
  21972. *
  21973. * Sub-classes should call `super` to ensure plugins are properly initialized.
  21974. *
  21975. * @param {Player} player
  21976. * A Video.js player instance.
  21977. */
  21978. function Plugin(player) {
  21979. classCallCheck(this, Plugin);
  21980. if (this.constructor === Plugin) {
  21981. throw new Error('Plugin must be sub-classed; not directly instantiated.');
  21982. }
  21983. this.player = player;
  21984. // Make this object evented, but remove the added `trigger` method so we
  21985. // use the prototype version instead.
  21986. evented(this);
  21987. delete this.trigger;
  21988. stateful(this, this.constructor.defaultState);
  21989. markPluginAsActive(player, this.name);
  21990. // Auto-bind the dispose method so we can use it as a listener and unbind
  21991. // it later easily.
  21992. this.dispose = bind(this, this.dispose);
  21993. // If the player is disposed, dispose the plugin.
  21994. player.on('dispose', this.dispose);
  21995. }
  21996. /**
  21997. * Get the version of the plugin that was set on <pluginName>.VERSION
  21998. */
  21999. Plugin.prototype.version = function version() {
  22000. return this.constructor.VERSION;
  22001. };
  22002. /**
  22003. * Each event triggered by plugins includes a hash of additional data with
  22004. * conventional properties.
  22005. *
  22006. * This returns that object or mutates an existing hash.
  22007. *
  22008. * @param {Object} [hash={}]
  22009. * An object to be used as event an event hash.
  22010. *
  22011. * @returns {Plugin~PluginEventHash}
  22012. * An event hash object with provided properties mixed-in.
  22013. */
  22014. Plugin.prototype.getEventHash = function getEventHash() {
  22015. var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  22016. hash.name = this.name;
  22017. hash.plugin = this.constructor;
  22018. hash.instance = this;
  22019. return hash;
  22020. };
  22021. /**
  22022. * Triggers an event on the plugin object and overrides
  22023. * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
  22024. *
  22025. * @param {string|Object} event
  22026. * An event type or an object with a type property.
  22027. *
  22028. * @param {Object} [hash={}]
  22029. * Additional data hash to merge with a
  22030. * {@link Plugin~PluginEventHash|PluginEventHash}.
  22031. *
  22032. * @returns {boolean}
  22033. * Whether or not default was prevented.
  22034. */
  22035. Plugin.prototype.trigger = function trigger$$1(event) {
  22036. var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  22037. return trigger(this.eventBusEl_, event, this.getEventHash(hash));
  22038. };
  22039. /**
  22040. * Handles "statechanged" events on the plugin. No-op by default, override by
  22041. * subclassing.
  22042. *
  22043. * @abstract
  22044. * @param {Event} e
  22045. * An event object provided by a "statechanged" event.
  22046. *
  22047. * @param {Object} e.changes
  22048. * An object describing changes that occurred with the "statechanged"
  22049. * event.
  22050. */
  22051. Plugin.prototype.handleStateChanged = function handleStateChanged(e) {};
  22052. /**
  22053. * Disposes a plugin.
  22054. *
  22055. * Subclasses can override this if they want, but for the sake of safety,
  22056. * it's probably best to subscribe the "dispose" event.
  22057. *
  22058. * @fires Plugin#dispose
  22059. */
  22060. Plugin.prototype.dispose = function dispose() {
  22061. var name = this.name,
  22062. player = this.player;
  22063. /**
  22064. * Signals that a advanced plugin is about to be disposed.
  22065. *
  22066. * @event Plugin#dispose
  22067. * @type {EventTarget~Event}
  22068. */
  22069. this.trigger('dispose');
  22070. this.off();
  22071. player.off('dispose', this.dispose);
  22072. // Eliminate any possible sources of leaking memory by clearing up
  22073. // references between the player and the plugin instance and nulling out
  22074. // the plugin's state and replacing methods with a function that throws.
  22075. player[PLUGIN_CACHE_KEY][name] = false;
  22076. this.player = this.state = null;
  22077. // Finally, replace the plugin name on the player with a new factory
  22078. // function, so that the plugin is ready to be set up again.
  22079. player[name] = createPluginFactory(name, pluginStorage[name]);
  22080. };
  22081. /**
  22082. * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
  22083. *
  22084. * @param {string|Function} plugin
  22085. * If a string, matches the name of a plugin. If a function, will be
  22086. * tested directly.
  22087. *
  22088. * @returns {boolean}
  22089. * Whether or not a plugin is a basic plugin.
  22090. */
  22091. Plugin.isBasic = function isBasic(plugin) {
  22092. var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
  22093. return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
  22094. };
  22095. /**
  22096. * Register a Video.js plugin.
  22097. *
  22098. * @param {string} name
  22099. * The name of the plugin to be registered. Must be a string and
  22100. * must not match an existing plugin or a method on the `Player`
  22101. * prototype.
  22102. *
  22103. * @param {Function} plugin
  22104. * A sub-class of `Plugin` or a function for basic plugins.
  22105. *
  22106. * @returns {Function}
  22107. * For advanced plugins, a factory function for that plugin. For
  22108. * basic plugins, a wrapper function that initializes the plugin.
  22109. */
  22110. Plugin.registerPlugin = function registerPlugin(name, plugin) {
  22111. if (typeof name !== 'string') {
  22112. throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.');
  22113. }
  22114. if (pluginExists(name)) {
  22115. log.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!');
  22116. } else if (Player.prototype.hasOwnProperty(name)) {
  22117. throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!');
  22118. }
  22119. if (typeof plugin !== 'function') {
  22120. throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.');
  22121. }
  22122. pluginStorage[name] = plugin;
  22123. // Add a player prototype method for all sub-classed plugins (but not for
  22124. // the base Plugin class).
  22125. if (name !== BASE_PLUGIN_NAME) {
  22126. if (Plugin.isBasic(plugin)) {
  22127. Player.prototype[name] = createBasicPlugin(name, plugin);
  22128. } else {
  22129. Player.prototype[name] = createPluginFactory(name, plugin);
  22130. }
  22131. }
  22132. return plugin;
  22133. };
  22134. /**
  22135. * De-register a Video.js plugin.
  22136. *
  22137. * @param {string} name
  22138. * The name of the plugin to be deregistered.
  22139. */
  22140. Plugin.deregisterPlugin = function deregisterPlugin(name) {
  22141. if (name === BASE_PLUGIN_NAME) {
  22142. throw new Error('Cannot de-register base plugin.');
  22143. }
  22144. if (pluginExists(name)) {
  22145. delete pluginStorage[name];
  22146. delete Player.prototype[name];
  22147. }
  22148. };
  22149. /**
  22150. * Gets an object containing multiple Video.js plugins.
  22151. *
  22152. * @param {Array} [names]
  22153. * If provided, should be an array of plugin names. Defaults to _all_
  22154. * plugin names.
  22155. *
  22156. * @returns {Object|undefined}
  22157. * An object containing plugin(s) associated with their name(s) or
  22158. * `undefined` if no matching plugins exist).
  22159. */
  22160. Plugin.getPlugins = function getPlugins() {
  22161. var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage);
  22162. var result = void 0;
  22163. names.forEach(function (name) {
  22164. var plugin = getPlugin(name);
  22165. if (plugin) {
  22166. result = result || {};
  22167. result[name] = plugin;
  22168. }
  22169. });
  22170. return result;
  22171. };
  22172. /**
  22173. * Gets a plugin's version, if available
  22174. *
  22175. * @param {string} name
  22176. * The name of a plugin.
  22177. *
  22178. * @returns {string}
  22179. * The plugin's version or an empty string.
  22180. */
  22181. Plugin.getPluginVersion = function getPluginVersion(name) {
  22182. var plugin = getPlugin(name);
  22183. return plugin && plugin.VERSION || '';
  22184. };
  22185. return Plugin;
  22186. }();
  22187. /**
  22188. * Gets a plugin by name if it exists.
  22189. *
  22190. * @static
  22191. * @method getPlugin
  22192. * @memberOf Plugin
  22193. * @param {string} name
  22194. * The name of a plugin.
  22195. *
  22196. * @returns {Function|undefined}
  22197. * The plugin (or `undefined`).
  22198. */
  22199. Plugin.getPlugin = getPlugin;
  22200. /**
  22201. * The name of the base plugin class as it is registered.
  22202. *
  22203. * @type {string}
  22204. */
  22205. Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
  22206. Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
  22207. /**
  22208. * Documented in player.js
  22209. *
  22210. * @ignore
  22211. */
  22212. Player.prototype.usingPlugin = function (name) {
  22213. return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
  22214. };
  22215. /**
  22216. * Documented in player.js
  22217. *
  22218. * @ignore
  22219. */
  22220. Player.prototype.hasPlugin = function (name) {
  22221. return !!pluginExists(name);
  22222. };
  22223. /**
  22224. * Signals that a plugin is about to be set up on a player.
  22225. *
  22226. * @event Player#beforepluginsetup
  22227. * @type {Plugin~PluginEventHash}
  22228. */
  22229. /**
  22230. * Signals that a plugin is about to be set up on a player - by name. The name
  22231. * is the name of the plugin.
  22232. *
  22233. * @event Player#beforepluginsetup:$name
  22234. * @type {Plugin~PluginEventHash}
  22235. */
  22236. /**
  22237. * Signals that a plugin has just been set up on a player.
  22238. *
  22239. * @event Player#pluginsetup
  22240. * @type {Plugin~PluginEventHash}
  22241. */
  22242. /**
  22243. * Signals that a plugin has just been set up on a player - by name. The name
  22244. * is the name of the plugin.
  22245. *
  22246. * @event Player#pluginsetup:$name
  22247. * @type {Plugin~PluginEventHash}
  22248. */
  22249. /**
  22250. * @typedef {Object} Plugin~PluginEventHash
  22251. *
  22252. * @property {string} instance
  22253. * For basic plugins, the return value of the plugin function. For
  22254. * advanced plugins, the plugin instance on which the event is fired.
  22255. *
  22256. * @property {string} name
  22257. * The name of the plugin.
  22258. *
  22259. * @property {string} plugin
  22260. * For basic plugins, the plugin function. For advanced plugins, the
  22261. * plugin class/constructor.
  22262. */
  22263. /**
  22264. * @file extend.js
  22265. * @module extend
  22266. */
  22267. /**
  22268. * A combination of node inherits and babel's inherits (after transpile).
  22269. * Both work the same but node adds `super_` to the subClass
  22270. * and Bable adds the superClass as __proto__. Both seem useful.
  22271. *
  22272. * @param {Object} subClass
  22273. * The class to inherit to
  22274. *
  22275. * @param {Object} superClass
  22276. * The class to inherit from
  22277. *
  22278. * @private
  22279. */
  22280. var _inherits = function _inherits(subClass, superClass) {
  22281. if (typeof superClass !== 'function' && superClass !== null) {
  22282. throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
  22283. }
  22284. subClass.prototype = Object.create(superClass && superClass.prototype, {
  22285. constructor: {
  22286. value: subClass,
  22287. enumerable: false,
  22288. writable: true,
  22289. configurable: true
  22290. }
  22291. });
  22292. if (superClass) {
  22293. // node
  22294. subClass.super_ = superClass;
  22295. }
  22296. };
  22297. /**
  22298. * Function for subclassing using the same inheritance that
  22299. * videojs uses internally
  22300. *
  22301. * @static
  22302. * @const
  22303. *
  22304. * @param {Object} superClass
  22305. * The class to inherit from
  22306. *
  22307. * @param {Object} [subClassMethods={}]
  22308. * The class to inherit to
  22309. *
  22310. * @return {Object}
  22311. * The new object with subClassMethods that inherited superClass.
  22312. */
  22313. var extendFn = function extendFn(superClass) {
  22314. var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  22315. var subClass = function subClass() {
  22316. superClass.apply(this, arguments);
  22317. };
  22318. var methods = {};
  22319. if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') {
  22320. if (subClassMethods.constructor !== Object.prototype.constructor) {
  22321. subClass = subClassMethods.constructor;
  22322. }
  22323. methods = subClassMethods;
  22324. } else if (typeof subClassMethods === 'function') {
  22325. subClass = subClassMethods;
  22326. }
  22327. _inherits(subClass, superClass);
  22328. // Extend subObj's prototype with functions and other properties from props
  22329. for (var name in methods) {
  22330. if (methods.hasOwnProperty(name)) {
  22331. subClass.prototype[name] = methods[name];
  22332. }
  22333. }
  22334. return subClass;
  22335. };
  22336. /**
  22337. * @file video.js
  22338. * @module videojs
  22339. */
  22340. // Include the built-in techs
  22341. // HTML5 Element Shim for IE8
  22342. if (typeof HTMLVideoElement === 'undefined' && isReal()) {
  22343. document_1.createElement('video');
  22344. document_1.createElement('audio');
  22345. document_1.createElement('track');
  22346. document_1.createElement('video-js');
  22347. }
  22348. /**
  22349. * Normalize an `id` value by trimming off a leading `#`
  22350. *
  22351. * @param {string} id
  22352. * A string, maybe with a leading `#`.
  22353. *
  22354. * @returns {string}
  22355. * The string, without any leading `#`.
  22356. */
  22357. var normalizeId = function normalizeId(id) {
  22358. return id.indexOf('#') === 0 ? id.slice(1) : id;
  22359. };
  22360. /**
  22361. * Doubles as the main function for users to create a player instance and also
  22362. * the main library object.
  22363. * The `videojs` function can be used to initialize or retrieve a player.
  22364. *
  22365. * @param {string|Element} id
  22366. * Video element or video element ID
  22367. *
  22368. * @param {Object} [options]
  22369. * Optional options object for config/settings
  22370. *
  22371. * @param {Component~ReadyCallback} [ready]
  22372. * Optional ready callback
  22373. *
  22374. * @return {Player}
  22375. * A player instance
  22376. */
  22377. function videojs(id, options, ready) {
  22378. var player = videojs.getPlayer(id);
  22379. if (player) {
  22380. if (options) {
  22381. log.warn('Player "' + id + '" is already initialised. Options will not be applied.');
  22382. }
  22383. if (ready) {
  22384. player.ready(ready);
  22385. }
  22386. return player;
  22387. }
  22388. var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
  22389. if (!isEl(el)) {
  22390. throw new TypeError('The element or ID supplied is not valid. (videojs)');
  22391. }
  22392. if (!document_1.body.contains(el)) {
  22393. log.warn('The element supplied is not included in the DOM');
  22394. }
  22395. options = options || {};
  22396. videojs.hooks('beforesetup').forEach(function (hookFunction) {
  22397. var opts = hookFunction(el, mergeOptions(options));
  22398. if (!isObject(opts) || Array.isArray(opts)) {
  22399. log.error('please return an object in beforesetup hooks');
  22400. return;
  22401. }
  22402. options = mergeOptions(options, opts);
  22403. });
  22404. // We get the current "Player" component here in case an integration has
  22405. // replaced it with a custom player.
  22406. var PlayerComponent = Component.getComponent('Player');
  22407. player = new PlayerComponent(el, options, ready);
  22408. videojs.hooks('setup').forEach(function (hookFunction) {
  22409. return hookFunction(player);
  22410. });
  22411. return player;
  22412. }
  22413. /**
  22414. * An Object that contains lifecycle hooks as keys which point to an array
  22415. * of functions that are run when a lifecycle is triggered
  22416. */
  22417. videojs.hooks_ = {};
  22418. /**
  22419. * Get a list of hooks for a specific lifecycle
  22420. * @function videojs.hooks
  22421. *
  22422. * @param {string} type
  22423. * the lifecyle to get hooks from
  22424. *
  22425. * @param {Function|Function[]} [fn]
  22426. * Optionally add a hook (or hooks) to the lifecycle that your are getting.
  22427. *
  22428. * @return {Array}
  22429. * an array of hooks, or an empty array if there are none.
  22430. */
  22431. videojs.hooks = function (type, fn) {
  22432. videojs.hooks_[type] = videojs.hooks_[type] || [];
  22433. if (fn) {
  22434. videojs.hooks_[type] = videojs.hooks_[type].concat(fn);
  22435. }
  22436. return videojs.hooks_[type];
  22437. };
  22438. /**
  22439. * Add a function hook to a specific videojs lifecycle.
  22440. *
  22441. * @param {string} type
  22442. * the lifecycle to hook the function to.
  22443. *
  22444. * @param {Function|Function[]}
  22445. * The function or array of functions to attach.
  22446. */
  22447. videojs.hook = function (type, fn) {
  22448. videojs.hooks(type, fn);
  22449. };
  22450. /**
  22451. * Add a function hook that will only run once to a specific videojs lifecycle.
  22452. *
  22453. * @param {string} type
  22454. * the lifecycle to hook the function to.
  22455. *
  22456. * @param {Function|Function[]}
  22457. * The function or array of functions to attach.
  22458. */
  22459. videojs.hookOnce = function (type, fn) {
  22460. videojs.hooks(type, [].concat(fn).map(function (original) {
  22461. var wrapper = function wrapper() {
  22462. videojs.removeHook(type, wrapper);
  22463. return original.apply(undefined, arguments);
  22464. };
  22465. return wrapper;
  22466. }));
  22467. };
  22468. /**
  22469. * Remove a hook from a specific videojs lifecycle.
  22470. *
  22471. * @param {string} type
  22472. * the lifecycle that the function hooked to
  22473. *
  22474. * @param {Function} fn
  22475. * The hooked function to remove
  22476. *
  22477. * @return {boolean}
  22478. * The function that was removed or undef
  22479. */
  22480. videojs.removeHook = function (type, fn) {
  22481. var index = videojs.hooks(type).indexOf(fn);
  22482. if (index <= -1) {
  22483. return false;
  22484. }
  22485. videojs.hooks_[type] = videojs.hooks_[type].slice();
  22486. videojs.hooks_[type].splice(index, 1);
  22487. return true;
  22488. };
  22489. // Add default styles
  22490. if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
  22491. var style = $('.vjs-styles-defaults');
  22492. if (!style) {
  22493. style = createStyleElement('vjs-styles-defaults');
  22494. var head = $('head');
  22495. if (head) {
  22496. head.insertBefore(style, head.firstChild);
  22497. }
  22498. setTextContent(style, '\n .video-js {\n width: 300px;\n height: 150px;\n }\n\n .vjs-fluid {\n padding-top: 56.25%\n }\n ');
  22499. }
  22500. }
  22501. // Run Auto-load players
  22502. // You have to wait at least once in case this script is loaded after your
  22503. // video in the DOM (weird behavior only with minified version)
  22504. autoSetupTimeout(1, videojs);
  22505. /**
  22506. * Current software version. Follows semver.
  22507. *
  22508. * @type {string}
  22509. */
  22510. videojs.VERSION = version;
  22511. /**
  22512. * The global options object. These are the settings that take effect
  22513. * if no overrides are specified when the player is created.
  22514. *
  22515. * @type {Object}
  22516. */
  22517. videojs.options = Player.prototype.options_;
  22518. /**
  22519. * Get an object with the currently created players, keyed by player ID
  22520. *
  22521. * @return {Object}
  22522. * The created players
  22523. */
  22524. videojs.getPlayers = function () {
  22525. return Player.players;
  22526. };
  22527. /**
  22528. * Get a single player based on an ID or DOM element.
  22529. *
  22530. * This is useful if you want to check if an element or ID has an associated
  22531. * Video.js player, but not create one if it doesn't.
  22532. *
  22533. * @param {string|Element} id
  22534. * An HTML element - `<video>`, `<audio>`, or `<video-js>` -
  22535. * or a string matching the `id` of such an element.
  22536. *
  22537. * @returns {Player|undefined}
  22538. * A player instance or `undefined` if there is no player instance
  22539. * matching the argument.
  22540. */
  22541. videojs.getPlayer = function (id) {
  22542. var players = Player.players;
  22543. var tag = void 0;
  22544. if (typeof id === 'string') {
  22545. var nId = normalizeId(id);
  22546. var player = players[nId];
  22547. if (player) {
  22548. return player;
  22549. }
  22550. tag = $('#' + nId);
  22551. } else {
  22552. tag = id;
  22553. }
  22554. if (isEl(tag)) {
  22555. var _tag = tag,
  22556. _player = _tag.player,
  22557. playerId = _tag.playerId;
  22558. // Element may have a `player` property referring to an already created
  22559. // player instance. If so, return that.
  22560. if (_player || players[playerId]) {
  22561. return _player || players[playerId];
  22562. }
  22563. }
  22564. };
  22565. /**
  22566. * Returns an array of all current players.
  22567. *
  22568. * @return {Array}
  22569. * An array of all players. The array will be in the order that
  22570. * `Object.keys` provides, which could potentially vary between
  22571. * JavaScript engines.
  22572. *
  22573. */
  22574. videojs.getAllPlayers = function () {
  22575. return (
  22576. // Disposed players leave a key with a `null` value, so we need to make sure
  22577. // we filter those out.
  22578. Object.keys(Player.players).map(function (k) {
  22579. return Player.players[k];
  22580. }).filter(Boolean)
  22581. );
  22582. };
  22583. /**
  22584. * Expose players object.
  22585. *
  22586. * @memberOf videojs
  22587. * @property {Object} players
  22588. */
  22589. videojs.players = Player.players;
  22590. /**
  22591. * Get a component class object by name
  22592. *
  22593. * @borrows Component.getComponent as videojs.getComponent
  22594. */
  22595. videojs.getComponent = Component.getComponent;
  22596. /**
  22597. * Register a component so it can referred to by name. Used when adding to other
  22598. * components, either through addChild `component.addChild('myComponent')` or through
  22599. * default children options `{ children: ['myComponent'] }`.
  22600. *
  22601. * > NOTE: You could also just initialize the component before adding.
  22602. * `component.addChild(new MyComponent());`
  22603. *
  22604. * @param {string} name
  22605. * The class name of the component
  22606. *
  22607. * @param {Component} comp
  22608. * The component class
  22609. *
  22610. * @return {Component}
  22611. * The newly registered component
  22612. */
  22613. videojs.registerComponent = function (name$$1, comp) {
  22614. if (Tech.isTech(comp)) {
  22615. log.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
  22616. }
  22617. Component.registerComponent.call(Component, name$$1, comp);
  22618. };
  22619. /**
  22620. * Get a Tech class object by name
  22621. *
  22622. * @borrows Tech.getTech as videojs.getTech
  22623. */
  22624. videojs.getTech = Tech.getTech;
  22625. /**
  22626. * Register a Tech so it can referred to by name.
  22627. * This is used in the tech order for the player.
  22628. *
  22629. * @borrows Tech.registerTech as videojs.registerTech
  22630. */
  22631. videojs.registerTech = Tech.registerTech;
  22632. /**
  22633. * Register a middleware to a source type.
  22634. *
  22635. * @param {String} type A string representing a MIME type.
  22636. * @param {function(player):object} middleware A middleware factory that takes a player.
  22637. */
  22638. videojs.use = use;
  22639. /**
  22640. * An object that can be returned by a middleware to signify
  22641. * that the middleware is being terminated.
  22642. *
  22643. * @type {object}
  22644. * @memberOf {videojs}
  22645. * @property {object} middleware.TERMINATOR
  22646. */
  22647. // Object.defineProperty is not available in IE8
  22648. if (!IS_IE8 && Object.defineProperty) {
  22649. Object.defineProperty(videojs, 'middleware', {
  22650. value: {},
  22651. writeable: false,
  22652. enumerable: true
  22653. });
  22654. Object.defineProperty(videojs.middleware, 'TERMINATOR', {
  22655. value: TERMINATOR,
  22656. writeable: false,
  22657. enumerable: true
  22658. });
  22659. } else {
  22660. videojs.middleware = { TERMINATOR: TERMINATOR };
  22661. }
  22662. /**
  22663. * A suite of browser and device tests from {@link browser}.
  22664. *
  22665. * @type {Object}
  22666. * @private
  22667. */
  22668. videojs.browser = browser;
  22669. /**
  22670. * Whether or not the browser supports touch events. Included for backward
  22671. * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
  22672. * instead going forward.
  22673. *
  22674. * @deprecated since version 5.0
  22675. * @type {boolean}
  22676. */
  22677. videojs.TOUCH_ENABLED = TOUCH_ENABLED;
  22678. /**
  22679. * Subclass an existing class
  22680. * Mimics ES6 subclassing with the `extend` keyword
  22681. *
  22682. * @borrows extend:extendFn as videojs.extend
  22683. */
  22684. videojs.extend = extendFn;
  22685. /**
  22686. * Merge two options objects recursively
  22687. * Performs a deep merge like lodash.merge but **only merges plain objects**
  22688. * (not arrays, elements, anything else)
  22689. * Other values will be copied directly from the second object.
  22690. *
  22691. * @borrows merge-options:mergeOptions as videojs.mergeOptions
  22692. */
  22693. videojs.mergeOptions = mergeOptions;
  22694. /**
  22695. * Change the context (this) of a function
  22696. *
  22697. * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
  22698. * `function() {}.bind(newContext);` instead of this.
  22699. *
  22700. * @borrows fn:bind as videojs.bind
  22701. */
  22702. videojs.bind = bind;
  22703. /**
  22704. * Register a Video.js plugin.
  22705. *
  22706. * @borrows plugin:registerPlugin as videojs.registerPlugin
  22707. * @method registerPlugin
  22708. *
  22709. * @param {string} name
  22710. * The name of the plugin to be registered. Must be a string and
  22711. * must not match an existing plugin or a method on the `Player`
  22712. * prototype.
  22713. *
  22714. * @param {Function} plugin
  22715. * A sub-class of `Plugin` or a function for basic plugins.
  22716. *
  22717. * @return {Function}
  22718. * For advanced plugins, a factory function for that plugin. For
  22719. * basic plugins, a wrapper function that initializes the plugin.
  22720. */
  22721. videojs.registerPlugin = Plugin.registerPlugin;
  22722. /**
  22723. * Deregister a Video.js plugin.
  22724. *
  22725. * @borrows plugin:deregisterPlugin as videojs.deregisterPlugin
  22726. * @method deregisterPlugin
  22727. *
  22728. * @param {string} name
  22729. * The name of the plugin to be deregistered. Must be a string and
  22730. * must match an existing plugin or a method on the `Player`
  22731. * prototype.
  22732. *
  22733. */
  22734. videojs.deregisterPlugin = Plugin.deregisterPlugin;
  22735. /**
  22736. * Deprecated method to register a plugin with Video.js
  22737. *
  22738. * @deprecated
  22739. * videojs.plugin() is deprecated; use videojs.registerPlugin() instead
  22740. *
  22741. * @param {string} name
  22742. * The plugin name
  22743. *
  22744. * @param {Plugin|Function} plugin
  22745. * The plugin sub-class or function
  22746. */
  22747. videojs.plugin = function (name$$1, plugin) {
  22748. log.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
  22749. return Plugin.registerPlugin(name$$1, plugin);
  22750. };
  22751. /**
  22752. * Gets an object containing multiple Video.js plugins.
  22753. *
  22754. * @param {Array} [names]
  22755. * If provided, should be an array of plugin names. Defaults to _all_
  22756. * plugin names.
  22757. *
  22758. * @return {Object|undefined}
  22759. * An object containing plugin(s) associated with their name(s) or
  22760. * `undefined` if no matching plugins exist).
  22761. */
  22762. videojs.getPlugins = Plugin.getPlugins;
  22763. /**
  22764. * Gets a plugin by name if it exists.
  22765. *
  22766. * @param {string} name
  22767. * The name of a plugin.
  22768. *
  22769. * @return {Function|undefined}
  22770. * The plugin (or `undefined`).
  22771. */
  22772. videojs.getPlugin = Plugin.getPlugin;
  22773. /**
  22774. * Gets a plugin's version, if available
  22775. *
  22776. * @param {string} name
  22777. * The name of a plugin.
  22778. *
  22779. * @return {string}
  22780. * The plugin's version or an empty string.
  22781. */
  22782. videojs.getPluginVersion = Plugin.getPluginVersion;
  22783. /**
  22784. * Adding languages so that they're available to all players.
  22785. * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
  22786. *
  22787. * @param {string} code
  22788. * The language code or dictionary property
  22789. *
  22790. * @param {Object} data
  22791. * The data values to be translated
  22792. *
  22793. * @return {Object}
  22794. * The resulting language dictionary object
  22795. */
  22796. videojs.addLanguage = function (code, data) {
  22797. var _mergeOptions;
  22798. code = ('' + code).toLowerCase();
  22799. videojs.options.languages = mergeOptions(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
  22800. return videojs.options.languages[code];
  22801. };
  22802. /**
  22803. * Log messages
  22804. *
  22805. * @borrows log:log as videojs.log
  22806. */
  22807. videojs.log = log;
  22808. videojs.createLogger = createLogger;
  22809. /**
  22810. * Creates an emulated TimeRange object.
  22811. *
  22812. * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
  22813. */
  22814. /**
  22815. * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
  22816. */
  22817. videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges;
  22818. /**
  22819. * Format seconds as a time string, H:MM:SS or M:SS
  22820. * Supplying a guide (in seconds) will force a number of leading zeros
  22821. * to cover the length of the guide
  22822. *
  22823. * @borrows format-time:formatTime as videojs.formatTime
  22824. */
  22825. videojs.formatTime = formatTime;
  22826. /**
  22827. * Replaces format-time with a custom implementation, to be used in place of the default.
  22828. *
  22829. * @borrows format-time:setFormatTime as videojs.setFormatTime
  22830. *
  22831. * @method setFormatTime
  22832. *
  22833. * @param {Function} customFn
  22834. * A custom format-time function which will be called with the current time and guide (in seconds) as arguments.
  22835. * Passed fn should return a string.
  22836. */
  22837. videojs.setFormatTime = setFormatTime;
  22838. /**
  22839. * Resets format-time to the default implementation.
  22840. *
  22841. * @borrows format-time:resetFormatTime as videojs.resetFormatTime
  22842. *
  22843. * @method resetFormatTime
  22844. */
  22845. videojs.resetFormatTime = resetFormatTime;
  22846. /**
  22847. * Resolve and parse the elements of a URL
  22848. *
  22849. * @borrows url:parseUrl as videojs.parseUrl
  22850. *
  22851. */
  22852. videojs.parseUrl = parseUrl;
  22853. /**
  22854. * Returns whether the url passed is a cross domain request or not.
  22855. *
  22856. * @borrows url:isCrossOrigin as videojs.isCrossOrigin
  22857. */
  22858. videojs.isCrossOrigin = isCrossOrigin;
  22859. /**
  22860. * Event target class.
  22861. *
  22862. * @borrows EventTarget as videojs.EventTarget
  22863. */
  22864. videojs.EventTarget = EventTarget;
  22865. /**
  22866. * Add an event listener to element
  22867. * It stores the handler function in a separate cache object
  22868. * and adds a generic handler to the element's event,
  22869. * along with a unique id (guid) to the element.
  22870. *
  22871. * @borrows events:on as videojs.on
  22872. */
  22873. videojs.on = on;
  22874. /**
  22875. * Trigger a listener only once for an event
  22876. *
  22877. * @borrows events:one as videojs.one
  22878. */
  22879. videojs.one = one;
  22880. /**
  22881. * Removes event listeners from an element
  22882. *
  22883. * @borrows events:off as videojs.off
  22884. */
  22885. videojs.off = off;
  22886. /**
  22887. * Trigger an event for an element
  22888. *
  22889. * @borrows events:trigger as videojs.trigger
  22890. */
  22891. videojs.trigger = trigger;
  22892. /**
  22893. * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
  22894. *
  22895. * @param {Object} options
  22896. * settings for the request.
  22897. *
  22898. * @return {XMLHttpRequest|XDomainRequest}
  22899. * The request object.
  22900. *
  22901. * @see https://github.com/Raynos/xhr
  22902. */
  22903. videojs.xhr = xhr;
  22904. /**
  22905. * TextTrack class
  22906. *
  22907. * @borrows TextTrack as videojs.TextTrack
  22908. */
  22909. videojs.TextTrack = TextTrack;
  22910. /**
  22911. * export the AudioTrack class so that source handlers can create
  22912. * AudioTracks and then add them to the players AudioTrackList
  22913. *
  22914. * @borrows AudioTrack as videojs.AudioTrack
  22915. */
  22916. videojs.AudioTrack = AudioTrack;
  22917. /**
  22918. * export the VideoTrack class so that source handlers can create
  22919. * VideoTracks and then add them to the players VideoTrackList
  22920. *
  22921. * @borrows VideoTrack as videojs.VideoTrack
  22922. */
  22923. videojs.VideoTrack = VideoTrack;
  22924. /**
  22925. * Determines, via duck typing, whether or not a value is a DOM element.
  22926. *
  22927. * @borrows dom:isEl as videojs.isEl
  22928. * @deprecated Use videojs.dom.isEl() instead
  22929. */
  22930. /**
  22931. * Determines, via duck typing, whether or not a value is a text node.
  22932. *
  22933. * @borrows dom:isTextNode as videojs.isTextNode
  22934. * @deprecated Use videojs.dom.isTextNode() instead
  22935. */
  22936. /**
  22937. * Creates an element and applies properties.
  22938. *
  22939. * @borrows dom:createEl as videojs.createEl
  22940. * @deprecated Use videojs.dom.createEl() instead
  22941. */
  22942. /**
  22943. * Check if an element has a CSS class
  22944. *
  22945. * @borrows dom:hasElClass as videojs.hasClass
  22946. * @deprecated Use videojs.dom.hasClass() instead
  22947. */
  22948. /**
  22949. * Add a CSS class name to an element
  22950. *
  22951. * @borrows dom:addElClass as videojs.addClass
  22952. * @deprecated Use videojs.dom.addClass() instead
  22953. */
  22954. /**
  22955. * Remove a CSS class name from an element
  22956. *
  22957. * @borrows dom:removeElClass as videojs.removeClass
  22958. * @deprecated Use videojs.dom.removeClass() instead
  22959. */
  22960. /**
  22961. * Adds or removes a CSS class name on an element depending on an optional
  22962. * condition or the presence/absence of the class name.
  22963. *
  22964. * @borrows dom:toggleElClass as videojs.toggleClass
  22965. * @deprecated Use videojs.dom.toggleClass() instead
  22966. */
  22967. /**
  22968. * Apply attributes to an HTML element.
  22969. *
  22970. * @borrows dom:setElAttributes as videojs.setAttribute
  22971. * @deprecated Use videojs.dom.setAttributes() instead
  22972. */
  22973. /**
  22974. * Get an element's attribute values, as defined on the HTML tag
  22975. * Attributes are not the same as properties. They're defined on the tag
  22976. * or with setAttribute (which shouldn't be used with HTML)
  22977. * This will return true or false for boolean attributes.
  22978. *
  22979. * @borrows dom:getElAttributes as videojs.getAttributes
  22980. * @deprecated Use videojs.dom.getAttributes() instead
  22981. */
  22982. /**
  22983. * Empties the contents of an element.
  22984. *
  22985. * @borrows dom:emptyEl as videojs.emptyEl
  22986. * @deprecated Use videojs.dom.emptyEl() instead
  22987. */
  22988. /**
  22989. * Normalizes and appends content to an element.
  22990. *
  22991. * The content for an element can be passed in multiple types and
  22992. * combinations, whose behavior is as follows:
  22993. *
  22994. * - String
  22995. * Normalized into a text node.
  22996. *
  22997. * - Element, TextNode
  22998. * Passed through.
  22999. *
  23000. * - Array
  23001. * A one-dimensional array of strings, elements, nodes, or functions (which
  23002. * return single strings, elements, or nodes).
  23003. *
  23004. * - Function
  23005. * If the sole argument, is expected to produce a string, element,
  23006. * node, or array.
  23007. *
  23008. * @borrows dom:appendContents as videojs.appendContet
  23009. * @deprecated Use videojs.dom.appendContent() instead
  23010. */
  23011. /**
  23012. * Normalizes and inserts content into an element; this is identical to
  23013. * `appendContent()`, except it empties the element first.
  23014. *
  23015. * The content for an element can be passed in multiple types and
  23016. * combinations, whose behavior is as follows:
  23017. *
  23018. * - String
  23019. * Normalized into a text node.
  23020. *
  23021. * - Element, TextNode
  23022. * Passed through.
  23023. *
  23024. * - Array
  23025. * A one-dimensional array of strings, elements, nodes, or functions (which
  23026. * return single strings, elements, or nodes).
  23027. *
  23028. * - Function
  23029. * If the sole argument, is expected to produce a string, element,
  23030. * node, or array.
  23031. *
  23032. * @borrows dom:insertContent as videojs.insertContent
  23033. * @deprecated Use videojs.dom.insertContent() instead
  23034. */
  23035. ['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
  23036. videojs[k] = function () {
  23037. log.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead');
  23038. return Dom[k].apply(null, arguments);
  23039. };
  23040. });
  23041. /**
  23042. * A safe getComputedStyle with an IE8 fallback.
  23043. *
  23044. * This is because in Firefox, if the player is loaded in an iframe with `display:none`,
  23045. * then `getComputedStyle` returns `null`, so, we do a null-check to make sure
  23046. * that the player doesn't break in these cases.
  23047. * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
  23048. *
  23049. * @borrows computed-style:computedStyle as videojs.computedStyle
  23050. */
  23051. videojs.computedStyle = computedStyle;
  23052. /**
  23053. * Export the Dom utilities for use in external plugins
  23054. * and Tech's
  23055. */
  23056. videojs.dom = Dom;
  23057. /**
  23058. * Export the Url utilities for use in external plugins
  23059. * and Tech's
  23060. */
  23061. videojs.url = Url;
  23062. return videojs;
  23063. })));